chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
export const CI_BUILD_ENVIRONMENT_VARIABLES = Object.freeze([
|
||||
"VITE_BUILD_ID",
|
||||
"VITE_COMMIT_SHA",
|
||||
"RELEASE_ID",
|
||||
"CI_RUNNER_IMAGE",
|
||||
"SOURCE_DATE_EPOCH",
|
||||
]);
|
||||
|
||||
export function ciBuildEnvironmentFailures(
|
||||
environment: Readonly<Record<string, string | undefined>>,
|
||||
) {
|
||||
if (environment.CI !== "true") 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<Record<string, string | undefined>>,
|
||||
) {
|
||||
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<Record<string, string | undefined>>,
|
||||
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<Record<string, string | undefined>>,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user