From a150369fef5c5d3feedf58c78ba83f11f4fca2ea Mon Sep 17 00:00:00 2001 From: Niek Otten Date: Fri, 24 Jul 2026 13:31:14 +0200 Subject: [PATCH] =?UTF-8?q?ci:=20unstick=20verify-stack=20on=20Gitea=201.2?= =?UTF-8?q?7=20+=20runner=202.0.0=20=E2=80=94=20drop=20status-function=20i?= =?UTF-8?q?f=20on=20a=20needs=20job=20(refs=20#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/ci.yaml | 10 +++++++--- docs/runbooks/gitea-actions-gotchas.md | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index a08a5b4..ea39123 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -144,11 +144,15 @@ jobs: # they never co-schedule now the runner has capacity >1. A concurrent Stryker run + full-stack # bring-up + Playwright browser on one host is what OOMs the e2e (commit d5e5fa2, #126). The # light .NET/frontend jobs have no `needs`, so they still parallelise up to runner capacity. - # `if: !cancelled()` keeps verify-stack running even when the mutation ratchet fails (so we don't - # lose its signal) while still honouring run cancellation from the concurrency group above. + # + # No `if: ${{ !cancelled() }}` here (removed in #134): on Gitea 1.27 + act_runner 2.0.0, a job + # gated by a status-function `if` (always()/cancelled()) on top of `needs` routes through the new + # transitional "Cancelling" state + capability negotiation and never leaves `waiting` — it's never + # dispatched (gitea-actions-gotchas.md §7). Default `if: success()` dispatches normally. Cost: a + # failing mutation ratchet now skips verify-stack instead of running it anyway; the fix-and-re-push + # re-run exercises verify-stack, so we still get the signal. verify-stack: needs: [mutation] - if: ${{ !cancelled() }} runs-on: ubuntu-latest steps: - uses: https://github.com/actions/checkout@v4 diff --git a/docs/runbooks/gitea-actions-gotchas.md b/docs/runbooks/gitea-actions-gotchas.md index 29f97d2..0be41d3 100644 --- a/docs/runbooks/gitea-actions-gotchas.md +++ b/docs/runbooks/gitea-actions-gotchas.md @@ -196,3 +196,28 @@ service name; the notif verify harness also registers the sink callback by IP. abonnement is registered and refuses it (`no-auth-on-callback-url`) unless it returns **401** without the configured `Authorization`. The verify sink (`infra/notification-sink.py`) enforces a bearer token for exactly this reason. + +--- + +## 7. A job with `if: ${{ !cancelled() }}` (or `always()`) + `needs` sticks in "waiting" + +**Symptom** — after upgrading to **Gitea 1.27** + **act_runner 2.0.0**, one job never +starts: the run sits in state `waiting` forever, the job has **no logs** (never +dispatched to a runner), and the other jobs finish normally. `main` stays pending/red. +Seen on the `verify-stack` job (#134). + +**Why** — Gitea 1.27 reworked cancellation/aggregation: a job gated by a +**status-function `if`** (`always()` / `cancelled()` / `!cancelled()`) on top of +`needs` now routes through a new transitional **`Cancelling`** job state plus a +server↔runner **capability negotiation** ("Requires Gitea Runner 2.0.0"). On the +1.27 + 2.0.0 pairing that handshake doesn't resolve for such a job, so it's never +offered to a runner and never leaves `waiting`. Jobs with no `if`/`needs` are +unaffected. (Related upstream: go-gitea/gitea#31074, #27116, #35782.) + +**Fix** — don't gate a `needs` job with a status-function `if`. Use the default +`if: success()` (i.e. omit the `if`). If you need "run even when an upstream job +fails", prefer serialising with a `concurrency` group over `needs` + `always()`. + +**Also** — a run already stuck this way will **not** clear itself; force-cancel it +from the Actions UI (plain cancel can also stall on this version, #35782). Push the +workflow fix to produce a fresh run.