Files
home-network-k3s-impl/infra/gitea
jakman85andClaude Opus 5 a2665c0511
gate / gate (push) Has been cancelled
gate / conformance (push) Has been cancelled
gate / gate (pull_request) Successful in 1m33s
gate / conformance (pull_request) Has been skipped
infra(gitea): script the branch protection that was never applied
Gitea repo settings live in its own database, not in a manifest Flux reconciles, so
they are drift-prone and invisible to the conformance gate -- which is how main came
to accept direct pushes while the log recorded it as protected.

Adds infra/gitea/branch-protection.sh (blocks direct pushes to main) and a README
carrying the measured before-state and the ORDER the remaining steps must happen in.

The script deliberately leaves enable_status_check:false and required_approvals:0.
Both would deadlock today: there is no runner to satisfy a required context, and the
only human account is not yet a repo collaborator, so no one could supply an approval.
The README sequences both.

It prints a push probe to run afterwards. The API response is not proof -- Gitea may
let site admins bypass protection, and that has to be observed, not assumed.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-29 00:20:12 -05:00
..

infra/gitea — platform settings that are NOT GitOps-managed

Gitea's own repo settings (branch protection, required status checks, approvals) live in Gitea's database, not in a manifest Flux reconciles. They are therefore drift-prone and invisible to the gate — exactly how the state below went unnoticed.

The gap this closes

Verified against the live API on 2026-09-29, against a DECISIONS-LOG.md entry (2026-08-31) asserting the opposite:

Claimed Measured
main is a protected branch, gate is a required status check branch_protections → []; protected:false, enable_status_check:false, required_approvals:0, user_can_push:true
Runner incluster-dind online zero runners registered at repo, org and site level

So main accepted direct pushes, and the gate could not run at all. CLAUDE.md is the rule; this directory is how the rule gets enforced by the machine.

branch-protection.sh

Blocks direct pushes to main. Run it, then run the push probe it prints — verify, do not trust the API response, since Gitea may let site admins bypass protection.

./infra/gitea/branch-protection.sh

It authenticates with the credential git already has stored for this host, and never prints the secret.

Sequence — order matters

  1. Get an Actions runner registered. It is GitOps-managed (apps/wave-1/gitea-actions.yaml, apps/wave-1/gitea-actions-runner-secret.sops.yaml), so it is deployed but not registering — suspect a stale registration token in that SOPS secret.
  2. Watch the gate actually report on a real PR, and note the context name it produced.
  3. Only then set enable_status_check: true with that exact context.
  4. Add the human reviewer as a repo collaborator, then raise required_approvals to 1.

Do not do step 3 before step 1. A required status check with no runner leaves every PR permanently pending — including the PR that would fix it. The same applies to gate.yml's conformance job: it is if: false and never runs, yet still publishes a gate / conformance (pull_request) context. Never list that one as required.