build(desktop): allow controlled unsigned v0.1.0 release
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled

This commit is contained in:
Cheng Zhou
2026-07-17 10:52:20 +08:00
parent fa26c84bd2
commit c23a5d6a22
16 changed files with 647 additions and 83 deletions
+16
View File
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"defaultPlatformSigningMode": "signed",
"updaterSigningRequired": true,
"unsignedPlatformExceptions": [
{
"version": "0.1.0",
"status": "approved",
"macos": "ad-hoc",
"windows": "unsigned",
"distribution": "controlled",
"approvedOn": "2026-07-17",
"reason": "The v0.1.0 release owner cannot currently provide Apple Developer signing/notarization credentials or an organization Windows code-signing certificate."
}
]
}
@@ -0,0 +1,11 @@
CTMS DESKTOP PLATFORM SIGNING WARNING
This release is distributed under the version-scoped v0.1.0 platform-signing exception.
- The macOS application uses an ad-hoc signature and is not Apple-notarized.
- The Windows application and installer have no Authenticode signature or RFC 3161 timestamp.
- macOS Gatekeeper and Windows SmartScreen warnings are expected.
- Tauri updater artifacts remain cryptographically signed and must pass updater feed verification.
- Install only after verifying the release tag, source commit, SHA256SUMS.txt, and DESKTOP-RELEASE-PROVENANCE.json through a trusted channel.
This exception does not apply to later versions unless they are separately approved in desktop-release-policy.json.
+3
View File
@@ -15,7 +15,10 @@
"desktop:build": "tauri build",
"desktop:build:app": "tauri build --config '{\"bundle\":{\"createUpdaterArtifacts\":false}}' --bundles app",
"desktop:build:macos-release": "tauri build --target universal-apple-darwin --bundles app,dmg",
"desktop:build:macos-unsigned-release": "tauri build --target universal-apple-darwin --bundles app,dmg --config '{\"bundle\":{\"macOS\":{\"signingIdentity\":\"-\"}}}'",
"desktop:build:windows-release": "tauri build --bundles nsis",
"desktop:release-policy:check": "node scripts/resolve-desktop-release-policy.mjs",
"desktop:release-provenance:create": "node scripts/create-desktop-release-provenance.mjs",
"desktop:update-feed:create": "node scripts/create-desktop-update-feed.mjs",
"desktop:bundle:dmg": "tauri build --bundles dmg",
"desktop:update-feed:check": "node scripts/verify-desktop-update-feed.mjs",
@@ -0,0 +1,64 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { loadDesktopReleasePolicy, resolveDesktopReleasePolicy } from "./desktop-release-policy.mjs";
const frontendDir = fileURLToPath(new URL("../", import.meta.url));
const packageInfo = JSON.parse(await readFile(resolve(frontendDir, "package.json"), "utf8"));
const args = process.argv.slice(2);
const value = (name) => {
const index = args.indexOf(name);
return index >= 0 ? args[index + 1] : undefined;
};
const policy = await loadDesktopReleasePolicy();
const resolution = resolveDesktopReleasePolicy(packageInfo.version, policy);
const tag = value("--tag") || process.env.GITHUB_REF_NAME;
const commit = value("--commit") || process.env.GITHUB_SHA;
const macosSigning = value("--macos-signing");
const windowsSigning = value("--windows-signing");
const outputPath = resolve(
frontendDir,
value("--output") || "src-tauri/target/desktop-release-feed/DESKTOP-RELEASE-PROVENANCE.json",
);
const failures = [];
const assert = (condition, message) => {
if (!condition) failures.push(message);
};
assert(tag === `v${packageInfo.version}`, `Release provenance tag must be v${packageInfo.version}.`);
assert(/^[0-9a-f]{40}$/i.test(commit || ""), "Release provenance commit must be a full 40-character SHA.");
assert(macosSigning === resolution.macosSigning, `macOS signing mode must be ${resolution.macosSigning}.`);
assert(windowsSigning === resolution.windowsSigning, `Windows signing mode must be ${resolution.windowsSigning}.`);
if (failures.length > 0) {
throw new Error(`Desktop release provenance creation failed:\n${failures.map((item) => ` - ${item}`).join("\n")}`);
}
const provenance = {
schemaVersion: 1,
product: "CTMS Desktop",
version: packageInfo.version,
tag,
commit,
platformSigningMode: resolution.platformSigningMode,
platformSigning: {
macos: macosSigning,
windows: windowsSigning,
},
updaterSigning: "required-and-verified",
artifactLabel: resolution.artifactLabel,
distribution: resolution.distribution,
warnings: resolution.warningRequired
? [
"macOS is ad-hoc signed and not Apple-notarized; Gatekeeper warnings are expected.",
"Windows is not Authenticode-signed or RFC 3161 timestamped; SmartScreen warnings are expected.",
"Verify the release tag, commit, updater signatures, and SHA256SUMS.txt through a trusted channel before installation.",
]
: [],
};
await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, `${JSON.stringify(provenance, null, 2)}\n`);
console.log(`Desktop release provenance created at ${outputPath}`);
@@ -0,0 +1,86 @@
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
const frontendDir = fileURLToPath(new URL("../", import.meta.url));
export const desktopReleasePolicyPath = resolve(frontendDir, "desktop-release-policy.json");
const semverPattern = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
const validatePolicy = (policy) => {
const failures = [];
const assert = (condition, message) => {
if (!condition) failures.push(message);
};
assert(policy?.schemaVersion === 1, "desktop release policy schemaVersion must be 1.");
assert(
policy?.defaultPlatformSigningMode === "signed",
"desktop release policy must default platform signing to signed.",
);
assert(policy?.updaterSigningRequired === true, "desktop release policy must always require updater signing.");
assert(
Array.isArray(policy?.unsignedPlatformExceptions),
"desktop release policy unsignedPlatformExceptions must be an array.",
);
const versions = new Set();
for (const exception of policy?.unsignedPlatformExceptions || []) {
const prefix = `unsigned platform exception ${exception?.version || "<missing>"}`;
assert(semverPattern.test(exception?.version || ""), `${prefix} must use an exact X.Y.Z version.`);
assert(!versions.has(exception?.version), `${prefix} is duplicated.`);
versions.add(exception?.version);
assert(exception?.status === "approved", `${prefix} must have status approved.`);
assert(exception?.macos === "ad-hoc", `${prefix} must constrain macOS to ad-hoc signing.`);
assert(exception?.windows === "unsigned", `${prefix} must constrain Windows to unsigned.`);
assert(exception?.distribution === "controlled", `${prefix} must constrain distribution to controlled.`);
assert(/^\d{4}-\d{2}-\d{2}$/.test(exception?.approvedOn || ""), `${prefix} must record an approval date.`);
assert(
typeof exception?.reason === "string" && exception.reason.trim().length >= 30,
`${prefix} must record a substantive reason.`,
);
}
if (failures.length > 0) {
throw new Error(failures.join("\n"));
}
};
export const loadDesktopReleasePolicy = async () => {
const policy = JSON.parse(await readFile(desktopReleasePolicyPath, "utf8"));
validatePolicy(policy);
return policy;
};
export const resolveDesktopReleasePolicy = (version, policy) => {
if (!semverPattern.test(version || "")) {
throw new Error(`Desktop release version must use exact X.Y.Z semver; found ${version || "<missing>"}.`);
}
const exception = policy.unsignedPlatformExceptions.find(
(candidate) => candidate.version === version && candidate.status === "approved",
);
if (!exception) {
return {
version,
platformSigningMode: "signed",
macosSigning: "apple-developer",
windowsSigning: "authenticode",
artifactLabel: "SIGNED-PLATFORM",
distribution: "formal",
warningRequired: false,
};
}
return {
version,
platformSigningMode: "unsigned-exception",
macosSigning: exception.macos,
windowsSigning: exception.windows,
artifactLabel: "UNSIGNED-PLATFORM",
distribution: exception.distribution,
warningRequired: true,
exception,
};
};
@@ -0,0 +1,47 @@
import { appendFile, readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { loadDesktopReleasePolicy, resolveDesktopReleasePolicy } from "./desktop-release-policy.mjs";
const frontendDir = fileURLToPath(new URL("../", import.meta.url));
const packageInfo = JSON.parse(await readFile(resolve(frontendDir, "package.json"), "utf8"));
const args = process.argv.slice(2);
const value = (name) => {
const index = args.indexOf(name);
return index >= 0 ? args[index + 1] : undefined;
};
const policy = await loadDesktopReleasePolicy();
const resolution = resolveDesktopReleasePolicy(packageInfo.version, policy);
const expectedTag = `v${packageInfo.version}`;
if (args.includes("--require-tag")) {
if (process.env.GITHUB_REF_TYPE !== "tag" || process.env.GITHUB_REF_NAME !== expectedTag) {
throw new Error(
`Desktop release policy must be resolved from tag ${expectedTag}; found ${process.env.GITHUB_REF_TYPE || "<missing>"} ${process.env.GITHUB_REF_NAME || "<missing>"}.`,
);
}
}
const outputs = {
version: resolution.version,
platform_signing_mode: resolution.platformSigningMode,
macos_signing: resolution.macosSigning,
windows_signing: resolution.windowsSigning,
artifact_label: resolution.artifactLabel,
distribution: resolution.distribution,
warning_required: String(resolution.warningRequired),
updater_signing_required: String(policy.updaterSigningRequired),
};
const githubOutput = value("--github-output");
if (githubOutput) {
await appendFile(githubOutput, `${Object.entries(outputs).map(([key, output]) => `${key}=${output}`).join("\n")}\n`);
}
console.log("Desktop release policy check passed.");
console.log(` version: ${resolution.version}`);
console.log(` platform signing mode: ${resolution.platformSigningMode}`);
console.log(` macOS: ${resolution.macosSigning}`);
console.log(` Windows: ${resolution.windowsSigning}`);
console.log(" updater signing: required");
@@ -2,10 +2,13 @@ import { execFileSync } from "node:child_process";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { loadDesktopReleasePolicy, resolveDesktopReleasePolicy } from "./desktop-release-policy.mjs";
const frontendDir = fileURLToPath(new URL("../", import.meta.url));
const rootDir = resolve(frontendDir, "..");
const packageInfo = JSON.parse(await readFile(resolve(frontendDir, "package.json"), "utf8"));
const desktopReleasePolicy = await loadDesktopReleasePolicy();
const desktopReleaseResolution = resolveDesktopReleasePolicy(packageInfo.version, desktopReleasePolicy);
const failures = [];
const env = process.env;
@@ -13,6 +16,10 @@ const fullShaPattern = /^[0-9a-f]{40}$/i;
const expectedTag = `v${packageInfo.version}`;
const requiresMacosSigning = env.REQUIRE_DESKTOP_SIGNING === "true";
const requiresWindowsSigning = env.REQUIRE_WINDOWS_SIGNING === "true";
const requiresUpdaterSigning = env.REQUIRE_UPDATER_SIGNING === "true";
const allowsUnsignedPlatformRelease = env.ALLOW_UNSIGNED_PLATFORM_RELEASE === "true";
const desktopPlatformSigningMode = env.DESKTOP_PLATFORM_SIGNING_MODE;
const desktopReleasePlatform = env.DESKTOP_RELEASE_PLATFORM;
const requiredUpdaterEnv = ["TAURI_SIGNING_PRIVATE_KEY", "TAURI_SIGNING_PRIVATE_KEY_PASSWORD"];
const requiredMacosEnv = ["APPLE_ID", "APPLE_PASSWORD", "APPLE_TEAM_ID"];
const requiredWindowsEnv = ["WINDOWS_CERTIFICATE", "WINDOWS_CERTIFICATE_PASSWORD"];
@@ -38,7 +45,7 @@ const gitMaybe = (args) => {
};
const requireEnv = (name) => {
assert(Boolean(env[name]), `${name} must be configured for signed desktop release readiness.`);
assert(Boolean(env[name]), `${name} must be configured for desktop release readiness.`);
};
const validateBaseUrl = () => {
@@ -95,19 +102,54 @@ if (headSha && env.VITE_BUILD_COMMIT) {
assert(env.VITE_BUILD_COMMIT === headSha, "VITE_BUILD_COMMIT must match the current release commit.");
}
assert(
requiresMacosSigning || requiresWindowsSigning,
"Release readiness requires REQUIRE_DESKTOP_SIGNING=true or REQUIRE_WINDOWS_SIGNING=true.",
);
assert(
!(requiresMacosSigning && requiresWindowsSigning),
"macOS and Windows signing readiness must be checked in their native jobs.",
);
assert(
desktopReleasePlatform === "macos" || desktopReleasePlatform === "windows",
"DESKTOP_RELEASE_PLATFORM must be macos or windows.",
);
assert(
desktopPlatformSigningMode === desktopReleaseResolution.platformSigningMode,
`DESKTOP_PLATFORM_SIGNING_MODE must be ${desktopReleaseResolution.platformSigningMode} for v${packageInfo.version}.`,
);
assert(
desktopReleasePolicy.updaterSigningRequired === true && requiresUpdaterSigning,
"Release readiness requires REQUIRE_UPDATER_SIGNING=true; updater signing cannot be disabled.",
);
for (const name of requiredUpdaterEnv) {
requireEnv(name);
}
if (desktopPlatformSigningMode === "signed") {
assert(!allowsUnsignedPlatformRelease, "Signed releases must not enable ALLOW_UNSIGNED_PLATFORM_RELEASE.");
assert(
(desktopReleasePlatform === "macos" && requiresMacosSigning && !requiresWindowsSigning) ||
(desktopReleasePlatform === "windows" && requiresWindowsSigning && !requiresMacosSigning),
"Signed readiness must enable only the matching native platform signing requirement.",
);
}
if (desktopPlatformSigningMode === "unsigned-exception") {
assert(
desktopReleaseResolution.platformSigningMode === "unsigned-exception",
`v${packageInfo.version} has no approved unsigned platform release exception.`,
);
assert(allowsUnsignedPlatformRelease, "The approved exception requires ALLOW_UNSIGNED_PLATFORM_RELEASE=true.");
assert(
!requiresMacosSigning && !requiresWindowsSigning,
"Unsigned platform readiness must not claim Apple or Windows platform signing.",
);
if (desktopReleasePlatform === "macos") {
assert(process.platform === "darwin", "The macOS ad-hoc readiness check must run on macOS.");
}
if (desktopReleasePlatform === "windows") {
assert(process.platform === "win32", "The unsigned Windows readiness check must run on Windows.");
}
}
if (requiresMacosSigning) {
assert(process.platform === "darwin", "Signed macOS desktop release readiness must run on macOS.");
for (const name of requiredMacosEnv) {
+34 -1
View File
@@ -62,7 +62,7 @@ const verifyTauriConfig = async () => {
assert(targetList.includes("dmg"), "Tauri bundle targets must include dmg for macOS distribution.");
assert(
tauriConfig.bundle?.createUpdaterArtifacts === true,
"Tauri must create updater artifacts for signed desktop release builds.",
"Tauri must create signed updater artifacts for formal desktop release builds.",
);
assert(
typeof tauriConfig.plugins?.updater?.pubkey === "string" && tauriConfig.plugins.updater.pubkey.length > 80,
@@ -392,7 +392,10 @@ const verifyWorkflowGates = async () => {
assert(packageInfo.engines?.node === ">=22.13.0", "package.json must require Node.js >=22.13.0.");
const requiredScripts = [
"desktop:build:macos-release",
"desktop:build:macos-unsigned-release",
"desktop:build:windows-release",
"desktop:release-policy:check",
"desktop:release-provenance:create",
"desktop:update-feed:create",
"desktop:update-feed:check",
"desktop:release-readiness:check",
@@ -403,6 +406,26 @@ const verifyWorkflowGates = async () => {
assert(Boolean(packageInfo.scripts?.[script]), `package.json must define ${script}.`);
}
const releasePolicy = await readJson(resolve(frontendDir, "desktop-release-policy.json"));
assert(releasePolicy.schemaVersion === 1, "Desktop release policy schemaVersion must be 1.");
assert(
releasePolicy.defaultPlatformSigningMode === "signed",
"Desktop release policy must default future versions to signed platform releases.",
);
assert(releasePolicy.updaterSigningRequired === true, "Desktop release policy must require updater signing.");
const currentUnsignedException = releasePolicy.unsignedPlatformExceptions?.find(
(exception) => exception.version === packageInfo.version,
);
if (packageInfo.version === "0.1.0") {
assert(Boolean(currentUnsignedException), "Desktop release policy must record the approved v0.1.0 exception.");
}
if (currentUnsignedException) {
assert(
currentUnsignedException.macos === "ad-hoc" && currentUnsignedException.windows === "unsigned",
`The v${packageInfo.version} exception must use macOS ad-hoc and unsigned Windows modes.`,
);
}
const workflow = await readFile(resolve(rootDir, ".github/workflows/client-quality-gates.yml"), "utf8");
const requiredCommands = [
"npm run version:check",
@@ -429,6 +452,16 @@ const verifyWorkflowGates = async () => {
"runs-on: windows-latest",
"REQUIRE_DESKTOP_SIGNING",
"REQUIRE_WINDOWS_SIGNING",
"REQUIRE_UPDATER_SIGNING",
"ALLOW_UNSIGNED_PLATFORM_RELEASE",
"DESKTOP_PLATFORM_SIGNING_MODE",
"desktop:release-policy:check",
"desktop:build:macos-unsigned-release",
"Signature=adhoc",
'"NotSigned"',
"UNSIGNED-PLATFORM",
"DESKTOP-RELEASE-PROVENANCE.json",
"--require-provenance",
"TAURI_SIGNING_PRIVATE_KEY",
"APPLE_ID",
"APPLE_PASSWORD",
@@ -2,9 +2,12 @@ import { access, readFile } from "node:fs/promises";
import { createHash } from "node:crypto";
import { basename, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { loadDesktopReleasePolicy, resolveDesktopReleasePolicy } from "./desktop-release-policy.mjs";
const frontendDir = fileURLToPath(new URL("../", import.meta.url));
const packageInfo = JSON.parse(await readFile(resolve(frontendDir, "package.json"), "utf8"));
const desktopReleasePolicy = await loadDesktopReleasePolicy();
const desktopReleaseResolution = resolveDesktopReleasePolicy(packageInfo.version, desktopReleasePolicy);
const failures = [];
const args = process.argv.slice(2);
@@ -21,6 +24,11 @@ const feedPath = resolve(
);
const artifactDir = optionValue("--artifacts-dir") || process.env.DESKTOP_UPDATE_ARTIFACTS_DIR;
const expectedBaseUrl = optionValue("--base-url") || process.env.DESKTOP_UPDATE_BASE_URL;
const requiresProvenance = args.includes("--require-provenance");
const provenancePath =
optionValue("--provenance") ||
process.env.DESKTOP_RELEASE_PROVENANCE ||
(artifactDir ? resolve(artifactDir, "DESKTOP-RELEASE-PROVENANCE.json") : undefined);
const requiredPlatforms = new Set([
"darwin-aarch64",
"darwin-x86_64",
@@ -94,6 +102,56 @@ const assertManifestEntries = async (checksums) => {
}
};
const verifyProvenance = async (checksums) => {
if (!requiresProvenance) return;
if (!provenancePath) {
fail("Desktop release provenance path is required.");
return;
}
let provenance;
try {
provenance = JSON.parse(await readFile(provenancePath, "utf8"));
} catch (error) {
fail(`Cannot read desktop release provenance ${provenancePath}: ${error.message}`);
return;
}
assert(provenance.schemaVersion === 1, "Desktop release provenance schemaVersion must be 1.");
assert(provenance.version === packageInfo.version, "Desktop release provenance version must match the package version.");
assert(provenance.tag === `v${packageInfo.version}`, "Desktop release provenance tag must match the package version.");
assert(/^[0-9a-f]{40}$/i.test(provenance.commit || ""), "Desktop release provenance must contain a full commit SHA.");
assert(
provenance.platformSigningMode === desktopReleaseResolution.platformSigningMode,
`Desktop release provenance platformSigningMode must be ${desktopReleaseResolution.platformSigningMode}.`,
);
assert(
provenance.platformSigning?.macos === desktopReleaseResolution.macosSigning,
`Desktop release provenance macOS signing must be ${desktopReleaseResolution.macosSigning}.`,
);
assert(
provenance.platformSigning?.windows === desktopReleaseResolution.windowsSigning,
`Desktop release provenance Windows signing must be ${desktopReleaseResolution.windowsSigning}.`,
);
assert(
provenance.updaterSigning === "required-and-verified",
"Desktop release provenance must record required-and-verified updater signing.",
);
assert(
provenance.artifactLabel === desktopReleaseResolution.artifactLabel,
`Desktop release provenance artifactLabel must be ${desktopReleaseResolution.artifactLabel}.`,
);
if (desktopReleaseResolution.warningRequired) {
assert(
Array.isArray(provenance.warnings) && provenance.warnings.length >= 2,
"Unsigned platform release provenance must include installation trust warnings.",
);
}
if (artifactDir) {
await assertChecksum(checksums, provenancePath, "desktop release provenance");
}
};
let feed;
try {
feed = JSON.parse(await readFile(feedPath, "utf8"));
@@ -104,6 +162,7 @@ try {
if (feed) {
const checksums = await readChecksumManifest();
await assertManifestEntries(checksums);
await verifyProvenance(checksums);
const normalizedFeedVersion = String(feed.version || "").replace(/^v/, "");
const platforms = feed.platforms || {};
const darwinArm = platforms["darwin-aarch64"];
+64 -9
View File
@@ -2,10 +2,13 @@ import { execFileSync } from "node:child_process";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { loadDesktopReleasePolicy, resolveDesktopReleasePolicy } from "./desktop-release-policy.mjs";
const frontendDir = fileURLToPath(new URL("../", import.meta.url));
const rootDir = resolve(frontendDir, "..");
const packageInfo = JSON.parse(await readFile(resolve(frontendDir, "package.json"), "utf8"));
const desktopReleasePolicy = await loadDesktopReleasePolicy();
const desktopReleaseResolution = resolveDesktopReleasePolicy(packageInfo.version, desktopReleasePolicy);
const failures = [];
const allowedChannels = new Set(["dev", "main", "release", "local"]);
@@ -20,6 +23,17 @@ const isTagBuild = env.GITHUB_REF_TYPE === "tag";
const isReleaseBuild = env.RELEASE_BUILD === "true" || isTagBuild || channel === "release";
const requiresMacosSigning = env.REQUIRE_DESKTOP_SIGNING === "true";
const requiresWindowsSigning = env.REQUIRE_WINDOWS_SIGNING === "true";
const requiresUpdaterSigning = env.REQUIRE_UPDATER_SIGNING === "true";
const allowsUnsignedPlatformRelease = env.ALLOW_UNSIGNED_PLATFORM_RELEASE === "true";
const desktopPlatformSigningMode = env.DESKTOP_PLATFORM_SIGNING_MODE;
const desktopReleasePlatform = env.DESKTOP_RELEASE_PLATFORM;
const isNativeDesktopRelease = Boolean(
desktopReleasePlatform ||
desktopPlatformSigningMode ||
requiresMacosSigning ||
requiresWindowsSigning ||
requiresUpdaterSigning,
);
const fail = (message) => failures.push(message);
const assert = (condition, message) => {
@@ -27,7 +41,7 @@ const assert = (condition, message) => {
};
const requireEnv = (name) => {
assert(Boolean(env[name]), `${name} must be configured for signed desktop release builds.`);
assert(Boolean(env[name]), `${name} must be configured for this desktop release build.`);
};
const gitHead = () => {
@@ -74,13 +88,59 @@ assert(
"macOS and Windows signing requirements must be checked in their native jobs.",
);
if (requiresMacosSigning) {
assert(process.platform === "darwin", "Signed macOS desktop release builds must run on macOS.");
if (isNativeDesktopRelease) {
assert(isReleaseBuild, "Native desktop release settings may only be used for release builds.");
assert(
desktopReleasePolicy.updaterSigningRequired === true && requiresUpdaterSigning,
"Formal desktop releases must set REQUIRE_UPDATER_SIGNING=true; updater signing cannot be disabled.",
);
assert(
desktopPlatformSigningMode === desktopReleaseResolution.platformSigningMode,
`DESKTOP_PLATFORM_SIGNING_MODE must be ${desktopReleaseResolution.platformSigningMode} for v${packageInfo.version}.`,
);
assert(
desktopReleasePlatform === "macos" || desktopReleasePlatform === "windows",
"DESKTOP_RELEASE_PLATFORM must be macos or windows for native desktop release builds.",
);
if (isCi) {
assert(isTagBuild, "Signed desktop release candidate builds in CI must run from a release tag.");
assert(isTagBuild, "Native desktop release candidate builds in CI must run from a release tag.");
}
}
if (requiresUpdaterSigning) {
requireEnv("TAURI_SIGNING_PRIVATE_KEY");
requireEnv("TAURI_SIGNING_PRIVATE_KEY_PASSWORD");
}
if (desktopPlatformSigningMode === "signed") {
assert(!allowsUnsignedPlatformRelease, "Signed releases must not enable ALLOW_UNSIGNED_PLATFORM_RELEASE.");
assert(
(desktopReleasePlatform === "macos" && requiresMacosSigning && !requiresWindowsSigning) ||
(desktopReleasePlatform === "windows" && requiresWindowsSigning && !requiresMacosSigning),
"Signed native release jobs must enable only their matching platform signing requirement.",
);
}
if (desktopPlatformSigningMode === "unsigned-exception") {
assert(
desktopReleaseResolution.platformSigningMode === "unsigned-exception",
`v${packageInfo.version} has no approved unsigned platform release exception.`,
);
assert(allowsUnsignedPlatformRelease, "The approved exception requires ALLOW_UNSIGNED_PLATFORM_RELEASE=true.");
assert(
!requiresMacosSigning && !requiresWindowsSigning,
"Unsigned platform exceptions must not claim Apple or Windows platform signing.",
);
if (desktopReleasePlatform === "macos") {
assert(process.platform === "darwin", "The macOS ad-hoc release exception must run on macOS.");
}
if (desktopReleasePlatform === "windows") {
assert(process.platform === "win32", "The unsigned Windows release exception must run on Windows.");
}
}
if (requiresMacosSigning) {
assert(process.platform === "darwin", "Signed macOS desktop release builds must run on macOS.");
requireEnv("APPLE_ID");
requireEnv("APPLE_PASSWORD");
requireEnv("APPLE_TEAM_ID");
@@ -95,11 +155,6 @@ if (requiresMacosSigning) {
if (requiresWindowsSigning) {
assert(process.platform === "win32", "Signed Windows desktop release builds must run on Windows.");
if (isCi) {
assert(isTagBuild, "Signed Windows desktop release candidate builds in CI must run from a release tag.");
}
requireEnv("TAURI_SIGNING_PRIVATE_KEY");
requireEnv("TAURI_SIGNING_PRIVATE_KEY_PASSWORD");
requireEnv("WINDOWS_CERTIFICATE");
requireEnv("WINDOWS_CERTIFICATE_PASSWORD");
requireEnv("WINDOWS_TIMESTAMP_URL");