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"]); const fullShaPattern = /^[0-9a-f]{40}$/i; const semverTag = `v${packageInfo.version}`; const env = process.env; const channel = env.VITE_BUILD_CHANNEL || "local"; const commit = env.VITE_BUILD_COMMIT || "local"; const isCi = env.CI === "true" || env.GITHUB_ACTIONS === "true"; 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) => { if (!condition) fail(message); }; const requireEnv = (name) => { assert(Boolean(env[name]), `${name} must be configured for this desktop release build.`); }; const gitHead = () => { try { return execFileSync("git", ["rev-parse", "HEAD"], { cwd: rootDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], }).trim(); } catch { return undefined; } }; assert(allowedChannels.has(channel), `VITE_BUILD_CHANNEL must be one of ${[...allowedChannels].join(", ")}.`); if (isCi || isReleaseBuild) { assert(channel !== "local", "CI and release builds must inject VITE_BUILD_CHANNEL."); assert(fullShaPattern.test(commit), "CI and release builds must inject a full 40-character VITE_BUILD_COMMIT."); } if (env.GITHUB_SHA) { assert( commit === env.GITHUB_SHA || commit === "local", "VITE_BUILD_COMMIT must match GITHUB_SHA when GitHub Actions provides a source commit.", ); } if (isReleaseBuild && fullShaPattern.test(commit)) { const headSha = gitHead(); assert(Boolean(headSha), "Release builds must be able to resolve the current Git commit."); if (headSha) { assert(commit === headSha, "VITE_BUILD_COMMIT must match the current Git HEAD for release builds."); } } if (isTagBuild) { assert(env.GITHUB_REF_NAME === semverTag, `Release tag must be ${semverTag}; found ${env.GITHUB_REF_NAME || ""}.`); assert(channel === "release", "Release tag builds must set VITE_BUILD_CHANNEL=release."); } assert( !(requiresMacosSigning && requiresWindowsSigning), "macOS and Windows signing requirements must be checked in their native jobs.", ); 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, "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"); assert( Boolean(env.APPLE_CERTIFICATE || env.APPLE_SIGNING_IDENTITY), "APPLE_CERTIFICATE or APPLE_SIGNING_IDENTITY must be configured for macOS signing.", ); if (env.APPLE_CERTIFICATE) { requireEnv("APPLE_CERTIFICATE_PASSWORD"); } } if (requiresWindowsSigning) { assert(process.platform === "win32", "Signed Windows desktop release builds must run on Windows."); requireEnv("WINDOWS_CERTIFICATE"); requireEnv("WINDOWS_CERTIFICATE_PASSWORD"); requireEnv("WINDOWS_TIMESTAMP_URL"); } if (failures.length > 0) { console.error(`Release build environment check failed:\n${failures.map((item) => ` - ${item}`).join("\n")}`); process.exitCode = 1; } else { console.log("Release build environment check passed."); }