Files
cc-ci/docs/enroll-recipe.md
autonomic-bot 7addb9686c
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
bridge: polling primary + org-membership auth (orchestrator design change)
Polling is now the primary, read-only trigger (always-on thread); the /hook
webhook is an optional admin-registered push optimization deduped by comment id.
Authorize commenters via GET /orgs/{owner}/members/{user} (204, read-level) +
optional allowlist, replacing the admin-requiring /collaborators permission
endpoint. Bot never self-registers webhooks. Enroll = POLL_REPOS + tests/<recipe>/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:41:25 +01:00

3.4 KiB

Enrolling a recipe under cc-ci (D5)

Adding a recipe is a small, repeatable, no-harness-surgery operation:

1. Make the recipe available on the mirror

Recipes under test live on the private mirror git.autonomic.zone/recipe-maintainers/<recipe>, synced from upstream git.coopcloud.tech. If not yet mirrored, mirror it (abra fetch + push to the org) — see the recipe mirror+PR flow (plan §4.1). A recipe may ship its own tests/ dir in its repo; those are discovered and run against the live app (D4 — see below).

2. Add the per-recipe test tree in this repo

tests/<recipe>/
├── recipe_meta.py      # optional per-recipe harness config (see below)
├── test_install.py     # install-stage assertions (health + Playwright)
├── test_upgrade.py     # upgrade-stage assertions (data survives)
└── test_backup.py      # backup→mutate→restore assertions

Copy from an existing recipe (e.g. tests/custom-html/ for a simple app, tests/keycloak/ for a DB-backed one). The shared fixtures live in tests/conftest.py + runner/harness/do not edit them to add a recipe; instead set per-recipe config in recipe_meta.py:

HEALTH_PATH = "/realms/master"   # path that returns a healthy status (default "/")
HEALTH_OK = (200,)               # acceptable status codes (default 200/301/302)
DEPLOY_TIMEOUT = 600             # seconds for services to converge (default 600)
HTTP_TIMEOUT = 600               # seconds for the app to answer (default 300)

The test files use the fixtures: deployed_app (install), deployed (function-scoped), and the harness.lifecycle helpers (http_get, http_body, exec_in_app, upgrade_app, backup_app, restore_app, previous_version). The harness forces LETS_ENCRYPT_ENV="" (no ACME) and a unique short domain per run, and guarantees teardown.

3. Recipe-local tests (D4)

If the recipe's own repo contains tests/test_*.py, the runner snapshots them right after fetch and runs them against the live deployment as a recipe-local stage. Contract: those tests receive env CCCI_BASE_URL (e.g. https://<app>.ci.commoninternet.net/) and CCCI_APP_DOMAIN.

4. Add the repo to the bridge poll list

The trigger is polling (primary): add the repo's full name to the comment-bridge POLL_REPOS csv (modules/bridge.nix) and nixos-rebuild switch. The bridge then polls that repo's open PRs every 30s and fires a run on a new !testme comment from an authorized org member. This needs only read + comment access — no webhook, no repo-admin.

!testme on a PR runs install/upgrade/backup + any recipe-local tests, and reports back to the PR.

Optional: lower-latency webhook (admin-registered)

Polling already satisfies D1 (<60s). For lower latency an admin may optionally register a Gitea issue_comment webhook (the bot does not self-register one — that needs repo-admin):

  • URL https://ci.commoninternet.net/hook, content-type application/json, event Issue Comment, secret = the shared webhook HMAC (secrets/secrets.yamlwebhook_hmac).
  • The Gitea instance must allow the host (admin: add ci.commoninternet.net to the [webhook] ALLOWED_HOST_LIST).

The webhook and poller are deduped by comment id, so a comment seen by both fires only once.

Run locally

RECIPE=<recipe> PR=<n> REF=<sha-or-branch> SRC=recipe-maintainers/<recipe> \
  STAGES=install,upgrade,backup cc-ci-run runner/run_recipe_ci.py