Files
cc-ci-orchestrator/cc-ci-plan/plan-phase-gtea-gitea-fulltests.md
autonomic-bot 24b3a25ce6 plan: queue gtea — enroll gitea as a fully-tested recipe + verify LFS PR #1
Operator-requested 2026-06-15. gitea is currently only a dep provider for
drone; this phase builds the full recipe-under-test suite (install/upgrade/
backup/restore/custom + lint + screenshot), ports the upstream parity corpus
(health_check, git_push), and verifies recipe-maintainers/gitea PR #1
('feat: support Git LFS on plain gitea', branch lfs-plain-gitea) via an LFS
round-trip + JWT-stability capstone test — red/skipped on main, green on the
PR. Central constraint: do NOT break drone's gitea-dep path (shared
recipe_meta.py). builder+adversary on sonnet.

The agents.toml diff also records the already-live aoeng/aotest/porepo/poe2e
queue head (agent-orchestrator workstream; their plan files remain owned by
that effort).
2026-06-15 19:32:14 +00:00

12 KiB
Raw Blame History

Phase gtea — enroll gitea as a FULLY-TESTED recipe (+ verify LFS PR #1)

Mission (operator-specified 2026-06-15): gitea currently exists in cc-ci only as a dependency provider for the drone phase — tests/gitea/recipe_meta.py is the lone file and its header says "NOT a standalone recipe-under-test". Turn gitea into a fully-tested recipe (install + upgrade + backup + restore + custom functional tests + lint + screenshot), to the same standard as cryptpad/keycloak. Then, once the suite is basically working, verify it against the open LFS PR https://git.autonomic.zone/recipe-maintainers/gitea/pulls/1 (feat: support Git LFS on plain gitea, branch lfs-plain-giteamain) — the suite's LFS capstone test is the mechanism that proves the PR.

The non-negotiable constraint that shapes everything below: do NOT break drone. gitea's recipe_meta.py is loaded by the SAME code path whether gitea is drone's dep or the recipe-under-test. Drone's CI must stay green throughout (it provisions gitea as an install-time dep, sqlite3 + relaxed auth, via sso.setup_gitea_oauth).

State files: STATUS-gtea.md, BACKLOG-gtea.md, REVIEW-gtea.md, JOURNAL-gtea.md. DECISIONS.md shared.

0. Prerequisites (verify before deploying)

  • /etc/timezone host fix is already live (shipped with the drone phase — gitea binds /etc/timezone:ro; the Nix environment.etc."timezone" fix is deployed). Builder's first action: confirm test -f /etc/timezone on the cc-ci host (content UTC). It should exist — drone already deploys gitea. If it's somehow absent, write a BLOCKED note in STATUS-gtea.md ("P0 host deploy needed — orchestrator") and do meta/test authoring that needs no deploy until the orchestrator restores it.
  • abra over a pseudo-TTY (ssh cc-ci 'script -qec "abra … -n" /dev/null'); creds baked into the mirror origin where needed; stash the untracked overlay. CI host has no python3 on the default PATH.

1. Scope — the file manifest for tests/gitea/

Build to the recipe-under-test anatomy (registry/contracts: runner/harness/meta.py, runner/harness/discovery.py; exemplars tests/cryptpad/, tests/keycloak/):

  1. recipe_meta.py (UPDATE — carefully; this is the conflict surface).

    • Keep the EXISTING dep behaviour intact (sqlite3 COMPOSE_FILE, relaxed-auth env) so drone's gitea-dep deploy is byte-for-byte unchanged. Update the header comment: gitea is now both a dep provider AND a recipe-under-test.
    • Add the recipe-under-test keys that are harmless to the dep path: HEALTH_PATH (/api/healthz already set), HEALTH_OK, sensible DEPLOY_TIMEOUT/HTTP_TIMEOUT, BACKUP_CAPABLE (the gitea recipe HAS backupbot.backup labels — backup/restore are REAL tiers, not skips), and SCREENSHOT (gitea has a real landing/login UI — default capture should work; follow the shot-phase standard). Consider READY_PROBE for a gitea-specific readiness gate (API reachable, not just TCP).
    • DB: sqlite3 (matches the dep config + lightest footprint; gitea ships compose.sqlite3.yml). Do not switch the dep to mariadb/postgres.
    • No new ALL-CAPS meta keys without adding them to the meta.py registry + regenerating docs (unknown caps = hard MetaError, unit-tested).
  2. ops.py (NEW) — uniform HookCtx hooks (pre_backup/pre_restore/pre_upgrade, and pre_install if needed for the LFS secret — see §3). Seed a data-integrity marker via the gitea admin REST API: create a repo (+ a user/org and a committed file) before the op so test_backup/test_restore/test_upgrade can assert data continuity. Model on tests/keycloak/ops.py (API-based markers) — NOT file-in-container hacks.

  3. Lifecycle overlays (NEW) — additive on the _generic floor:

    • test_install.py — generic assert_serving() + gitea-specific: /api/healthz → 200 and the admin API is reachable/authenticated.
    • test_backup.py — the seeded repo/user marker is present after backup.
    • test_restore.pymutation pattern: ops.pre_restore deletes/mutates the marker; restore must return it to the backed-up state; assert reverted (real divergence — a no-op restore must fail this).
    • test_upgrade.py — the marker survives the upgrade to PR-head (data continuity across the chaos redeploy).
  4. custom/ functional tests (NEW) — discovered from tests/gitea/custom/test_*.py. Floor is ≥2 tests beyond parity. Deliver:

    • custom/test_health.pyparity port of recipe-info/gitea/tests/health_check.py.
    • custom/test_git_push.pyparity port of recipe-info/gitea/tests/git_push.py: create repo via API → clone → commit → push over HTTPS → verify commit landed via API → clean up. (Has real teeth: a broken SCM fails the push.)
    • custom/test_admin_api.py — beyond-parity: user + org + token lifecycle via the REST API (create, read-back, delete), proving admin-API CRUD works.
    • custom/test_lfs_roundtrip.py — beyond-parity and the PR-#1 capstone (see §3).
  5. PARITY.md (NEW) — map the upstream corpus (recipe-info/gitea/tests/health_check.py, git_push.py) to the cc-ci ports; document the beyond-parity custom tests + why they're non-vacuous; record that backup/restore are REAL tiers (backupbot labels present); record the sqlite3 DB choice; declare any deferrals with reasons (EXPECTED_NA for anything that structurally can't run on main, e.g. LFS before PR #1 merges — see §3).

2. The gitea-dep conflict — the central design problem (READ FIRST)

meta_mod.load("gitea") returns ONE object used by BOTH roles. The two roles never run concurrently (different RECIPE values: RECIPE=drone provisions gitea as a dep; RECIPE=gitea is the recipe-under-test), but they SHARE recipe_meta.py. Therefore:

  • Hard gate: drone CI stays green. After any recipe_meta.py change, run drone through its CI/!testme path and confirm GREEN — gitea-as-dep provisioning (sso.setup_gitea_oauth, tests/unit/test_gitea_dep.py) must be unaffected. Treat a drone regression as a failed gate, never "acceptable collateral".
  • Do not let recipe-under-test config leak into the dep deploy. In particular the LFS overlay (§3) must apply ONLY when gitea is the recipe-under-test, never to drone's dep deploy. Investigate whether EXTRA_ENV(ctx) can distinguish the roles (e.g. via ctx.op / ctx.deps / domain shape) or whether the overlay belongs on a recipe-under-test-only knob. Decide, document the mechanism in PARITY.md + DECISIONS.md, and prove both paths.
  • If a clean split proves impossible inside one recipe_meta.py, raise it in STATUS before forking any shared harness behaviour — do not silently special-case gitea in runner/.

3. Verify the LFS PR (#1) — the capstone

PR #1 (lfs-plain-giteamain) adds opt-in Git LFS to plain gitea: a new compose.lfs.yml overlay (GITEA_LFS_START_SERVER=true + a mounted lfs_jwt_secret), an app.ini.tmpl change emitting a stable LFS_JWT_SECRET, .env.sample docs, and a version bump 3.5.2 → 3.6.0. The PR's own "Tested on cctest" evidence is exactly the test to encode: an LFS object upload→download round-trip via the batch API (downloaded bytes hash to the OID), plus the LFS JWT secret stable across abra app restart (the regression it fixes).

  • custom/test_lfs_roundtrip.py enables LFS (COMPOSE_FILE includes compose.lfs.yml; generate the lfs_jwt_secret via abra app secret generate … lfs_jwt_secret v1 + SECRET_LFS_JWT_SECRET_VERSION=v1, length 43 — wire through EXTRA_ENV/ops.pre_install for the recipe-under-test deploy only, never the drone dep), then: init a repo with git lfs, push an LFS-tracked object, fetch it from a clean clone, assert the OID hash matches; then abra app restart and assert the rendered app.ini LFS_JWT_SECRET is unchanged and tokens still validate.
  • Because compose.lfs.yml is NEW in PR #1, this is the proof the suite verifies the PR: on gitea main the overlay is absent → the LFS test structurally skips (declare it in EXPECTED_NA with reason "LFS overlay lands in PR #1"); on the lfs-plain-gitea head the overlay exists → the LFS test runs and must pass. Red/absent on main, green on the PR.
  • How to verify against the PR: once the suite is green on main, run the harness with the PR head checked out — the ci-test-review/recipe-upgrade PR path (RECIPE=gitea REF=lfs-plain-gitea via verify-pr.sh), and/or post !testme on PR #1 so the result lands in the PR for the operator. Full lifecycle (install/upgrade/backup/restore/custom) must stay green on the PR head, AND the LFS capstone must go green there.
  • We VERIFY PR #1; we do NOT merge it (operator's call — mirror PR-only rules). If the verification surfaces a real defect in the PR, leave a PR comment per the recipe-upgrade discipline; do not "fix" it by weakening the test.

4. Gates

M1 — Suite built + green locally (on gitea main). All chosen tiers green on the harness path with evidence: install + upgrade + backup + restore + custom (test_health, test_git_push, test_admin_api) + lint (L5) + screenshot. recipe_meta.py updated WITHOUT breaking drone — drone's gitea-dep deploy still green (proven, not assumed). The LFS test correctly skips on main (overlay absent), declared in EXPECTED_NA. Unit tests for any new harness-visible surface; no gate weakening anywhere. Adversary cold-verifies from a clean checkout: tests have teeth (a misconfigured gitea fails install/health; git_push actually pushes + verifies via API; the backup/restore mutation genuinely diverges then reverts); declared skips justified against the published recipe; no drone regression; the dep-vs-recipe-under-test split (§2) is real and documented.

M2 — Proven in real CI + PR #1 verified. Full gitea lifecycle green via the real CI / !testme path on main; drone CI re-confirmed green (dep path intact); screenshot real + visually verified; level recorded under the de-capped semantics; canonical/warm enrollment decision documented. Then the PR-#1 verification: harness on lfs-plain-gitea head → full lifecycle green AND the LFS round-trip + JWT-stability capstone GREEN (and demonstrably red/skipped on main), result posted to PR #1; nothing merged. Operator summary in STATUS-gtea.md. Fresh Adversary PASS on both milestones → ## DONE.

5. Guardrails (binding)

  • Drone stays green — the dep path is sacred. Never relax/weaken gitea's dep behaviour to make the standalone suite pass. A drone regression fails the phase.
  • Recipe mirrors are PR-only. Never push the mirror's main, never merge. PR #1 is the operator's — we verify it (run the harness / post !testme), we do not merge it. A recipe defect found → PR/comment per recipe-upgrade, operator decides.
  • Never weaken a test to turn red green. Bounded changes (the enrollment + LFS capstone), not harness rewrites. No new meta keys without registry + doc regeneration.
  • Secrets (gitea admin password, OAuth client secret, lfs_jwt_secret) are generated per-run and MUST stay out of logs/commits/artifacts — manifest redaction rules apply.
  • Shared swarm: ≤23 concurrent deploys; one dev-/test gitea at a time; tear down every deploy on every exit path (success, red, or abort), dep included. The drone path brings up two deploys (gitea + drone) — budget for it.
  • Host changes are orchestrator/operator-only (file them in STATUS, don't improvise). Commit author autonomic-bot <autonomic-bot@noreply.git.autonomic.zone>; push every commit.

6. Definition of Done

gitea enrolled as a fully-tested recipe (install/upgrade/backup/restore + custom functional tests + lint + screenshot) green in real CI, without breaking drone's gitea-dep path; PARITY.md complete (upstream health_check/git_push ported, beyond-parity tests + LFS documented); the LFS PR #1 (lfs-plain-gitea) verified GREEN via the LFS capstone (round-trip + JWT-secret stability) on the PR head, with the same test demonstrably red/skipped on main — proving the suite catches the feature; result posted to PR #1, nothing merged; levels/records reconciled; M1 + M2 fresh Adversary PASSes recorded in REVIEW-gtea.md.