Skip to content
13 changes: 12 additions & 1 deletion scripts/ci-main-tree-proof.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ function assert(condition, message) {
}

async function selfTest() {
if (process.env.CI_TREE_PROOF_SELF_TEST_FORCE_FAIL === "1") assert(false, "forced failure (test hook)");
const ok = (name, extra = {}) => ({ name, status: "completed", conclusion: "success", ...extra });
const full = [
{ ...ok("Change scope"), id: 501 },
Expand Down Expand Up @@ -775,7 +776,17 @@ function recordTestedTree({ env = process.env, runGit = git } = {}) {
}

async function main() {
if (process.argv.includes("--self-test")) return selfTest();
if (process.argv.includes("--self-test")) {
// A self-test failure must fail the process. The fail-open handler below exists for the CI
// proof path only; applied here it would print a warning and exit 0, hiding broken guards.
try {
await selfTest();
} catch (error) {
console.error(`ci-main-tree-proof self-test FAILED: ${String(error?.stack ?? error)}`);
process.exitCode = 1;
}
return;
}
if (process.argv.includes("--record-tested-tree")) {
try {
recordTestedTree();
Expand Down
28 changes: 28 additions & 0 deletions tests/ci-main-tree-proof.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ describe("main tree proof script", () => {
expect(result.stdout).toContain("ci-main-tree-proof self-test passed.");
});

it("exits non-zero when a self-test assertion fails, writing no GitHub outputs", () => {
const outDir = mkdtempSync(path.join(tmpdir(), "tree-proof-self-test-"));
const outputFile = path.join(outDir, "output");
const summaryFile = path.join(outDir, "summary");
writeFileSync(outputFile, "");
writeFileSync(summaryFile, "");
try {
const result = spawnSync(process.execPath, ["scripts/ci-main-tree-proof.mjs", "--self-test"], {
encoding: "utf8",
env: {
...process.env,
CI_TREE_PROOF_SELF_TEST_FORCE_FAIL: "1",
GITHUB_OUTPUT: outputFile,
GITHUB_STEP_SUMMARY: summaryFile,
},
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
expect(result.status).toBe(1);
expect(result.stderr).toContain("self-test FAILED");
expect(result.stderr).toMatch(/forced failure \(test hook\)[\r\n]\s+at /);
expect(result.stdout).not.toContain("self-test passed");
expect(result.stdout).not.toContain("coverage=");
expect(readFileSync(outputFile, "utf8")).toBe("");
expect(readFileSync(summaryFile, "utf8")).toBe("");
} finally {
rmSync(outDir, { recursive: true, force: true, maxRetries: 5 });
}
});

it("names exactly the PR-run jobs each group skips, matching ci.yml job names", () => {
const names = Object.values(jobs).map((job) => String((job as { name?: string }).name ?? ""));
for (const name of [
Expand Down
Loading