From a7bfe6d210bdfb7b7c9531e0fc0e135ac6b6095f Mon Sep 17 00:00:00 2001 From: Maximiliano <40447063+msalvatti@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:03:58 -0300 Subject: [PATCH 1/2] ci: add the CI passed aggregate the org rule can require MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A required status check is matched by literal name, and no two repos here name their jobs alike — from one job in bymax-bio-web to sixteen in rust-auth. There is no list of contexts that works org-wide, which is why required checks never moved above the repository level: only 12 of 38 repos require CI at all, each with its own vocabulary. One aggregate per repo, always spelled `CI passed`, is what a single organisation ruleset can require. It also means a repository created from now on inherits the gate without anyone configuring it. always() is what makes it a gate: without it the job is skipped as soon as a dependency fails, and a skipped check reports neutral — the pull request would look unblocked exactly when it is broken. cancelled is named for the same reason, and is not hypothetical: the Actions incident of 2026-08-06 produced nothing but cancelled jobs for hours. --- .github/workflows/ci.yml | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 917b047..2fb1e68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -577,3 +577,46 @@ jobs: examples/e2e/playwright-report/ examples/e2e/test-results/ retention-days: 7 + + # Single check the org ruleset can require, in every repo, under one name. + # + # A required status check is matched by literal name, and this repo's jobs are + # named nothing like the next repo's — so there is no list of contexts that + # works org-wide. One aggregate per repo, always spelled `CI passed`, is what + # makes a single rule at the organisation level possible, and what makes every + # repository created from now on inherit the gate without being configured. + # + # `always()` is what makes this a gate rather than decoration: without it the + # job is skipped the moment a dependency fails, and a skipped check reports + # neutral — the pull request would look unblocked precisely when it is broken. + # `cancelled` is named explicitly for the same reason; a cancelled job is not + # a passing one, and a queue timeout produces exactly that. + # + # A skipped dependency is accepted on purpose: jobs here are conditional on the + # event, and failing on skip would break every push that legitimately runs a + # subset. + ci-pass: + name: CI passed + if: always() + needs: + - core + - feature-matrix + - doc + - ts-rs-drift + - wasm-purity + - wasm-binding + - wasm-pack + - supply-chain + - invariants + - versions + - public-api + - fuzz-smoke + - npm + - examples + - dogfood + - e2e-browser + runs-on: ubuntu-latest + steps: + - name: Fail when any dependency did not succeed + if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: exit 1 From 460522b6d4ea4c4e26bfca936faa6522c6014f38 Mon Sep 17 00:00:00 2001 From: Maximiliano <40447063+msalvatti@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:21:14 -0300 Subject: [PATCH 2/2] ci: address Copilot review on the aggregate gate - timeout-minutes: once this is the required check, a hung runner holds every merge behind it for GitHub's default of six hours. - Report every dependency's result, on success as well as failure. The gate reported a bare red X and left the reader opening each job to find which one broke. - Rename the step to what the condition does. skipped is deliberately not a failure here, so "did not succeed" described a stricter gate. --- .github/workflows/ci.yml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fb1e68..43eea79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -616,7 +616,25 @@ jobs: - dogfood - e2e-browser runs-on: ubuntu-latest + # Once this is the required check, a hung runner holds every merge behind it + # for GitHub's default of six hours. The job is one comparison, so a short + # ceiling costs nothing and bounds that. + timeout-minutes: 5 steps: - - name: Fail when any dependency did not succeed + # Printed on success as well as failure. Without it the check reports a bare + # red X and the reader has to open each dependency to find which one broke. + - name: Report the result of every dependency + env: + NEEDS: ${{ toJSON(needs) }} + run: | + { + echo "| Job | Result |" + echo "| --- | --- |" + echo "$NEEDS" | jq -r 'to_entries[] | "| \(.key) | \(.value.result) |"' | sort + } | tee -a "$GITHUB_STEP_SUMMARY" + + # Named for what the condition does: `skipped` is deliberately not a failure + # here, so "did not succeed" would describe a stricter gate than this is. + - name: Fail when any dependency failed or was cancelled if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') run: exit 1