Prereleases for the dev server, and the hosts run the published images (#1043) #1044

Merged
claude-bot merged 4 commits from feat/1043-prereleases-and-image-deploys into main 2026-10-03 15:02:31 +00:00
Collaborator

Closes #1043. Closes #1042.

Summary

The repo side of the owner's 2026-10-03 decisions: cut prereleases for testing on dev, and run the published images on both hosts instead of building from source. It also fills in every remaining TODO(ask) in the project profile. No host is changed by this PR; the cutovers come after it, each with the owner's go-ahead.

  • docker-compose.images.yml (new). Pins migrate, backend, worker, beat, frontend and bot to git.rhoving.com/rbrooks/quest-board-<c>:<version>, with build: !reset null so an image host cannot build from source. A host turns it on with COMPOSE_FILE=docker-compose.yml:docker-compose.images.yml in .env and updates by checking out a release tag. The file says 4.4.1 today, matching APP_VERSION.
  • check_version_sync.py. The six image tags must equal APP_VERSION, so a release commit that forgets the overlay fails CI.
  • changelog.py render. Prints the pending fragments exactly as assemble would insert them (it reuses _assemble_text), writing nothing.
  • release.yml. A tag containing - gets notes from render under a "Prerelease of X.Y.Z for testing on the dev server; not for production." line, instead of "No changelog entry found". Finals are unchanged.
  • docs/OPERATIONS.md. New "Cutting a prerelease"; "Running from Published Images" and "Updating Production" rewritten for image hosts (enable, update, rollback only across migration-free releases, never build from source).
  • Project profile (CLAUDE.md). All of #1042, with items 2 and 3 as the owner answered them. Dev's Deploy.model is image. The lint recipe gains --no-cache, which the dev host needs.
  • /qb-deploy-dev. Deploys a tag (git checkout v<version>, pull, up) instead of building main.

Migrations on an image host: make migrate-safe runs inside the running backend, which on an image host is still the old release, so its alembic upgrade head would be the old code's. The update procedure instead runs the read-only db_preflight from the new image (docker compose run --rm migrate python -m app.cli db_preflight) after the pull and before up, and says not to use migrate-safe for an update. The same weakness exists for a source-build self-hoster (git pull does not change the running container either); that is left for a follow-up.

Changelog

  • Fragment: changelog.d/1043-prereleases-and-image-deploys.changed.md

Test plan

  • check_version_sync.py: Version sync OK — app 4.4.1, bot contract v2.
  • changelog.py check: OK (141 fragments).
  • changelog.py render: prints today's fragments grouped under Added and the rest.
  • On docker-test, in a scratch work dir: docker compose -f docker-compose.yml -f docker-compose.images.yml config succeeds, none of the six services has a build: key, each has its 4.4.1 image; with COMPOSE_FILE set in .env, plain docker compose config --images resolves the overlay.
  • Backend: tests/test_version_sync.py tests/test_changelog_script.py, 32 passed (10 new cases).
  • Lint (ruff 0.4.4, format and check): clean.
  • release.yml's notes step run locally for v4.5.0-rc1 (rendered prerelease notes), v4.4.1 (unchanged path) and v9.9.9 (the fallback).
  • Not run: a real rc tag through Forgejo Actions, docker compose pull/up with the overlay against a live stack, and docker compose run --rm migrate python -m app.cli db_preflight. These are exercised first at the dev cutover.

🤖 Generated with Claude Code

Closes #1043. Closes #1042. ## Summary The repo side of the owner's 2026-10-03 decisions: cut prereleases for testing on dev, and run the published images on both hosts instead of building from source. It also fills in every remaining `TODO(ask)` in the project profile. **No host is changed by this PR**; the cutovers come after it, each with the owner's go-ahead. - **`docker-compose.images.yml` (new).** Pins `migrate`, `backend`, `worker`, `beat`, `frontend` and `bot` to `git.rhoving.com/rbrooks/quest-board-<c>:<version>`, with `build: !reset null` so an image host cannot build from source. A host turns it on with `COMPOSE_FILE=docker-compose.yml:docker-compose.images.yml` in `.env` and updates by checking out a release tag. The file says 4.4.1 today, matching `APP_VERSION`. - **`check_version_sync.py`.** The six image tags must equal `APP_VERSION`, so a release commit that forgets the overlay fails CI. - **`changelog.py render`.** Prints the pending fragments exactly as `assemble` would insert them (it reuses `_assemble_text`), writing nothing. - **`release.yml`.** A tag containing `-` gets notes from `render` under a "Prerelease of X.Y.Z for testing on the dev server; not for production." line, instead of "No changelog entry found". Finals are unchanged. - **`docs/OPERATIONS.md`.** New "Cutting a prerelease"; "Running from Published Images" and "Updating Production" rewritten for image hosts (enable, update, rollback only across migration-free releases, never build from source). - **Project profile (`CLAUDE.md`).** All of #1042, with items 2 and 3 as the owner answered them. Dev's `Deploy.model` is `image`. The lint recipe gains `--no-cache`, which the dev host needs. - **`/qb-deploy-dev`.** Deploys a tag (`git checkout v<version>`, `pull`, `up`) instead of building `main`. **Migrations on an image host:** `make migrate-safe` runs inside the running `backend`, which on an image host is still the old release, so its `alembic upgrade head` would be the old code's. The update procedure instead runs the read-only `db_preflight` from the new image (`docker compose run --rm migrate python -m app.cli db_preflight`) after the pull and before `up`, and says not to use `migrate-safe` for an update. The same weakness exists for a source-build self-hoster (`git pull` does not change the running container either); that is left for a follow-up. ## Changelog - [x] Fragment: `changelog.d/1043-prereleases-and-image-deploys.changed.md` ## Test plan - [x] `check_version_sync.py`: `Version sync OK — app 4.4.1, bot contract v2.` - [x] `changelog.py check`: OK (141 fragments). - [x] `changelog.py render`: prints today's fragments grouped under **Added** and the rest. - [x] On docker-test, in a scratch work dir: `docker compose -f docker-compose.yml -f docker-compose.images.yml config` succeeds, none of the six services has a `build:` key, each has its 4.4.1 image; with `COMPOSE_FILE` set in `.env`, plain `docker compose config --images` resolves the overlay. - [x] Backend: `tests/test_version_sync.py tests/test_changelog_script.py`, 32 passed (10 new cases). - [x] Lint (ruff 0.4.4, format and check): clean. - [x] `release.yml`'s notes step run locally for `v4.5.0-rc1` (rendered prerelease notes), `v4.4.1` (unchanged path) and `v9.9.9` (the fallback). - [ ] Not run: a real rc tag through Forgejo Actions, `docker compose pull`/`up` with the overlay against a live stack, and `docker compose run --rm migrate python -m app.cli db_preflight`. These are exercised first at the dev cutover. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
An image host checks a risky migration with the new release's code, not migrate-safe (#1043)
All checks were successful
CI / Bot/backend version sync (pull_request) Successful in 32s
CI / Backend lint (ruff) (pull_request) Successful in 39s
CI / Does this change touch the recording path? (pull_request) Successful in 18s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Feedback site tests (pull_request) Successful in 55s
CI / Bot tests and audit (pull_request) Successful in 2m15s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m35s
CI / Docker image build (pull_request) Successful in 4m52s
CI / End-to-end journeys (phone) (pull_request) Successful in 10m44s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 11m30s
CI / End-to-end journeys (desktop) (pull_request) Successful in 11m40s
CI / Frontend (shard 2/2) (pull_request) Successful in 17m18s
CI / Frontend (shard 1/2) (pull_request) Successful in 21m57s
CI / Backend migration, tests, and audit (pull_request) Successful in 48m44s
e76fa0e3bb
make migrate-safe runs inside the running backend container, which on an
image host is still the old release, so its alembic upgrade head is the old
code's. The update procedure now runs the read-only db_preflight from the new
image (docker compose run --rm migrate ...) after the pull and before up, and
says not to use migrate-safe for an update.

Also: the lint recipe passes --no-cache (the image's unprivileged user cannot
write .ruff_cache into a mounted work dir on the dev host), and the profile's
release object says a prerelease's body is rendered from changelog.d.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-bot deleted branch feat/1043-prereleases-and-image-deploys 2026-10-03 15:02:34 +00:00
Sign in to join this conversation.
No description provided.