返回 CodeWhale
fetch-compiled-host-proof.js
根目录 / scripts / release / fetch-compiled-host-proof.js
1 #!/usr/bin/env node
2 // Fetch only a completed, green official CI receipt for this exact source.
3 // This does not install Bun, publish assets, or confer runtime authority.
4 const { execFileSync } = require("node:child_process");
5 const fs = require("node:fs");
6 const path = require("node:path");
7 const hosts = require("../../npm/codewhale/scripts/compiled-hosts");
8
9 function fetchProof({ repo, runId, sourceSha, target, output }, exec = execFileSync) {
10 if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo) || !/^\d+$/.test(runId) || !/^[a-f0-9]{40}$/.test(sourceSha) || !hosts.TARGETS[target]) throw new Error("invalid exact-source CI proof request");
11 const gh = (args) => exec(process.env.GH_BIN || "gh", args, { encoding: "utf8", maxBuffer: 1024 * 1024 });
12 const workflow = JSON.parse(gh(["api", `repos/${repo}/actions/workflows/ci.yml`]));
13 const run = JSON.parse(gh(["api", `repos/${repo}/actions/runs/${runId}`]));
14 if (run.workflow_id !== workflow.id || run.path !== ".github/workflows/ci.yml" || run.head_sha !== sourceSha || run.status !== "completed" || run.conclusion !== "success" || run.repository?.full_name !== repo) throw new Error("compiled-host proof must come from green official ci.yml at this exact source/repository");
15 const runner = { linux: "Linux", darwin: "macOS", win32: "Windows" }[hosts.TARGETS[target][0]];
16 const architecture = { x64: "X64", arm64: "ARM64" }[hosts.TARGETS[target][1]];
17 const name = `native-compiled-host-${runner}-${architecture}`;
18 const pages = JSON.parse(gh(["api", `repos/${repo}/actions/runs/${runId}/artifacts?per_page=100`, "--paginate", "--slurp"]));
19 const artifacts = pages.flatMap((page) => page.artifacts || []).filter((artifact) => artifact.name === name && !artifact.expired);
20 if (artifacts.length !== 1) throw new Error(`CI has no unique current ${name} artifact; delivery is not qualified`);
21 fs.mkdirSync(output, { recursive: true });
22 if (fs.readdirSync(output).length) throw new Error("compiled proof directory must be empty");
23 gh(["run", "download", runId, "--repo", repo, "--name", name, "--dir", path.resolve(output)]);
24 const readRegular = (name, limit = Infinity) => {
25 if (typeof name !== "string" || path.basename(name) !== name || name === "." || name === "..") throw new Error("proof files must be contained basenames");
26 const file = path.join(output, name);
27 const metadata = fs.lstatSync(file);
28 if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size > limit) throw new Error(`invalid proof file: ${name}`);
29 return fs.readFileSync(file);
30 };
31 const receipt = JSON.parse(readRegular("native-receipt.json", 64 * 1024));
32 if (receipt.scope !== "native-compiled-host" || receipt.source_sha !== sourceSha || receipt.platform !== hosts.TARGETS[target][0] || receipt.arch !== hosts.TARGETS[target][1] || receipt.failed !== 0 || receipt.skipped !== 0 || (!Number.isSafeInteger(receipt.passed) || receipt.passed < hosts.nativeMinimum(target))) throw new Error("downloaded CI receipt does not qualify this exact native target");
33 const image = path.join(output, hosts.HOST_NAME + (target.startsWith("windows-") ? ".exe" : ""));
34 hosts.verifyBytes(readRegular(path.basename(image)), receipt.host_sha256, "exact contained CI image");
35 if (typeof receipt.log !== "string" || path.basename(receipt.log) !== receipt.log) throw new Error("Native proof log must be a contained basename");
36 hosts.verifyBytes(readRegular(receipt.log), receipt.log_sha256, "Native qualification log");
37 return { image, receipt: path.join(output, "native-receipt.json") };
38 }
39
40 if (require.main === module) {
41 const args = process.argv.slice(2);
42 const value = (name) => { const index = args.indexOf(name); if (index < 0 || !args[index + 1]) throw new Error(`required ${name}`); return args[index + 1]; };
43 try {
44 console.log(JSON.stringify(fetchProof({ repo: value("--repo"), runId: value("--run-id"), sourceSha: value("--source-sha"), target: value("--target"), output: value("--output") })));
45 } catch (error) { console.error(error.message); process.exitCode = 1; }
46 }
47 module.exports = { fetchProof };
48
48 lines JAVASCRIPT