Skip to content

Commit 84265e5

Browse files
ci: fail the contract when a job declares no timeout (#1833)
Co-authored-by: Felix T.J. Dietrich <felixtj.dietrich@gmail.com>
1 parent fcceb5d commit 84265e5

1 file changed

Lines changed: 18 additions & 0 deletions

File tree

scripts/ci-contract.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,24 @@ void describe("CI contract", () => {
15161516
}
15171517
}
15181518
});
1519+
1520+
void test("bounds every job that runs steps of its own", async () => {
1521+
// A job that declares no `timeout-minutes` inherits GitHub's six-hour default, so one hung
1522+
// step holds a runner for a working day and reports nothing until it is killed. A job that
1523+
// only `uses:` a reusable workflow runs no step here: its bound is on the callee's jobs.
1524+
const unbounded: string[] = [];
1525+
for (const [file, source] of await workflowSources()) {
1526+
const jobs = parseDocument(source).get("jobs");
1527+
if (!isMap(jobs)) continue;
1528+
for (const entry of jobs.items) {
1529+
const definition = entry.value;
1530+
if (!isMap(definition) || !isSeq(definition.get("steps"))) continue;
1531+
if (definition.get("timeout-minutes") === undefined)
1532+
unbounded.push(`${file}#${String(entry.key)}`);
1533+
}
1534+
}
1535+
assert.deepEqual(unbounded, [], "these jobs run steps without bounding how long they may run");
1536+
});
15191537
});
15201538

15211539
void test("the task graph keeps its cache posture", async () => {

0 commit comments

Comments
 (0)