diff --git a/.console/log.md b/.console/log.md index 900d54338..b11d28dcb 100644 --- a/.console/log.md +++ b/.console/log.md @@ -1,3 +1,28 @@ +## 2026-08-19 — B4 resolved: the audit context cannot match GitHub's + +Forgejo Actions is running: runner 6.3.1 registered against the live instance, +a probe workflow executed end to end (pulled node:20-bookworm, success in 91s). + +The spec's B4 assumed `audit` "must be reproduced under an identical context +name". It cannot be. Forgejo composes the context as +` / ()`, and with no `run-name:` the middle +segment is the COMMIT MESSAGE — so the default context changes every push and +is unsatisfiable as a required check, not merely mismatched. Setting +`run-name: audit` pins it to `custodian-audit / audit (push)`, stable across +commit messages. + +Also: `push` and `pull_request` each produce their own context on a PR head, so +the GitHub workflow's dual trigger doubles the work and leaves a second context +outstanding. The Forgejo workflow should trigger on `pull_request` only. + +Cutover config is therefore: `run-name: audit`, `on: pull_request`, and +branch protection requiring `custodian-audit / audit (pull_request)` — +Forgejo's string, not GitHub's. + +Bonus: the probe live-validated #517's translation against real status data — +`completed: ['...(pull_request)']`, `incomplete: ['...(push)']`, resolved from +a three-entry posting history by the latest-per-context dedupe. + ## 2026-08-19 — CI found what 3.12 could not The new `test-rest` job went red on its first run, which is the job doing its diff --git a/deploy/forgejo/README.md b/deploy/forgejo/README.md new file mode 100644 index 000000000..3bea06b55 --- /dev/null +++ b/deploy/forgejo/README.md @@ -0,0 +1,59 @@ +# Forgejo Actions runner + +The `audit` status the fleet's branch protection requires is produced by a +workflow, and a workflow needs a runner. This is how the local one is set up. + +## Runner + +```bash +TOKEN=$(docker exec -u git forgejo forgejo forgejo-cli actions generate-runner-token) + +docker volume create forgejo-runner-data +docker run --rm -v forgejo-runner-data:/data --network host \ + code.forgejo.org/forgejo/runner:6.3.1 \ + forgejo-runner register --no-interactive \ + --instance http://localhost:3000 \ + --token "$TOKEN" \ + --name oc-local-runner \ + --labels docker:docker://node:20-bookworm,ubuntu-latest:docker://node:20-bookworm + +docker run -d --name forgejo-runner --restart unless-stopped \ + -v forgejo-runner-data:/data \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --group-add "$(getent group docker | cut -d: -f3)" \ + --network host \ + code.forgejo.org/forgejo/runner:6.3.1 forgejo-runner daemon +``` + +`--group-add` rather than `--user 0:0`: the runner needs the docker socket to +spawn job containers, and joining the socket's group is narrower than running +the daemon as root. Note that either way this grants control of the Docker +daemon, which is root-equivalent on the host — run it only where that is +acceptable. + +## The status context is NOT the job name + +Forgejo composes it as ` / ()`, and with no +`run-name:` the middle segment is **the commit message** — so the context +changes on every push and can never satisfy a required status check. + +Always set `run-name:`, and trigger on `pull_request` only (a `push` trigger +produces a second, separate context on the same head): + +```yaml +name: custodian-audit +run-name: audit +on: + pull_request: + branches: [main] +jobs: + audit: + runs-on: ubuntu-latest +``` + +That yields the stable context `custodian-audit / audit (pull_request)`, which +is what branch protection's `status_check_contexts` must list. It is Forgejo's +format, not GitHub's `audit` — a checklist that says "use the same name" is +wrong. + +See `docs/specs/forgejo-pr-adapter.md` for the evidence behind both points. diff --git a/docs/specs/forgejo-pr-adapter.md b/docs/specs/forgejo-pr-adapter.md index aba18259d..8cd705c1e 100755 --- a/docs/specs/forgejo-pr-adapter.md +++ b/docs/specs/forgejo-pr-adapter.md @@ -224,6 +224,83 @@ negative-case verification). Three findings revise the adversarial sections: 3. **`check-runs` is a confirmed 404**, and `GET /pulls/{index}.diff` serves the raw diff. +## B4 resolved — `audit` on Forgejo Actions, 2026-08-19 + +Actions enabled on the live instance, `forgejo-runner` 6.3.1 registered, and a +probe workflow run end to end. B4 said the `audit` status "must be reproduced +under an identical context name, or every PR blocks forever". **The identical +name is not achievable**, and the reason matters more than the inconvenience. + +### The context format is not the job name + +GitHub uses the job id, so OC's required context is the bare string `audit`. +Forgejo composes it: + +``` + / () +``` + +With no `run-name:`, the middle segment is **the commit message**. The probe's +first run produced: + +``` +audit.yml / ci: probe the status context name (push) +``` + +A required status check must be a fixed string. A context containing the commit +message changes on every push, so under the default configuration branch +protection can never be satisfied — not "blocks until you rename it", but +unsatisfiable in principle. + +### `run-name:` makes it stable + +Setting `run-name: audit` pins the middle segment. Re-run with a deliberately +different commit message: + +``` +custodian-audit / audit (push) +``` + +Stable, deterministic, and independent of the commit. That is the property +branch protection needs. + +### Both events fire on a PR head + +`push` and `pull_request` each produce their own context, so the GitHub +workflow's dual trigger yields **two** runs and two contexts per PR head: + +``` +custodian-audit / audit (pull_request) -> success +custodian-audit / audit (push) -> pending +``` + +Two consequences: the work is done twice, and a gate that requires "nothing +incomplete" sees the slower run as outstanding. The Forgejo workflow should +trigger on `pull_request` only. + +### Cutover configuration + +* workflow: `name: custodian-audit`, `run-name: audit`, `on: pull_request` +* branch protection `status_check_contexts`: `custodian-audit / audit (pull_request)` +* `apply_to_admins: true` (per the earlier live finding) + +The required-context string is therefore **Forgejo's format, not a copy of +GitHub's**. Any cutover checklist that says "use the same name" is wrong. + +### Live validation of the adapter + +The same probe confirmed `ForgejoPRClient` reads real status data correctly — +not fixtures: + +``` +failed: [] +incomplete: ['custodian-audit / audit (push)'] +completed: ['custodian-audit / audit (pull_request)'] +``` + +The statuses endpoint returned the full posting history (pending, pending, +success) for one job, and the latest-per-context dedupe resolved it as designed. + ## Correctness criteria 1. Status→check translation is explicit about what it loses. A test asserts a @@ -248,8 +325,8 @@ negative-case verification). Three findings revise the adversarial sections: - [x] `PRClient` protocol extracted; all 17 callers migrated (#512–#515) - [x] `ForgejoPRClient` implementing the protocol (`adapters/forgejo/pr_client.py`) - [x] Status→check translation with the losses tested (`STATUS_TO_CHECK`; warning→neutral, error→failure with its own name) -- [ ] `audit` (or its replacement) produced on Forgejo under an identical name -- [ ] Live verification against a real instance, **negative cases first** +- [x] `audit` produced on Forgejo — under Forgejo's context format, which *cannot* be identical (see B4 resolved) +- [x] Live verification against a real instance, negative cases first (#517, and the Actions probe above) - [ ] Cutover, GitHub demoted to read-only mirror ## Phasing