From 330eb8e8e7969be99c5468ef44975a961564e708 Mon Sep 17 00:00:00 2001 From: stxkxs <139715017+stxkxs@users.noreply.github.com> Date: Fri, 7 Aug 2026 22:41:17 -0700 Subject: [PATCH] feat: one required status check that fails closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Branch protection is about to go on across the org, and it needs a check name that is stable and that actually gates. Naming the real jobs gives neither. Names are not stable. Matrix legs are named after their inputs, so a required context can stop reporting because a component was deleted — and a required context that never reports leaves every pull request Pending forever, from a change that looks unrelated to CI. Naming jobs also cannot keep up: a job added to a workflow is not automatically required, so the branch-protection list is a hand-kept copy of the CI graph that drifts in the direction of less coverage. This adds one gate job per PR-triggered workflow, depending on every other job in it, delegating to the shared action in nanohype/.github. That single name becomes the only required check. Two details carry the whole thing: `if: always()` — without it a failed dependency SKIPS the gate, and GitHub counts a skipped check as passing for branch protection, so the gate would report green in exactly the case it exists to catch. Restricted to pull_request — the gate treats a skipped dependency as a failure, and a job carrying `if: github.event_name == 'pull_request'` is legitimately skipped on a push to main. The gate exists to gate merges, and merges come from pull requests. The shared action refuses to pass on a dependency that is not success (skipped and cancelled included), on an empty needs list, and on any job in the workflow that the gate does not watch. That last check is what keeps the hand-written needs list honest as jobs are added. No repository settings change here. The gate blocks nothing until it is named as a required check. --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da3fb4e..b7214cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -558,3 +558,37 @@ jobs: repo: context.repo.repo, body: body }); + + merge-gate: + name: merge gate + runs-on: ubuntu-latest + needs: + [ + placeholders, + lint, + dashboards, + serviceaccount-bindings, + kyverno, + fork-safety, + helm-render, + policy-admission, + appsets, + appset-render, + secrets, + validate, + pr-summary, + ] + # always() is load-bearing: without it a failed dependency SKIPS this job, + # and GitHub counts a skipped check as passing for branch protection, so the + # gate would report green exactly when something broke. + # + # Restricted to pull_request because the gate treats a skipped dependency as + # a failure, and a job carrying `if: github.event_name == 'pull_request'` is + # legitimately skipped on a push to main. The gate exists to gate merges, and + # merges come from pull requests; on push it is skipped and gates nothing. + if: always() && github.event_name == 'pull_request' + steps: + - uses: nanohype/.github/actions/merge-gate@6ec6c5b3e6c4a8b15e12da4afd7ac4870a630092 # main + with: + needs: ${{ toJSON(needs) }} + gate-job-id: merge-gate