Skip to content

fix(release): gate releases only on the allowlisted required checks - #1259

Merged
seonghobae merged 4 commits into
mainfrom
fix/release-gate-required-checks-only
Sep 27, 2026
Merged

seonghobae merged 4 commits into
mainfrom
fix/release-gate-required-checks-only

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Problem

scripts/ci/release_checks_gate.sh decides whether a commit can be released, and two versions of it have been wrong on protected main:

  • Original gate: every check-run on the commit had to be terminal and non-failing. The hourly opencode-hourly-loop.yml and provider-catalog-sync.yml runs attach check-runs to main's tip, so a failed or in-flight maintenance run could block a release.
  • First revision of this PR: every check-run carrying a required name had to be success. security.yml's weekly schedule runs also execute on main's tip, and their jobs are if: github.event_name != 'schedule', so they leave skipped (or failed) check-runs under the same required names. main @ 5665b0ad fails for that reason today (runs 35581051361, 35710548003), and any old failed run blocks a SHA forever.

Change

The gate now certifies a commit from exactly one workflow run:

  • Run selection:
    • It lists actions/runs?head_sha=$TARGET_SHA&event=push and re-filters locally to path == .github/workflows/security.yml, event == push, head_branch == main and head_sha == TARGET_SHA.
    • The newest run wins: highest run id, then highest run_attempt, so a re-run replaces a red attempt.
  • Job evaluation: it reads only that run's jobs with an explicit jobs?filter=latest (each job's most recent attempt, which covers "re-run failed jobs"). It checks them against the explicit allowlist RELEASE_EXPECTED_PUSH_CHECKS: "Tests and package quality", "Property and coverage-guided fuzzing", "Rust workspace gate", "CodeQL, supply chain, and SBOM".
  • Fail closed when:
    • no such run exists;
    • the run is not completed;
    • a required job is missing;
    • a required job is not completed + success (skipped and neutral count as failures);
    • the allowlist is empty or malformed.
  • Ignored: everything else on the commit — scheduled security.yml runs, the hourly maintenance workflows, and this release run itself. This is an allowlist, not a denylist.
  • Removed: the check-runs listing and the brittle details_url contains /actions/runs/<GITHUB_RUN_ID>/ self-exclusion.
  • release.yml: the publish job gains read-only actions: read, needed for the recheck. verify already had it. Two comments were updated. There are no other workflow changes.

main's branch rules (read through the public API) contain no required-status-checks rule, only org-required PR workflows. The allowlist therefore stays the pinned security.yml job list, which a test keeps equal to security.yml's job names. ADR 0129 rejected a live ruleset lookup because it needs administration: read.

Tests

  • New tests/test_release_checks_required_allowlist.py runs the real script against a stubbed gh that serves the Actions runs/jobs API. Covered scenarios:
    • a schedule run with skipped and failed jobs next to a green push run: passes;
    • a red push run: fails;
    • a re-run with a higher run_attempt wins, including the case where the newest attempt is red;
    • a newer push run supersedes an older red one;
    • missing run: fails, parametrised over no runs, schedule-only, dispatch-only, other workflow, hourly loop, other branch, other SHA;
    • an in-progress, queued, waiting, requested or pending run: fails;
    • a missing required job: fails;
    • each non-success conclusion or status of a required job: fails;
    • non-required jobs in the run and hourly maintenance runs: ignored;
    • a malformed allowlist: fails;
    • static checks: pinned endpoints and path, filter=latest, no check-runs, no details_url, no GITHUB_RUN_ID, both release jobs have actions: read, and no other workflow reuses a required job name.
  • Existing gate tests (test_release_workflow_contract.py, test_release_checks_required_success_contract.py) now serve the runs/jobs API. test_release_workflow_idempotency_contract.py now allows job-scoped actions: read on publish and still forbids it workflow-wide and forbids actions: write.
  • Release test set: 177 passed. actionlint 1.7.7 with shellcheck: clean.
  • Full suite on this branch: the same 154 failures plus 1 collection error that pristine main @ 5665b0ad also has (main is red; fix(ci): repair protected-main security and runtime regressions #1209 addresses it). No new failures.

Merge order

Merge this before #1258. #1258 will then resolve its one expected conflict, in docs/RELEASING.md (precondition 4). release.yml merges cleanly.

scripts/ci/release_checks_gate.sh required every check-run on the release
commit to be terminal and non-failing, so the hourly opencode-hourly-loop and
provider-catalog-sync runs attached to main's tip could block a release.
Evaluate only the explicit RELEASE_EXPECTED_PUSH_CHECKS allowlist (the four
required security.yml jobs); keep failing closed when a required check is
missing, pending, not success, or the allowlist is empty or malformed.
@seonghobae seonghobae added the release: required Needed before contextual-orchestrator can cut a normal release label Sep 26, 2026 — with Cursor
@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 26 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 44b9d7a5-2061-4297-995f-aa0c9da0d789

📥 Commits

Reviewing files that changed from the base of the PR and between 6385faa and 7b2ac4d.

📒 Files selected for processing (8)
  • .github/workflows/release.yml
  • CHANGELOG.d/release-gate-required-checks-only.md
  • docs/RELEASING.md
  • scripts/ci/release_checks_gate.sh
  • tests/test_release_checks_required_allowlist.py
  • tests/test_release_checks_required_success_contract.py
  • tests/test_release_workflow_contract.py
  • tests/test_release_workflow_idempotency_contract.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Review follow-up on the release checks gate. Judging every check-run with a
required name let security.yml's scheduled runs (jobs skipped on schedule)
or any old failed run block a commit forever. Select the newest push-event
.github/workflows/security.yml run on main for TARGET_SHA (highest run id,
then run_attempt) and evaluate only its jobs (explicit filter=latest)
against the RELEASE_EXPECTED_PUSH_CHECKS allowlist; fail closed when the run
is missing or unfinished or a required job is missing or not success. Drop
the details_url self-exclusion and the check-runs listing. publish gains
read-only actions: read for the recheck.

Copy link
Copy Markdown
Contributor Author

Admission correction — exact head de62e03ca4e7b68b5fa50925dc07c5bedff4abd0

Current-head Security and Quality run 36223456594 is terminally failed. Exact logs show test collection cannot import _DEFAULT_EMBEDDING_CLAIM_LEASE_SECONDS, pinned Rust 1.97.1 lacks rustfmt, and hash-required installation rejects the VCS-sourced fast-mlsirm requirement. The test-signal owner is contextual-orchestrator#1266@86d4e83a82bd15be6d67a05fa495362d03a39789; the remaining failures are explicit protected-main infrastructure prerequisites. This PR is returned to Draft/Proposed with its exact release-gate delta preserved; no leaf workaround or gate weakening is appropriate.

@seonghobae
seonghobae marked this pull request as ready for review September 27, 2026 11:15
@seonghobae
seonghobae merged commit fd45068 into main Sep 27, 2026
21 checks passed
@seonghobae
seonghobae deleted the fix/release-gate-required-checks-only branch September 27, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release: required Needed before contextual-orchestrator can cut a normal release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant