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 releaseStage = value("--stage") || "complete"; 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}.`); assert(["complete", "macos-first"].includes(releaseStage), "Release provenance stage must be complete or macos-first."); if (releaseStage === "macos-first") { assert( resolution.macosLocalExactTagFallback === true && resolution.windowsDelivery === "deferred-same-tag", `v${packageInfo.version} does not approve a staged local macOS-first release.`, ); } 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, stage: releaseStage, platformAvailability: releaseStage === "macos-first" ? { macos: "available", windows: "pending-same-tag" } : { macos: "available", windows: "available" }, updaterFeedActivation: releaseStage === "macos-first" ? "withheld-pending-combined-verification" : "combined-platform-verification-required", 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}`);