77 lines
2.3 KiB
JavaScript
77 lines
2.3 KiB
JavaScript
import { spawnSync } from "node:child_process";
|
|
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
|
|
import { supplyChainDigest } from "./lib/supply-chain.mjs";
|
|
|
|
/** @param {string} directory @returns {Promise<string[]>} */
|
|
async function filesWithin(directory) {
|
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
const nested = /** @type {string[][]} */ (await Promise.all(
|
|
entries.map((entry) => {
|
|
const target = path.join(directory, entry.name);
|
|
return entry.isDirectory() ? filesWithin(target) : [target];
|
|
}),
|
|
));
|
|
return nested.flat().sort();
|
|
}
|
|
|
|
async function distDigest() {
|
|
const rows = await Promise.all(
|
|
(await filesWithin("dist")).map(async (file) => ({
|
|
path: path.relative("dist", file).replaceAll("\\", "/"),
|
|
bytes: (await readFile(file)).byteLength,
|
|
content: supplyChainDigest(await readFile(file)),
|
|
})),
|
|
);
|
|
return supplyChainDigest(rows);
|
|
}
|
|
|
|
function build(environment = process.env) {
|
|
return spawnSync("corepack", ["pnpm", "build"], {
|
|
env: environment,
|
|
encoding: "utf8",
|
|
maxBuffer: 16 * 1024 * 1024,
|
|
});
|
|
}
|
|
|
|
const deterministicEnvironment = {
|
|
...process.env,
|
|
SOURCE_DATE_EPOCH: "946684800",
|
|
};
|
|
const firstBuild = build(deterministicEnvironment);
|
|
const firstDigest = firstBuild.status === 0 ? await distDigest() : "BUILD_FAILED";
|
|
const secondBuild = build(deterministicEnvironment);
|
|
const secondDigest =
|
|
secondBuild.status === 0 ? await distDigest() : "BUILD_FAILED";
|
|
const restoreBuild = build();
|
|
const passed =
|
|
firstBuild.status === 0 &&
|
|
secondBuild.status === 0 &&
|
|
restoreBuild.status === 0 &&
|
|
firstDigest === secondDigest;
|
|
|
|
await mkdir("artifacts/release", { recursive: true });
|
|
await writeFile(
|
|
"artifacts/release/reproducible-build.json",
|
|
`${JSON.stringify(
|
|
{
|
|
schemaVersion: 1,
|
|
sourceDateEpoch: deterministicEnvironment.SOURCE_DATE_EPOCH,
|
|
firstDigest,
|
|
secondDigest,
|
|
restored: restoreBuild.status === 0,
|
|
status: passed ? "PASS" : "FAIL",
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`,
|
|
);
|
|
if (!passed) {
|
|
process.stderr.write(
|
|
`Reproducible build failed: first=${firstDigest} second=${secondDigest}\n`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
process.stdout.write(`Reproducible build: PASS (${firstDigest})\n`);
|