# Per-recipe harness config for ghost (Phase 2 Q4.4 — Node.js publishing platform). # Ghost serves an HTML site at `/`; admin UI at `/ghost/`. The first GET to /ghost/ redirects # to the setup wizard (302). Ghost exposes a JSON Content API at /ghost/api/content/ which # requires an API key; the Admin API at /ghost/api/admin/ requires a session/token (see # functional/_ghost.py — version-negotiated, no /v3/ path). # State lives in a **MySQL** `ghost` DB (compose `db` service, mysql:8.0) + the `ghost_content` # volume (themes/images) — NOT sqlite. The `db` service is backupbot-labelled with a logical # mysqldump pre-hook; P4 (ops.py + test_{backup,restore,upgrade}.py) seeds a `ci_marker` row there. HEALTH_PATH = "/" # Ghost serves a themed site HTML at root (200) HEALTH_OK = (200,) DEPLOY_TIMEOUT = 1200 # subprocess timeout for `abra app deploy` HTTP_TIMEOUT = 900 # Ghost's fresh-DB first boot runs a full schema migration (dozens of CREATE TABLEs, each a separate # MySQL round-trip → ~6-9min on cc-ci, round-trip-bound so more vCPU doesn't help). The published # recipe healthcheck used `start_period: 1m` (+10×30s ≈ 6min grace) — too tight on cc-ci: swarm kills # the still-migrating task, leaving a stale `migrations_lock` → every later task deadlocks # (`MigrationsAreLockedError`). # # FIXED IN THE RECIPE-PR (recipe-maintainers/ghost#1, branch ci/mysql-backup): the app-service # healthcheck `start_period` is bumped to a literal 15m in the recipe itself — the real recipe # everyone runs, NOT a cc-ci compose fork. This is the plan §9 / plan-ccci-compose-overlay-policy.md # "prefer upstream PR" path: start_period CANNOT be expressed as an env var (abra validates the literal # compose 'duration' format BEFORE env substitution — `${VAR}` / `"${VAR:-1m}"` → FATA 'Does not match # format duration'; reproduced by the Adversary, REVIEW-2 4b862f6), so a literal recipe-PR bump is the # only §9-compliant way to widen it for the HEAD. Precedent: discourse + lasuite-drive collabora PRs. # start_period only widens the startup grace window (a healthy check still marks healthy at once → fast # hosts unaffected); NO test/assertion is weakened. # # UPGRADE-tier BASE grace (compose.ccci.yml): upgrade-to-latest must ALWAYS run # (plan-ccci-compose-overlay-policy.md §1), so the harness base-deploys the previous PUBLISHED version # (1.1.1+6-alpine) — which predates the PR and still ships the too-tight 1m start_period → it would # deadlock on the same migration kill. compose.ccci.yml re-applies the 15m grace to the BASE so the # from-version is deployable; install_steps.sh provides it to the checkout; CHAOS_BASE_DEPLOY skips the # clean-tree gate on that untracked overlay. It persists across the head checkout (idempotent — the PR # head already ships 15m). This is the policy-blessed "minimal overlay on the from-version so # upgrade-to-latest can run" — grace-only, masks no defect, weakens no test. # TIMEOUT 1200s = migration (≤9min) + convergence, bounded so a genuine failure still fails (not a # long blackout). See DECISIONS (ghost MySQL cold-boot / start_period recipe-PR + base-grace overlay). CHAOS_BASE_DEPLOY = True EXTRA_ENV = { "TIMEOUT": "1200", "COMPOSE_FILE": "compose.yml:compose.ccci.yml", }