export const CI_BUILD_ENVIRONMENT_VARIABLES = Object.freeze([ "VITE_BUILD_ID", "VITE_COMMIT_SHA", "RELEASE_ID", "CI_RUNNER_IMAGE", "SOURCE_DATE_EPOCH", ]); /** * The repository's single definition of "this is a CI run". The workflow sets * `CI: "true"` at the top-level `env:` block, so it holds in every job; the * `CI_RUN_ID` family is declared only by the release-tier provider jobs and is * absent from the merge gates. */ export function isCiRun( environment: Readonly>, ) { return environment.CI === "true"; } export function ciBuildEnvironmentFailures( environment: Readonly>, ) { if (!isCiRun(environment)) return []; const failures = CI_BUILD_ENVIRONMENT_VARIABLES.filter( (name) => !environment[name]?.trim(), ).map((name) => `missing required CI build environment: ${name}`); const commitSha = environment.VITE_COMMIT_SHA?.trim(); if (commitSha && !isValidCommitSha(commitSha)) { failures.push( "VITE_COMMIT_SHA must be a full 40- or 64-character hexadecimal commit ID", ); } const sourceDateEpoch = environment.SOURCE_DATE_EPOCH?.trim(); if (sourceDateEpoch && !isValidSourceDateEpoch(sourceDateEpoch)) { failures.push("SOURCE_DATE_EPOCH must be non-negative epoch seconds"); } const runnerImage = environment.CI_RUNNER_IMAGE?.trim(); if ( runnerImage && !/@sha256:[0-9a-f]{64}$/i.test(runnerImage) ) { failures.push( "CI_RUNNER_IMAGE must end with an immutable @sha256 image digest", ); } return failures; } export function assertCiBuildEnvironment( environment: Readonly>, ) { const failures = ciBuildEnvironmentFailures(environment); if (failures.length > 0) { throw new Error(failures.join("; ")); } } export function isValidCommitSha(value: string) { return /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(value); } export function isValidSourceDateEpoch(value: string) { if (!/^\d+$/.test(value)) return false; const milliseconds = Number(value) * 1_000; return Number.isSafeInteger(milliseconds) && Number.isFinite( new Date(milliseconds).getTime(), ); } export function ciCheckoutIdentityFailures( environment: Readonly>, checkout: { commitSha: string; sourceDateEpoch: string }, ) { if (environment.CI !== "true") return []; const failures = []; const configuredCommitSha = environment.VITE_COMMIT_SHA?.trim(); if ( configuredCommitSha && configuredCommitSha.toLowerCase() !== checkout.commitSha.toLowerCase() ) { failures.push("VITE_COMMIT_SHA does not identify the checked-out commit"); } const configuredEpoch = environment.SOURCE_DATE_EPOCH?.trim(); if (configuredEpoch && configuredEpoch !== checkout.sourceDateEpoch) { failures.push( "SOURCE_DATE_EPOCH does not match the checked-out commit timestamp", ); } return failures; } export function buildDate( environment: Readonly>, ) { const sourceDateEpoch = environment.SOURCE_DATE_EPOCH?.trim(); if (!sourceDateEpoch) return new Date(); if (!isValidSourceDateEpoch(sourceDateEpoch)) { throw new Error("SOURCE_DATE_EPOCH must be non-negative epoch seconds"); } return new Date(Number(sourceDateEpoch) * 1_000); }