113 lines
4.7 KiB
JavaScript
113 lines
4.7 KiB
JavaScript
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.`);
|
|
if (exception?.version === "0.1.0") {
|
|
assert(exception?.macosLocalExactTagFallback === true, `${prefix} must explicitly approve the local exact-tag macOS fallback.`);
|
|
assert(exception?.windowsDelivery === "deferred-same-tag", `${prefix} must defer Windows only from the same tag.`);
|
|
assert(
|
|
exception?.updaterFeedActivation === "after-combined-platform-verification",
|
|
`${prefix} must withhold the production updater feed until both platforms are verified.`,
|
|
);
|
|
assert(
|
|
exception?.githubReleaseAssetProfile === "installer-and-checksum-only",
|
|
`${prefix} must keep the user-facing GitHub Release limited to installers and their checksum manifest.`,
|
|
);
|
|
assert(
|
|
exception?.stagedEvidenceRetention === "private-until-updater-publish",
|
|
`${prefix} must retain updater artifacts and release evidence privately until updater publication.`,
|
|
);
|
|
assert(
|
|
exception?.updaterArtifactPublishTarget === "anonymous-https-origin",
|
|
`${prefix} must publish updater artifacts to an anonymous HTTPS origin.`,
|
|
);
|
|
}
|
|
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,
|
|
macosLocalExactTagFallback: exception.macosLocalExactTagFallback === true,
|
|
windowsDelivery: exception.windowsDelivery,
|
|
updaterFeedActivation: exception.updaterFeedActivation,
|
|
githubReleaseAssetProfile: exception.githubReleaseAssetProfile,
|
|
stagedEvidenceRetention: exception.stagedEvidenceRetention,
|
|
updaterArtifactPublishTarget: exception.updaterArtifactPublishTarget,
|
|
exception,
|
|
};
|
|
};
|