158 lines
6.9 KiB
JavaScript
158 lines
6.9 KiB
JavaScript
import { spawnSync } from "node:child_process";
|
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { basename, join, resolve } from "node:path";
|
|
|
|
import { buildAndValidatePortablePackage } from "./lib/portable-package.mjs";
|
|
import { readCandidateEnvironment } from "./lib/release-candidate.mjs";
|
|
import {
|
|
buildFinalReleaseRecord,
|
|
scanReleaseFiles,
|
|
sha256File,
|
|
validateFinalEvidence,
|
|
} from "./lib/wp7-07-final-release.mjs";
|
|
|
|
const runId = process.env.DADA_TDD_RUN_ID ?? `wp7-07-final-${new Date().toISOString().replace(/[^0-9]/g, "")}`;
|
|
const runDirectory = resolve("artifacts", "tdd", runId);
|
|
const releaseCase = resolve(runDirectory, "cases", "TDD-WP7-REL-001-final-release-record");
|
|
const securityCase = resolve(runDirectory, "cases", "TDD-WP7-SEC-001-artifact-leak-scan");
|
|
const outputRoot = resolve(".build", "wp7-07-final-release");
|
|
if (existsSync(runDirectory)) throw new Error(`Evidence run already exists: ${runId}`);
|
|
mkdirSync(releaseCase, { recursive: true });
|
|
mkdirSync(securityCase, { recursive: true });
|
|
|
|
function writeJson(path, value) {
|
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
}
|
|
|
|
function git(args) {
|
|
const result = spawnSync("git", args, { encoding: "utf8", maxBuffer: 64 * 1024 * 1024, timeout: 120_000 });
|
|
if ((result.status ?? 1) !== 0) throw new Error(`WP7_07_GIT_FAILED:${args.join(" ")}`);
|
|
return result.stdout.trim();
|
|
}
|
|
|
|
function gitGrep(args) {
|
|
const result = spawnSync("git", ["grep", ...args], { encoding: "utf8", maxBuffer: 64 * 1024 * 1024, timeout: 120_000 });
|
|
if (![0, 1].includes(result.status ?? 2)) throw new Error("WP7_07_GIT_GREP_FAILED");
|
|
return result.status === 0 ? result.stdout.trim() : "";
|
|
}
|
|
|
|
function run(name, command) {
|
|
const startedAt = new Date().toISOString();
|
|
const result = spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", command], {
|
|
encoding: "utf8",
|
|
env: process.env,
|
|
maxBuffer: 64 * 1024 * 1024,
|
|
});
|
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
return { command, exit_code: result.status ?? 1, finished_at: new Date().toISOString(), name, started_at: startedAt };
|
|
}
|
|
|
|
const currentCommit = git(["rev-parse", "HEAD"]);
|
|
const prefreezeCommit = git(["ls-remote", "origin", "refs/heads/codex/wp7-06"]).split(/\s+/)[0];
|
|
if (!prefreezeCommit || spawnSync("git", ["merge-base", "--is-ancestor", prefreezeCommit, "HEAD"]).status !== 0) {
|
|
throw new Error("WP7_07_PREFREEZE_LINEAGE_INVALID");
|
|
}
|
|
|
|
const commands = [
|
|
run("unit", "node --test tests/package/wp7-07-final-release.test.mjs"),
|
|
run("security", "pnpm test:security"),
|
|
run("trace", "pnpm validate:tdd-trace"),
|
|
];
|
|
if (commands.some(({ exit_code }) => exit_code !== 0)) {
|
|
writeJson(join(releaseCase, "commands.json"), { commands, run_id: runId, schema_version: "1.0" });
|
|
process.exit(1);
|
|
}
|
|
|
|
const environment = readCandidateEnvironment();
|
|
const packageJson = JSON.parse(readFileSync("package.json", "utf8"));
|
|
const release = buildFinalReleaseRecord({
|
|
appVersion: packageJson.version,
|
|
browsers: environment.browsers.map(({ brand, full_version }) => ({ brand, fullVersion: full_version })),
|
|
buildCommit: currentCommit,
|
|
frozenFromCommit: prefreezeCommit,
|
|
recordedAt: new Date().toISOString(),
|
|
windows: {
|
|
arch: environment.windows.arch,
|
|
build: environment.windows.build,
|
|
displayVersion: environment.windows.display_version,
|
|
},
|
|
});
|
|
writeJson(resolve("RELEASE.json"), release);
|
|
|
|
const packageResult = await buildAndValidatePortablePackage({ evidenceDirectory: releaseCase, outputRoot, releaseRecord: release });
|
|
const packageDirectory = join(outputRoot, packageResult.packageManifest.package_name);
|
|
const zipPath = join(outputRoot, `${packageResult.packageManifest.package_name}.zip`);
|
|
const releaseSha256 = sha256File(resolve("RELEASE.json"));
|
|
const packageReleaseSha256 = sha256File(join(packageDirectory, "RELEASE.json"));
|
|
if (releaseSha256 !== packageReleaseSha256) throw new Error("WP7_07_PACKAGE_RELEASE_DRIFT");
|
|
|
|
const finalScan = scanReleaseFiles({ roots: [packageDirectory, releaseCase] });
|
|
const trackedSensitive = gitGrep(["-I", "-n", "-E", "C:\\\\Users\\\\[^\\\\]+|sk-[A-Za-z0-9_-]{24,}", "HEAD", "--", ":!tests", ":!scripts/lib/wp7-07-final-release.mjs"]);
|
|
const scan = {
|
|
...finalScan,
|
|
git_current_findings: trackedSensitive ? trackedSensitive.split(/\r?\n/).filter(Boolean) : [],
|
|
status: finalScan.status === "passed" && !trackedSensitive ? "passed" : "failed",
|
|
};
|
|
writeJson(join(securityCase, "scan-report.json"), scan);
|
|
writeJson(join(securityCase, "allowlist.json"), {
|
|
entries: ["tests/**:synthetic security traps", "scripts/lib/wp7-07-final-release.mjs:scanner patterns"],
|
|
real_values_allowed: false,
|
|
schema_version: "1.0",
|
|
});
|
|
if (scan.status !== "passed") throw new Error("WP7_07_LEAK_SCAN_FAILED");
|
|
|
|
const finalEvidence = validateFinalEvidence({ packageManifest: packageResult.packageManifest, release, releaseSha256, scan });
|
|
copyFileSync(resolve("RELEASE.json"), join(releaseCase, "RELEASE.json"));
|
|
copyFileSync(join(packageDirectory, "START-HERE.txt"), join(releaseCase, "START-HERE.txt"));
|
|
writeJson(join(releaseCase, "environment.json"), {
|
|
browsers: release.browsers,
|
|
fixed_port: release.fixedPort,
|
|
windows: release.windows,
|
|
schema_version: "1.0",
|
|
});
|
|
writeJson(join(releaseCase, "final-package.json"), {
|
|
file_name: basename(zipPath),
|
|
release_sha256: finalEvidence.release_sha256,
|
|
release_status: release.releaseStatus,
|
|
zip_sha256: finalEvidence.zip_sha256,
|
|
schema_version: "1.0",
|
|
});
|
|
writeJson(join(releaseCase, "commands.json"), { commands, run_id: runId, schema_version: "1.0" });
|
|
writeJson(join(releaseCase, "result.json"), {
|
|
acceptance_criteria: ["AC-24", "AC-41", "AC-48", "AC-56"],
|
|
deferred_external_tasks: release.deferredExternalTasks,
|
|
evidence_refs: ["RELEASE.json", "START-HERE.txt", "environment.json", "package-manifest.json", "final-package.json"],
|
|
release_gate: ["release:P0-A"],
|
|
requirements: ["NFR-01", "NFR-09", "PRIV-01", "PRIV-02"],
|
|
status: "passed",
|
|
task_id: "TASK-WP7-07",
|
|
test_id: "TDD-WP7-REL-001-final-release-record",
|
|
});
|
|
writeJson(join(securityCase, "result.json"), {
|
|
evidence_refs: ["scan-report.json", "allowlist.json"],
|
|
status: "passed",
|
|
task_id: "TASK-WP7-07",
|
|
test_id: "TDD-WP7-SEC-001-artifact-leak-scan",
|
|
});
|
|
writeJson(join(runDirectory, "evidence.json"), {
|
|
cases: [
|
|
{ missing_evidence: [], status: "passed", test_id: "TDD-WP7-REL-001-final-release-record" },
|
|
{ missing_evidence: [], status: "passed", test_id: "TDD-WP7-SEC-001-artifact-leak-scan" },
|
|
],
|
|
deferred_external_tasks: release.deferredExternalTasks,
|
|
release_sha256: finalEvidence.release_sha256,
|
|
run_id: runId,
|
|
status: "passed",
|
|
zip_sha256: finalEvidence.zip_sha256,
|
|
schema_version: "1.0",
|
|
});
|
|
|
|
console.log(JSON.stringify({
|
|
deferred_external_tasks: release.deferredExternalTasks,
|
|
release_sha256: finalEvidence.release_sha256,
|
|
run_id: runId,
|
|
status: "passed",
|
|
zip_sha256: finalEvidence.zip_sha256,
|
|
}, null, 2));
|