Skip to content

Commit dff4433

Browse files
chore(ci): pin ADR maintainer approval in required-context registry (#7205)
Part of #7022. The maintainer added `ADR maintainer approval` (.github/workflows/adr-merge-approval.yml) to the main ruleset's required status checks (confirmed 2026-08-10 ~02:3xZ). This registers the job name in scripts/check-required-contexts.mjs's REQUIRED_CONTEXTS so a future rename or drift is caught by the pin's own self-test instead of silently detaching the gate again. Also extends the self-test: adds adr-merge-approval.yml to the sources map, adds a rename-ablation fixture for the new entry, and narrows two pre-existing generic scanWorkflows fixtures (unparseable / no-jobs) to an explicit two-file registry so they stay independent of how many files REQUIRED_CONTEXTS grows to register. Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn Co-authored-by: Claude <noreply@anthropic.com>
1 parent f40c5b4 commit dff4433

1 file changed

Lines changed: 37 additions & 4 deletions

File tree

scripts/check-required-contexts.mjs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ export const REQUIRED_CONTEXTS = [
188188
authorized: '#5617 closing ruling 2026-08-09, second batch',
189189
carries: 'the live-server datetime conformance axis (#3912/#3942)',
190190
},
191+
{
192+
workflow: 'adr-merge-approval.yml',
193+
job: 'adr-merge-approval',
194+
context: 'ADR maintainer approval',
195+
authorized: '#7022 maintainer settings action, confirmed to the devx PM seat 2026-08-10 ~02:3xZ (screenshot of the `main` ruleset)',
196+
carries: 'the #6741 ruling that only the maintainer own-account approval may land a docs/adr/** merge (#6942/#6962 landed unapproved while this context sat outside the required set)',
197+
},
191198
];
192199

193200
/** Repository root, resolved from this file rather than from the cwd. */
@@ -426,6 +433,7 @@ async function selfTest() {
426433
const sources = {
427434
'lint.yml': readFileSync(join(root, '.github', 'workflows', 'lint.yml'), 'utf8'),
428435
'ci.yml': readFileSync(join(root, '.github', 'workflows', 'ci.yml'), 'utf8'),
436+
'adr-merge-approval.yml': readFileSync(join(root, '.github', 'workflows', 'adr-merge-approval.yml'), 'utf8'),
429437
};
430438

431439
/** Judge the real workflows with one file's text replaced by `source`. */
@@ -481,6 +489,20 @@ async function selfTest() {
481489
'dropping the "(live PG + MySQL)" suffix ⇒ red — the parenthetical is part of the contract, not decoration',
482490
);
483491

492+
// The #7022 addition: a third workflow file, registered for the first time.
493+
// Same reverse-verification shape as the ESLint/Build Core renames above —
494+
// a pin that has never been exercised for its own entry is exactly the
495+
// "registered but nothing checks it" gap this script exists to close.
496+
const renamedAdrApproval = fixture('rename ADR maintainer approval', 'adr-merge-approval.yml', (s) =>
497+
s.replace(' name: ADR maintainer approval\n', ' name: ADR Merge Approval\n'),
498+
);
499+
assert(
500+
renamedAdrApproval.problems.some(
501+
(p) => p.includes("job 'adr-merge-approval'") && p.includes('"ADR Merge Approval"') && p.includes("'ADR maintainer approval'"),
502+
),
503+
'renaming adr-merge-approval.yml\'s job ⇒ red, naming the job, the new name and the required context (#7022)',
504+
);
505+
484506
// ── (2) the job disappearing entirely ─────────────────────────────────────
485507
const droppedJob = fixture('drop the console-pin job', 'ci.yml', (s) => s.replace('\n console-pin:\n', '\n console-pin-disabled:\n'));
486508
assert(
@@ -565,19 +587,30 @@ async function selfTest() {
565587
judge({ registry: REQUIRED_CONTEXTS, workflows: new Map() }).problems.some((p) => p.includes('was never read')),
566588
'a workflow that was never read ⇒ red',
567589
);
590+
// These three generic-scanner assertions deliberately use a fixed two-file
591+
// registry rather than the real REQUIRED_CONTEXTS: they exercise
592+
// scanWorkflows' file-level handling (missing / no-jobs / unparseable), not
593+
// any particular entry, so pinning them to two arbitrary files keeps them
594+
// stable as the registry grows (#7022 learned this the hard way — the first
595+
// draft used REQUIRED_CONTEXTS directly and broke the moment a third
596+
// workflow file was registered, for a reason unrelated to what it tests).
597+
const twoFileRegistry = [
598+
{ workflow: 'lint.yml', job: 'placeholder', context: 'Placeholder Lint' },
599+
{ workflow: 'ci.yml', job: 'placeholder', context: 'Placeholder CI' },
600+
];
568601
assert(
569-
judge({ registry: REQUIRED_CONTEXTS, workflows: new Map([['lint.yml', { error: 'boom' }], ['ci.yml', { error: 'boom' }]]) }).problems.every((p) => p.includes('boom')),
602+
judge({ registry: twoFileRegistry, workflows: new Map([['lint.yml', { error: 'boom' }], ['ci.yml', { error: 'boom' }]]) }).problems.every((p) => p.includes('boom')),
570603
'an unparseable workflow ⇒ red',
571604
);
572605
const empty = mkdtempSync(join(tmpdir(), 'required-contexts-'));
573606
try {
574-
assert((await scanWorkflows(empty)).problems.some((p) => p.includes('does not exist')), 'a missing workflow file ⇒ red, never a pass');
607+
assert((await scanWorkflows(empty, twoFileRegistry)).problems.some((p) => p.includes('does not exist')), 'a missing workflow file ⇒ red, never a pass');
575608
mkdirSync(join(empty, '.github', 'workflows'), { recursive: true });
576609
writeFileSync(join(empty, '.github', 'workflows', 'lint.yml'), 'name: Lint\non: push\n');
577610
writeFileSync(join(empty, '.github', 'workflows', 'ci.yml'), 'name: CI\non: push\n');
578-
assert((await scanWorkflows(empty)).problems.every((p) => p.includes('no jobs')), 'a workflow with no jobs: map ⇒ red');
611+
assert((await scanWorkflows(empty, twoFileRegistry)).problems.every((p) => p.includes('no jobs')), 'a workflow with no jobs: map ⇒ red');
579612
writeFileSync(join(empty, '.github', 'workflows', 'lint.yml'), 'jobs: [oops\n - :\n');
580-
assert((await scanWorkflows(empty)).problems.some((p) => p.includes('could not be read as YAML')), 'an unparseable workflow ⇒ red');
613+
assert((await scanWorkflows(empty, twoFileRegistry)).problems.some((p) => p.includes('could not be read as YAML')), 'an unparseable workflow ⇒ red');
581614
} finally {
582615
rmSync(empty, { recursive: true, force: true });
583616
}

0 commit comments

Comments
 (0)