fix(web): Cloudflare UA block, Gitea path@ref, attempt 0

Three bugs the real API surfaced, none visible against a stubbed payload:

- Gitea here is proxied by Cloudflare, which answers urllib's default
  `Python-urllib/3.x` agent with 403 + "error code: 1010" before the request
  reaches Gitea — every endpoint 403s, including unauthenticated `/version`,
  so it reads exactly like a token problem. Send an explicit User-Agent.
- Gitea reports `path` as either `.gitea/workflows/lint.yml` or the
  `lint.yml@refs/heads/main` form; splitting on "/" alone rendered the latter
  as the workflow name "main". Drop the ref first.
- Gitea reports `run_attempt: 0` for a never-retried run while the job id the
  workflow builds uses github.run_attempt, which starts at 1 — so 0 must read
  as 1 or no VM would ever correlate to its run. (The existing `or 1` already
  did this; it now has a test naming why, against the real payload shape.)

HTTP failures now carry the server's own message (Gitea names the missing
scope; a Cloudflare interception says something else entirely) instead of a
bare status code that sent this investigation the wrong way once already.
This commit is contained in:
2026-07-25 15:27:01 +02:00
parent d0ea7f661d
commit 91ea90348c
3 changed files with 158 additions and 6 deletions
+10 -1
View File
@@ -318,7 +318,16 @@ sudo -u ci-runner /opt/ci/venv/bin/python -m ci_orchestrator creds set \
**Degraded states** never take the card down: no token, an expired token, or
an unreachable Gitea each render a warning notice *and still list every
running VM* below it, so the dashboard is never less useful than the VM table
it replaced.
it replaced. The notice includes whatever the server said (Gitea names the
missing scope in its own body), which is the fastest way to tell a token
problem from something in front of Gitea.
**Gotcha — Cloudflare in front of Gitea.** `gitea.emulab.it` is proxied by
Cloudflare, which rejects urllib's default `Python-urllib/3.x` user agent with
`403` + `error code: 1010` *before the request reaches Gitea* — indistinguishable
from a token refusal if you only look at the status code. `operations/executions.py`
therefore always sends an explicit `User-Agent`; any ordinary agent string
passes. Do not remove that header.
**Orphaned VMs are not handled in this card.** A powered-on VM whose run is no
longer in the window (or that no run ever claimed) keeps a row of its own