From 24d2513368e28b47b305e84889507a83ec6f26ed Mon Sep 17 00:00:00 2001 From: Kyle Tse Date: Fri, 30 Oct 2026 10:25:22 +0000 Subject: [PATCH] fix(ci): require named jobs to succeed --- .github/workflows/dart.yml | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 7f49308..5ee6b11 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -37,19 +37,30 @@ jobs: - name: Upload coverage to codecov run: curl -s https://codecov.io/bash | bash - # Aggregate gate: the merge queue requires only this check. Skipped jobs pass; - # a failed or cancelled gating job fails it. + # Aggregate gate: every required job must succeed before this check passes. ci-ok: - if: always() + if: ${{ always() }} needs: [build] runs-on: sylphx-linux-standard timeout-minutes: 5 steps: - - name: All gating jobs passed + - name: Require every selected job to succeed env: - RESULTS: ${{ join(needs.*.result, ' ') }} + NEEDS: ${{ toJSON(needs) }} + REQUIRED: build run: | - echo "gating job results: $RESULTS" - for r in $RESULTS; do - case "$r" in failure|cancelled) exit 1 ;; esac - done + python3 - <<'PY' + import json, os, sys + needs = json.loads(os.environ["NEEDS"]) + required = {job.strip() for job in os.environ["REQUIRED"].split(",") if job.strip()} + if not isinstance(needs, dict) or not needs or not required: + sys.exit("ci-ok: needs and required jobs must be nonempty") + failed = {job: "absent" for job in required - needs.keys()} + for job, value in sorted(needs.items()): + result = value.get("result") if isinstance(value, dict) else None + print(f"{job}: {result}") + if result not in ("success", "skipped") or (job in required and result != "success"): + failed[job] = result or "absent" + if failed: + sys.exit(f"ci-ok: required jobs did not succeed: {failed}") + PY