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.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: recipe-enroll
|
||||
description: Add a NEW recipe to cc-ci's maintained set, end to end — create + sync the recipe-maintainers mirror from coopcloud upstream, author a real test suite (health floor + non-vacuous recipe-specific tests incl. a create-an-object round-trip), enroll it in the !testme bridge (POLL_REPOS) and the weekly /upgrade-all inventory (used-recipes.md weekly row + upstream registry), then VERIFY the whole thing: full harness suite green with the new tests, bridge deployed (test-before-switch) and healthy. Opens the cc-ci PR for visibility and merges it directly once verification is green (the skill invocation is the authorization); the report lists merged PR links + what changed. Invoke as /recipe-enroll <recipe>.
|
||||
---
|
||||
|
||||
# recipe-enroll (thin wrapper)
|
||||
|
||||
The canonical definition of this skill lives in the **opencode** position:
|
||||
|
||||
**`.opencode/skills/recipe-enroll/SKILL.md`**
|
||||
|
||||
Read that file for the full procedure. This `.claude/skills/` copy is kept as a
|
||||
thin pointer for Claude Code compatibility; opencode loads the canonical
|
||||
definition from `.opencode/skills/` directly.
|
||||
@@ -0,0 +1,137 @@
|
||||
---
|
||||
name: recipe-enroll
|
||||
description: Add a NEW recipe to cc-ci's maintained set, end to end — create + sync the recipe-maintainers mirror from coopcloud upstream, author a real test suite (health floor + non-vacuous recipe-specific tests incl. a create-an-object round-trip), enroll it in the !testme bridge (POLL_REPOS) and the weekly /upgrade-all inventory (used-recipes.md weekly row + upstream registry), then VERIFY the whole thing: full harness suite green with the new tests, bridge deployed (test-before-switch) and healthy. Opens the cc-ci PR for visibility and merges it directly once verification is green (the skill invocation is the authorization); the report lists merged PR links + what changed. Invoke as /recipe-enroll <recipe>.
|
||||
---
|
||||
|
||||
# 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.py`** — `HEALTH_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.
|
||||
Reference in New Issue
Block a user