import { spawnSync } from "node:child_process"; import { access, mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; const gateId = process.argv .slice(2) .find((argument) => /^FE-GATE-\d{3}$/.test(argument)); const document = /** @type {{ * gates: Record, * logPath: string, * evidence: string[], * retentionClass: string, * requiresEnvironment?: string[] * }> * }} */ (JSON.parse(await readFile("config/ci/gates.json", "utf8"))); const gate = gateId ? document.gates[gateId] : undefined; if (!gateId || !gate) { process.stderr.write("Usage: ci:gate -- FE-GATE-001..FE-GATE-026\n"); process.exit(2); } const output = []; let passed = true; for (const variable of gate.requiresEnvironment ?? []) { if (!process.env[variable]) { output.push(`missing required environment: ${variable}`); passed = false; } } if (passed) { for (const step of gate.steps) { const result = spawnSync( "corepack", ["pnpm", step.script, ...(step.args ?? [])], { encoding: "utf8", env: process.env }, ); output.push( `$ corepack pnpm ${step.script} ${(step.args ?? []).join(" ")}`.trim(), result.stdout, result.stderr, ); const exitedSuccessfully = result.status === 0; const expectationMet = step.expect === "pass" ? exitedSuccessfully : !exitedSuccessfully; if (!expectationMet) { output.push( `expectation failed: expected ${step.expect}, exit=${result.status}`, ); passed = false; break; } } } await mkdir(path.dirname(gate.logPath), { recursive: true }); await writeFile(gate.logPath, `${output.filter(Boolean).join("\n")}\n`); if (passed) { for (const evidencePath of gate.evidence) { try { await access(evidencePath); } catch { output.push(`missing evidence: ${evidencePath}`); passed = false; } } if (!passed) { await writeFile(gate.logPath, `${output.filter(Boolean).join("\n")}\n`); } } if (!passed) { process.stderr.write(`${gateId} ${gate.name}: FAIL\n`); process.exit(1); } process.stdout.write( `${gateId} ${gate.name}: PASS (${gate.retentionClass})\n`, );