diff --git a/.gitea/scripts/ci-required-drift.py b/.gitea/scripts/ci-required-drift.py index 9d4e60c8a..5bf56abce 100755 --- a/.gitea/scripts/ci-required-drift.py +++ b/.gitea/scripts/ci-required-drift.py @@ -13,8 +13,9 @@ Sources: Three failure classes: F1 Job in (A) is not under the sentinel's `needs:` โ€” sentinel doesn't gate it, so a red job on that name can sneak through. - Ignores jobs whose `if:` references `github.event_name` (those - run only on specific events and may be `skipped` legitimately). + Ignores jobs whose `if:` references `github.event_name` or + `github.ref` (those run only on specific events or refs and may + be `skipped` legitimately). F2 Context in (B) corresponds to no emitter โ€” i.e. there's no job in ci.yml whose runtime status-name maps to that context. A stale required-check name is silent: protection demands a @@ -203,12 +204,17 @@ def ci_jobs_all(ci_doc: dict) -> set[str]: def ci_job_names(ci_doc: dict) -> set[str]: """Set of job keys in ci.yml MINUS the sentinel itself MINUS jobs - whose `if:` gates on `github.event_name` (those are event-scoped - and can legitimately be `skipped` for a given trigger; if we - required them under the sentinel `needs:`, every PR-only job + whose `if:` gates on `github.event_name` or `github.ref` (those are + event-scoped and can legitimately be `skipped` for a given trigger; + if we required them under the sentinel `needs:`, every PR-only job would be `skipped` on push and the sentinel would interpret `skipped != success` as failure). RFC ยง4 spec. + `github.ref` is the companion gate for jobs that run only on direct + pushes to specific branches (e.g. `github.ref == 'refs/heads/main'`). + These never execute in a PR context, so flagging them as missing + from `all-required.needs:` is a false positive (mc#958 / mc#959). + Used for F1 (jobs missing from sentinel needs). NOT used for F1b (typos in needs) โ€” see `ci_jobs_all` for that.""" jobs = ci_doc.get("jobs") @@ -221,7 +227,9 @@ def ci_job_names(ci_doc: dict) -> set[str]: continue if isinstance(v, dict): gate = v.get("if") - if isinstance(gate, str) and "github.event_name" in gate: + if isinstance(gate, str) and ( + "github.event_name" in gate or "github.ref" in gate + ): continue names.add(k) return names