Files
cc-ci-orchestrator/.opencode/skills/recipe-enroll/SKILL.md
T
autonomic-bot 91179f872c skills: add /recipe-enroll — end-to-end enrollment of a new maintained recipe
Codifies the full path walked for the 2026-08-03 wordpress enrollment (cc-ci PR #14):
survey -> mirror create+sync from coopcloud -> author test suite (health floor +
non-vacuous recipe-specific tests incl. sec4.3 create-an-object round-trip, recipe-local
setup helper, PARITY.md) -> bridge POLL_REPOS -> used-recipes.md weekly row +
upstream/<recipe>.md registry -> full-suite-green verification with the new tests ->
bridge deploy via test-before-switch -> merge-on-green + report (PR-visibility policy).
Includes the traps hit live: creds injection over stdin for cc-ci-side helpers, fresh-
deploy wizard state in HEALTH_OK, repo-dev-shell ruff, swarm serialization, stale bridge
secret 401s silently dropping !testme.
2026-08-03 22:41:16 +00:00

8.3 KiB
Raw Blame History


recipe-enroll

Enroll a coopcloud recipe as a maintained recipe: mirrored, test-covered, !testme-triggerable, and picked up by the weekly /upgrade-all. This is the full end-to-end path — worked example: the 2026-08-03 wordpress enrollment (cc-ci PR #14).

"Maintained" = ALL of:

  1. Mirror recipe-maintainers/<recipe> exists, main = coopcloud upstream main, tags synced.
  2. tests/<recipe>/ enrolled in the cc-ci repo (this is what /upgrade-all + ci-test-review enumerate).
  3. POLL_REPOS in nix/modules/bridge.nix includes the mirror (the !testme bridge).
  4. cc-ci-plan/used-recipes.md has a <recipe> weekly row (orchestrator repo — /upgrade-all skips recipes without it or tagged external).
  5. cc-ci-plan/upstream/<recipe>.md registry entry (release-notes URLs + standing notes).

Preconditions

  • The recipe exists upstream: ssh cc-ci 'script -qec "abra recipe fetch <recipe>" /dev/null' succeeds (every recipe must have a coop-cloud correspondent).
  • GITEA_* creds in /srv/cc-ci/.testenv (orchestrator side; the cc-ci host does NOT have this file — inject creds over stdin when running helper scripts there, see step 2).
  • The shared Swarm is quiescent for step 6 (verification deploys the recipe) — do NOT verify concurrent with /upgrade-all or other verify runs; author everything first, verify when clear.

Procedure

1. Survey the recipe (read-only)

On cc-ci, after abra recipe fetch <recipe>, read ~/.abra/recipes/<recipe>/:

  • compose.yml — services + images (for the upstream registry), healthcheck (start_period informs DEPLOY_TIMEOUT), traefik labels.
  • .env.sample — is the app self-initializing, or does a fresh deploy sit in a setup wizard (e.g. wordpress without POST_DEPLOY_CMDS core_install)? The tests must handle the state a fresh CI deploy actually lands in.
  • Auth model — how will a test create an object? (REST + token, session login, XML-RPC, …) Check overlay configs (htaccess/nginx templates) for blocked endpoints before relying on one.

2. Create + sync the mirror

Create recipe-maintainers/<recipe> (Gitea API: POST /orgs/recipe-maintainers/repos, {"name":…,"private":true,"default_branch":"main","auto_init":false}), then force-sync from coopcloud with the existing helper run on cc-ci with creds injected via stdin (the host has no .testenv):

set -a; . /srv/cc-ci/.testenv; set +a
{ printf 'export GITEA_USERNAME=%q GITEA_PASSWORD=%q GITEA_URL=%q\n' "$GITEA_USERNAME" "$GITEA_PASSWORD" "$GITEA_URL";
  cat /srv/cc-ci-orch/.claude/skills/recipe-upgrade/open-recipe-pr.sh; } \
  | ssh cc-ci 'bash -s -- <recipe> --reconcile-only'

Expect: repo created (or exists), main force-synced to upstream, published tags pushed.

3. Author the test suite (dedicated cc-ci clone + branch)

git clone ssh://git@git.autonomic.zone:2222/recipe-maintainers/cc-ci.git /home/loops/work/cc-ci-tests-<recipe>
cd … && git checkout -b test/<recipe>-enroll-$(date -u +%Y%m%d)

tests/<recipe>/ contents (template: tests/uptime-kuma/, tests/wordpress/):

  • recipe_meta.pyHEALTH_PATH / HEALTH_OK (accept the fresh-deploy state, e.g. a 302 to a setup wizard), DEPLOY_TIMEOUT (recipe healthcheck start_period + DB init + first-boot copy, be generous), HTTP_TIMEOUT, WARM_CANONICAL = True (canon §2.B — all recipes enroll as data-warm canonicals, operator 2026-06-17).
  • custom/ — the health floor + ≥2 recipe-specific, non-vacuous tests, one of which is the §4.3 create-an-object + read-it-back (write through the app's real API, read back via a different path where possible — e.g. wordpress: XML-RPC write → REST read → permalink HTML). If the app needs setup/auth, put it in a recipe-local _<recipe>.py helper (idempotent ensure_* so test ordering doesn't matter; run-scoped class-B credentials — the app is destroyed at teardown). Design assertions to name the broken layer (e.g. assert both the rewrite-dependent and rewrite-independent API routes separately).
  • PARITY.md — table of tests × what's verified × why non-vacuous; note there's no recipe-maintainer parity corpus if so.
  • Lint with the repo dev-shell ruff: nix develop -c ruff check tests/<recipe>/ && nix develop -c ruff format tests/<recipe>/ (pre-existing drift in other files is not yours).

4. Bridge enrollment (same branch)

nix/modules/bridge.nix: append ,recipe-maintainers/<recipe> to the POLL_REPOS= CSV.

5. Inventory + registry (orchestrator repo, direct to main)

  • cc-ci-plan/used-recipes.md: add <recipe> weekly row (alphabetical).
  • cc-ci-plan/upstream/<recipe>.md: images table (source repo + releases/changelog links) + standing notes (setup-wizard behavior, auth caveats, known upgrade traps).
  • Commit + push (doc commits go direct to main in this repo).

6. Verify end-to-end — full suite GREEN with the new tests (swarm-serialized)

Open the cc-ci PR first (visibility): TITLE=… BODY_FILE=… bash /srv/cc-ci-orch/.claude/skills/ci-test-review/open-cc-ci-pr.sh. The body: what's enrolled, the test design rationale, the verify + deploy plan.

Then, when the swarm is clear:

ssh cc-ci 'rm -rf /root/cc-ci-test-verify && git clone --branch test/<recipe>-enroll-<date> \
  ssh://git@git.autonomic.zone:2222/recipe-maintainers/cc-ci.git /root/cc-ci-test-verify && \
  cd /root/cc-ci-test-verify && git submodule update --init secrets 2>/dev/null || true'
RECIPE=<recipe> REMOTE_ROOT=/root/cc-ci-test-verify \
  bash /srv/cc-ci-orch/.claude/skills/ci-test-review/verify-pr.sh     # no REF — recipe main

Required: cold full-suite green (install/upgrade/backup/restore/custom). Iterate the tests (bounded, ≤3 attempts) if red — fix the TESTS to match real app behavior, never weaken. Clean up /root/cc-ci-test-verify after.

7. Deploy the bridge change to the cc-ci host (test-before-switch)

The POLL_REPOS change only takes effect via a host rebuild. Per /cc-ci-server-update steps 5a-e: stage the branch to /root/cc-ci-deploy (+ secrets copy), nixos-rebuild build, detached nixos-rebuild test, verify reachable + healthy, then switch. Confirm the bridge is polling the new repo: ssh cc-ci 'docker service inspect ccci-bridge_app --format "{{json .Spec.TaskTemplate.ContainerSpec.Env}}"' | grep <recipe> and the bridge task is 1/1 with no auth errors in its logs (a stale Gitea secret 401s silently — see the 2026-08-03 finding).

8. Merge + report

Merge the cc-ci PR (invocation = authorization; PR is the visible record — comment the verification evidence first). Report to the operator: merged PR link + change summary, the verify log path, and the note that the next weekly /upgrade-all picks the recipe up automatically (it enumerates tests/<recipe>/ dirs × weekly rows).

Guardrails

  • Full-suite green is the enrollment gate — an enrolled-but-red recipe poisons every future sweep. Don't merge on partial green.
  • Never weaken: the tests assert the app's real current behavior, incl. asserting removed auth paths are rejected where that's the upstream intent.
  • Single-writer: dedicated clones/branches; never push main of cc-ci; never touch /root/builder-clone or the loops' clones; /root/cc-ci-test-verify is yours — remove after.
  • Serialize on the swarm: authoring is free, verification + bridge deploy wait for /upgrade-all/other runs to finish.
  • abra over ssh needs the pseudo-TTY wrap: ssh cc-ci 'script -qec "abra …" /dev/null'.
  • PRs for visibility, merged directly once verified; failed enrollment leaves the PR open with an explanatory comment and the report says exactly what's missing.