chore(#3833): gate every PR compute lane behind a mergeability preflight (#3843)

* test(#3833): failing-first suite for the PR mergeability preflight

* chore(#3833): gate every PR compute lane behind a mergeability preflight

* fix(#3833): assert the preflight gate on parsed yaml and guard status-function if

* fix(#3833): run stub-backed cli tests in-process to avoid a spawnsync deadlock

* fix(#3833): fail the preflight open when its script is absent at the base sha

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-24 21:13:55 -04:00
committed by GitHub
parent 63abcface9
commit 6905726d9c
15 changed files with 1655 additions and 26 deletions

View File

@@ -39,6 +39,10 @@ const PROTECTED_WORKFLOWS = new Set([
'mutation.yml',
'security-scan.yml',
'release.yml',
// #3833: the reusable gate every `pull_request` compute lane depends on —
// marking it inert would let a change to the gate itself ship without ever
// running the full matrix it is responsible for enforcing.
'pr-mergeable-preflight.yml',
]);
for (const wf of PROTECTED_WORKFLOWS) {
if (INERT_WORKFLOWS.has(wf)) {
@@ -341,19 +345,48 @@ function missingRuleTestFiles(rules) {
return [...referenced].filter(f => !existsSync(path.join(__dirname, '..', f))).sort();
}
// Fail loudly at module load, mirroring the PROTECTED_WORKFLOWS check above —
// this fires on EVERY invocation of the CLI (including the real `changes` job
// in .github/workflows/test.yml), not only when a test suite happens to run.
{
const missing = missingRuleTestFiles(RULES);
if (missing.length > 0) {
throw new Error(
`ci-test-scope: RULES reference test file(s) that do not exist on disk ` +
`(silent coverage hole — see #2758):\n ${missing.join('\n ')}`,
);
}
/**
* Every PROTECTED_WORKFLOWS member that does not exist on disk.
* The name list and the real filenames are two surfaces over one fact
* (#3833): without this, renaming or deleting a gating workflow silently
* un-protects it and CI stays green while the protection is gone. Same
* shape and rationale as missingRuleTestFiles() above.
*/
function missingProtectedWorkflows(names = PROTECTED_WORKFLOWS) {
return [...names]
.filter(name => !existsSync(path.join(__dirname, '..', '.github', 'workflows', name)))
.sort();
}
/**
* Shared module-load assertion for the two "this list names something that
* does not exist on disk" guards above. Both are silent-coverage-hole guards:
* a name that no longer resolves stops selecting/protecting anything while CI
* stays green, so both must fail loudly at load rather than at test time.
*/
function assertNoneMissing(missing, summary, issueRef) {
if (missing.length === 0) return;
throw new Error(`ci-test-scope: ${summary} (${issueRef}):\n ${missing.join('\n ')}`);
}
// Fail loudly at module load — this fires on EVERY invocation of the CLI
// (including the real `changes` job in .github/workflows/test.yml), not only
// when a test suite happens to run.
assertNoneMissing(
missingRuleTestFiles(RULES),
'RULES reference test file(s) that do not exist on disk (silent coverage hole)',
'see #2758',
);
// A PROTECTED_WORKFLOWS entry naming a file that does not exist means the
// workflow was renamed or deleted without updating this list, silently
// un-protecting it while CI stays green.
assertNoneMissing(
missingProtectedWorkflows(PROTECTED_WORKFLOWS),
'PROTECTED_WORKFLOWS reference workflow file(s) that do not exist on disk (silent protection hole)',
'see #3833',
);
function usage() {
return [
'Usage:',
@@ -590,4 +623,4 @@ if (require.main === module) {
runMain(main);
}
module.exports = { RULES, missingRuleTestFiles };
module.exports = { RULES, missingRuleTestFiles, PROTECTED_WORKFLOWS, INERT_WORKFLOWS, missingProtectedWorkflows };