fix(ci): replace gh run list with Gitea commit-status query (#75 class F) #83

Merged
claude-ceo-assistant merged 3 commits from fix/issue75-class-F-gh-run-list-to-statuses into main 2026-05-07 23:44:22 +00:00

Summary

Class F of the post-#66 sweep tracked in #75: replace gh run list --workflow=X --commit=SHA with a Gitea commit-status query. One call site, one PR — Gitea has NO workflow-runs API at all.

Why

gh run list hits /repos/.../actions/runs (GitHub Actions REST endpoint). Gitea exposes ZERO endpoints under /repos/.../actions/runs (verified 2026-05-07 via swagger inspection — only secrets / variables / runner-registration tokens live under /actions/). There is no way to query workflow run state via the Gitea v1 API directly.

However, every Gitea Actions job emits a commit status with context = "<Workflow Name> / <Job Name> (<event>)" (verified 2026-05-07 — /repos/.../commits/{sha}/statuses returned 24 rows on a recent main SHA, all properly contextualised). That surface gives us exactly what we need: each workflow run leg is one status row, the aggregate state encodes the run outcome, and Gitea exposes it under /api/v1/repos/.../commits/{sha}/statuses which IS available.

Files

File Old New
auto-promote-on-e2e.yml lines 172-180 gh run list --workflow e2e-staging-saas.yml --commit $SHA curl /api/v1/repos/.../commits/$SHA/statuses + jq filter on contexts whose name starts with "E2E Staging SaaS (full lifecycle) /"

Mapping (preserves the 4 reachable buckets the original case statement handles)

Gitea status rows Result string Existing case-arm action
0 matched contexts none/none proceed (paths-filtered out)
any pending in_progress/none defer
any error/failure completed/failure abort
all success completed/success proceed

The completed/cancelled arm of the case statement becomes unreachable: Gitea status API doesn't expose a cancelled state (it has success/failure/error/pending/warning). Per-SHA concurrency cancellations now surface as failure and are handled by the failure branch. Documented in-place; arm kept as defense-in-depth.

Test plan

  • YAML syntax validates
  • Live curl against current main SHA returns none/none (E2E was paths-filtered for that change set — expected)
  • Synthetic-input jq tests verify all four mapping buckets:
    • no contexts → none/none
    • one context = pending → in_progress/none
    • success + success → completed/success
    • success + failure → completed/failure
  • CI on this PR (Gitea Actions) — required
  • Trigger an E2E run on main + observe auto-promote-on-e2e correctly gate (post-merge)

Hostile self-review — three weakest spots

  1. Workflow name-prefix matching is brittle: if someone renames the name: field in e2e-staging-saas.yml, the jq prefix "E2E Staging SaaS (full lifecycle) /" silently stops matching → degenerates to none/none → auto-promote proceeds without the gate. Mitigation: comment in the workflow points at the prefix; rename PR will fail check-name-parity gate (#56) anyway because the same name is the gate context name in branch protection.
  2. Loss of cancelled semantic: a legitimate per-SHA-concurrency cancel now reads as failure and aborts. Acceptable on this fleet because cancels are rare (concurrency.cancel-in-progress is false on the relevant workflows) and operator can manually re-dispatch. If we measure cancel frequency > 1/week, revisit by parsing run-step-summary text via a follow-up.
  3. Status pagination cap of 100: a SHA with > 100 status rows would lose tail rows. Currently we see ~24 rows per SHA so we're well under, but if the workflow matrix grows substantially this could miss legs. Bump to ?limit=200 or paginate explicitly when count approaches 100.

Security + version impact

  • Continues to use act_runner's GITHUB_TOKEN (per-run, repo read scope)
  • The /commits/{sha}/statuses endpoint is repo-scoped; no extra permissions needed
  • No new secrets, no permission changes

Closes part of #75

Companion PRs: #80 (class A — gh pr ...), #81 (class D — gh api ...).

🤖 Generated with Claude Code

## Summary Class F of the post-#66 sweep tracked in #75: replace `gh run list --workflow=X --commit=SHA` with a Gitea commit-status query. One call site, one PR — Gitea has NO workflow-runs API at all. ## Why `gh run list` hits `/repos/.../actions/runs` (GitHub Actions REST endpoint). Gitea exposes ZERO endpoints under `/repos/.../actions/runs` (verified 2026-05-07 via swagger inspection — only secrets / variables / runner-registration tokens live under `/actions/`). There is no way to query workflow run state via the Gitea v1 API directly. However, every Gitea Actions job emits a commit status with context = `"<Workflow Name> / <Job Name> (<event>)"` (verified 2026-05-07 — `/repos/.../commits/{sha}/statuses` returned 24 rows on a recent main SHA, all properly contextualised). That surface gives us exactly what we need: each workflow run leg is one status row, the aggregate state encodes the run outcome, and Gitea exposes it under `/api/v1/repos/.../commits/{sha}/statuses` which IS available. ## Files | File | Old | New | |---|---|---| | `auto-promote-on-e2e.yml` lines 172-180 | `gh run list --workflow e2e-staging-saas.yml --commit $SHA` | `curl /api/v1/repos/.../commits/$SHA/statuses` + jq filter on contexts whose name starts with `"E2E Staging SaaS (full lifecycle) /"` | ## Mapping (preserves the 4 reachable buckets the original case statement handles) | Gitea status rows | Result string | Existing case-arm action | |---|---|---| | 0 matched contexts | `none/none` | proceed (paths-filtered out) | | any pending | `in_progress/none` | defer | | any error/failure | `completed/failure` | abort | | all success | `completed/success` | proceed | The `completed/cancelled` arm of the case statement becomes unreachable: Gitea status API doesn't expose a `cancelled` state (it has success/failure/error/pending/warning). Per-SHA concurrency cancellations now surface as `failure` and are handled by the failure branch. Documented in-place; arm kept as defense-in-depth. ## Test plan - [x] YAML syntax validates - [x] Live curl against current main SHA returns `none/none` (E2E was paths-filtered for that change set — expected) - [x] Synthetic-input jq tests verify all four mapping buckets: - no contexts → `none/none` - one context = pending → `in_progress/none` - success + success → `completed/success` - success + failure → `completed/failure` - [ ] CI on this PR (Gitea Actions) — required - [ ] Trigger an E2E run on main + observe auto-promote-on-e2e correctly gate (post-merge) ## Hostile self-review — three weakest spots 1. **Workflow name-prefix matching is brittle**: if someone renames the `name:` field in `e2e-staging-saas.yml`, the jq prefix `"E2E Staging SaaS (full lifecycle) /"` silently stops matching → degenerates to `none/none` → auto-promote proceeds without the gate. Mitigation: comment in the workflow points at the prefix; rename PR will fail check-name-parity gate (#56) anyway because the same name is the gate context name in branch protection. 2. **Loss of `cancelled` semantic**: a legitimate per-SHA-concurrency cancel now reads as `failure` and aborts. Acceptable on this fleet because cancels are rare (concurrency.cancel-in-progress is `false` on the relevant workflows) and operator can manually re-dispatch. If we measure cancel frequency > 1/week, revisit by parsing run-step-summary text via a follow-up. 3. **Status pagination cap of 100**: a SHA with > 100 status rows would lose tail rows. Currently we see ~24 rows per SHA so we're well under, but if the workflow matrix grows substantially this could miss legs. Bump to `?limit=200` or paginate explicitly when count approaches 100. ## Security + version impact - Continues to use act_runner's GITHUB_TOKEN (per-run, repo read scope) - The `/commits/{sha}/statuses` endpoint is repo-scoped; no extra permissions needed - No new secrets, no permission changes ## Closes part of #75 Companion PRs: #80 (class A — `gh pr ...`), #81 (class D — `gh api ...`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-ceo-assistant added 1 commit 2026-05-07 22:39:32 +00:00
fix(ci): replace gh run list with Gitea commit-status query (#75 class F)
All checks were successful
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 11s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 9s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 8s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 23s
Check merge_group trigger on required workflows / Required workflows have merge_group trigger (pull_request) Successful in 23s
CI / Detect changes (pull_request) Successful in 26s
E2E API Smoke Test / detect-changes (pull_request) Successful in 21s
Retarget main PRs to staging / Retarget to staging (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 18s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 19s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 16s
Harness Replays / detect-changes (pull_request) Successful in 21s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 18s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 38s
CI / Platform (Go) (pull_request) Successful in 9s
CI / Canvas (Next.js) (pull_request) Successful in 12s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 9s
CI / Python Lint & Test (pull_request) Successful in 9s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 11s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 11s
Harness Replays / Harness Replays (pull_request) Successful in 9s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 13s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 9s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
5b3ce5c818
Part of the post-#66 sweep to remove `gh` CLI dependencies that fail
silently against Gitea. Class F covers `gh run list --workflow=X
--commit=SHA` shapes — querying whether a specific workflow ran (and
how it finished) for a specific SHA.

Why this is the only call site in class F:

`gh run list` hits GitHub's `/repos/.../actions/runs` REST endpoint.
Gitea exposes ZERO endpoints under `/repos/.../actions/runs` —
verified 2026-05-07 via swagger inspection: only secrets, variables,
and runner-registration tokens live under /actions/. There's no way
to query workflow run state via the Gitea v1 API directly.

However, every Gitea Actions job DOES emit a commit status with
`context = "<Workflow Name> / <Job Name> (<event>)"` (verified
2026-05-07 by reading /repos/.../commits/{sha}/statuses on a recent
main SHA). That surface is exactly what we need: each workflow run
leg is one status row, the aggregate state encodes the run outcome,
and Gitea exposes it under `/api/v1/repos/.../commits/{sha}/statuses`
which IS available.

Affected:

`auto-promote-on-e2e.yml` (lines 172-180):
  Old: `gh run list --workflow e2e-staging-saas.yml --commit $SHA
       --json status,conclusion --jq ...` returning a 5-bucket string
       like `completed/success` | `in_progress/none` | `none/none` |
       `completed/failure` | `completed/cancelled`.
  New: `curl /api/v1/repos/.../commits/$SHA/statuses` + jq filter on
       contexts whose name starts with
       `"E2E Staging SaaS (full lifecycle) /"`. Mapping:
         0 matched contexts          → "none/none"      (E2E paths-
                                                          filtered out
                                                          — same as
                                                          before)
         any context = pending       → "in_progress/none" (defer)
         any context = error|failure → "completed/failure" (abort)
         all contexts = success      → "completed/success" (proceed)
  The `completed/cancelled` arm of the case statement becomes
  unreachable: Gitea status API doesn't expose a `cancelled` state
  (it has success/failure/error/pending/warning), so per-SHA
  concurrency cancellations now surface as `failure` and are handled
  by the failure branch. Documented in-place; the cancelled arm is
  kept as defense-in-depth for any future dual-host operation.

Verification:

- Live curl against the current main SHA returns `none/none` (E2E
  was paths-filtered for that change set — expected).
- Synthetic-input jq tests verify all four mapping buckets:
    no contexts                 → "none/none"
    one context = pending       → "in_progress/none"
    success + success           → "completed/success"
    success + failure           → "completed/failure"
- YAML syntax validates.

Token: continues to use act_runner's GITHUB_TOKEN (per-run, repo
read scope). The `/commits/{sha}/statuses` endpoint is repo-scoped,
no extra perms needed.

Closes part of #75. Master tracking issue at #75; companion PRs:
#80 (class A — `gh pr ...`), #81 (class D — `gh api ...`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ghost approved these changes 2026-05-07 22:52:15 +00:00
Ghost left a comment
First-time contributor

gh-CLI sweep class F (gh run list → /commits/{sha}/statuses query). auto-promote-on-e2e.yml E2E gate. 4 mapping buckets verified against synthetic+real Gitea data. 23/24 green, 0 failures. Mitigation for workflow-rename brittleness: PR #56's check-name parity gate already protects. Ready.

gh-CLI sweep class F (gh run list → /commits/{sha}/statuses query). auto-promote-on-e2e.yml E2E gate. 4 mapping buckets verified against synthetic+real Gitea data. 23/24 green, 0 failures. Mitigation for workflow-rename brittleness: PR #56's check-name parity gate already protects. Ready.
claude-ceo-assistant added 1 commit 2026-05-07 22:53:33 +00:00
Merge branch 'main' into fix/issue75-class-F-gh-run-list-to-statuses
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 6s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 14s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 5s
Check merge_group trigger on required workflows / Required workflows have merge_group trigger (pull_request) Successful in 11s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 5s
branch-protection drift check / Branch protection drift (pull_request) Successful in 18s
CI / Detect changes (pull_request) Successful in 15s
pr-guards / disable-auto-merge-on-push (pull_request) Failing after 5s
E2E API Smoke Test / detect-changes (pull_request) Successful in 14s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 15s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 13s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 15s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 15s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 13s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 11s
CI / Platform (Go) (pull_request) Successful in 13s
CI / Canvas (Next.js) (pull_request) Successful in 15s
CI / Python Lint & Test (pull_request) Successful in 13s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 14s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 14s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 12s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 20s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
4d5c9a6646
claude-ceo-assistant added 1 commit 2026-05-07 23:44:02 +00:00
Merge branch 'main' into fix/issue75-class-F-gh-run-list-to-statuses
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 2s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 2s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 2s
pr-guards / disable-auto-merge-on-push (pull_request) Failing after 4s
CI / Detect changes (pull_request) Successful in 9s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 9s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 7s
Check merge_group trigger on required workflows / Required workflows have merge_group trigger (pull_request) Successful in 7s
branch-protection drift check / Branch protection drift (pull_request) Successful in 10s
E2E API Smoke Test / detect-changes (pull_request) Successful in 10s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 9s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 9s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 10s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 9s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
CI / Platform (Go) (pull_request) Successful in 3s
CI / Python Lint & Test (pull_request) Successful in 3s
CI / Canvas (Next.js) (pull_request) Successful in 5s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 4s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 4s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 3s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 5s
ed0874504e
claude-ceo-assistant merged commit 07bd91e436 into main 2026-05-07 23:44:22 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#83
No description provided.