import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const reportRoot = path.join(repoRoot, "release/ci-reports"); const requested = process.argv.slice(2); const paths = requested.length > 0 ? requested.map((value) => path.isAbsolute(value) ? value : path.resolve(repoRoot, value)) : ["quick", "chromium", "release"].map((lane) => path.join(reportRoot, `${lane}.json`)).filter((file) => fs.existsSync(file)); assert.ok(paths.length > 0, "no CI lane reports were provided or found"); const sha256 = (value) => crypto.createHash("sha256").update(value).digest("hex"); const commonBindings = ["packageJson", "lockfile", "ledger", "engineManifest", "releaseNotes", "knownLimitations", "releaseRecovery", "sbom"]; const releaseBindings = ["binaryArchive", "sourceArchive", "sha256Sums", "rcManifest"]; const expectedRecordIds = { quick: ["lockfile-install", "typecheck", "lint", "unit", "status", "evidence-schema", "rc-docs"], chromium: ["lockfile-install", "p0-user-loop", "offline-browser-smoke", "network-interruption", "device-loss", "oom-recovery", "opfs-quota", "malicious-blend", "malicious-archive"], release: [ "lockfile-install", "full-e2e", "performance-100k-1m", "performance-10m", "performance-texture-4k", "performance-texture-8k", "performance-simulation", "performance-long-media", "vdb-native", "vdb-server", "vdb-opfs", "vdb-webgpu", "vdb-viewport", "vdb-faults", "v1-acceptance", "offline-reproducibility", "archive-browser", "binary-archive", "source-archive", "deployment-runbook", "upgrade-runbook", "rollback-runbook", "operations-diagnostics", "operations-rehearsal", "rc-manifest", ], }; const reports = []; for (const reportPath of paths) { const bytes = fs.readFileSync(reportPath); const report = JSON.parse(bytes); assert.equal(report.schemaVersion, 1, `${reportPath} schemaVersion drifted`); assert.ok(["quick", "chromium", "release"].includes(report.lane), `${reportPath} lane is invalid`); assert.equal(report.status, "READY", `${reportPath} is not READY`); assert.match(report.commit, /^[a-f0-9]{40}$/); assert.ok(!Number.isNaN(Date.parse(report.generatedAt)), `${reportPath} generatedAt is invalid`); assert.equal(report.retentionDays, report.lane === "quick" ? 7 : report.lane === "chromium" ? 14 : 30); for (const name of ["os", "node", "npm", "chrome", "blender", "emscripten"]) assert.equal(typeof report.environment[name], "string", `${reportPath} omits environment.${name}`); for (const name of [...commonBindings, ...releaseBindings]) { const binding = report.bindings[name]; assert.ok(binding && typeof binding.path === "string", `${reportPath} omits binding ${name}`); if (binding.sha256 !== null) { assert.match(binding.sha256, /^[a-f0-9]{64}$/); const file = path.join(repoRoot, binding.path); assert.equal(sha256(fs.readFileSync(file)), binding.sha256, `${reportPath} binding drifted: ${name}`); } } if (report.lane === "release") { for (const name of releaseBindings) assert.match(report.bindings[name].sha256, /^[a-f0-9]{64}$/); } else { for (const name of releaseBindings) assert.equal(report.bindings[name].sha256, null); } assert.ok(Array.isArray(report.records) && report.records.length > 0, `${reportPath} has no command records`); assert.deepEqual(report.records.map((record) => record.id), expectedRecordIds[report.lane], `${reportPath} command coverage drifted`); for (const record of report.records) { assert.equal(record.exitCode, 0, `${reportPath} command failed: ${record.id}`); assert.ok(Number.isSafeInteger(record.durationMs) && record.durationMs >= 0); assert.match(record.output.sha256, /^[a-f0-9]{64}$/); const artifact = path.join(repoRoot, record.output.path); assert.equal(fs.statSync(artifact).size, record.output.bytes, `${record.id} output length drifted`); assert.equal(sha256(fs.readFileSync(artifact)), record.output.sha256, `${record.id} output hash drifted`); } const sidecar = fs.readFileSync(`${reportPath}.sha256`, "utf8").trim().split(/\s+/)[0]; assert.equal(sidecar, sha256(bytes), `${reportPath} sidecar hash drifted`); reports.push(report); } if (reports.length > 1) { assert.equal(new Set(reports.map((report) => report.commit)).size, 1, "CI lane reports do not bind one commit"); for (const binding of commonBindings) { assert.equal(new Set(reports.map((report) => report.bindings[binding].sha256)).size, 1, `CI lane ${binding} bindings differ`); } } if (reports.length === 3) { assert.deepEqual(new Set(reports.map((report) => report.lane)), new Set(["quick", "chromium", "release"])); const releaseReport = reports.find((report) => report.lane === "release"); const rcManifest = JSON.parse(fs.readFileSync(path.join(repoRoot, releaseReport.bindings.rcManifest.path), "utf8")); assert.equal(rcManifest.gitCommit, releaseReport.commit, "RC manifest and CI reports bind different commits"); assert.equal(rcManifest.bindings.packageJsonSha256, releaseReport.bindings.packageJson.sha256); assert.equal(rcManifest.bindings.packageLockSha256, releaseReport.bindings.lockfile.sha256); assert.equal(rcManifest.bindings.ledgerSha256, releaseReport.bindings.ledger.sha256); assert.equal(rcManifest.bindings.engineManifestSha256, releaseReport.bindings.engineManifest.sha256); assert.equal(rcManifest.artifacts.sbom.sha256, releaseReport.bindings.sbom.sha256); assert.equal(rcManifest.artifacts.binaryArchive.sha256, releaseReport.bindings.binaryArchive.sha256); assert.equal(rcManifest.artifacts.sourceArchive.sha256, releaseReport.bindings.sourceArchive.sha256); assert.equal(rcManifest.bindings.sha256SumsSha256, releaseReport.bindings.sha256Sums.sha256); } process.stdout.write(`ci-report-ok lanes=${reports.map((report) => report.lane).join(",")} commit=${reports[0].commit}\n`);