latest tag may resolve to an older version that treats Cloudflare R2's
NoSuchKey response on empty-prefix ListObjectsV2 as a hard error instead
of an empty list, causing the replica sync to stall on first deployment.
v0.5.8 is the current stable release (2026-02-12).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Router had no profile so it was always included in `up -d --wait`.
Writing the new target's config BEFORE the wait caused the router to become
unhealthy if the new slot failed — leaving it in a broken state for the next
deploy attempt.
Now: router keeps its old config (pointing to the still-running old slot)
during the health check wait, so it stays healthy throughout. Config is only
written and nginx -s reload triggered after the new slot passes its health
check. This is the correct blue-green pattern.
Also add `retries: 3` and `start_period: 10s` to the router health check
for resilience against transient startup failures.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
build context, env_file, and litestream volume mount all pointed at
./padelnomics/ which no longer exists after the flatten.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pgrep may not be available in the litestream image. kill -0 1 checks
whether PID 1 (litestream, after exec) is alive — works in any container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Re-enable deploy gate on litestream: pgrep-based healthcheck with 6
retries (30s window) after a 15s start period — broken backups now
fail the deploy loudly instead of silently succeeding.
Extend retention from 7d to 1yr (8760h): WAL frames are tiny for a
low-traffic app, R2 free tier covers years of storage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.5.8 dropped multi-replica support — remove the local path replica,
keeping only R2. Also disable litestream's healthcheck so deploy's
`up --wait` isn't gated on the backup service.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migration atomicity:
- Remove conn.commit() and executescript() from all up() functions (0000,
0011, 0012, 0013, 0014, 0015); executescript() issued implicit COMMITs
which broke the batch-rollback guarantee of the migration runner
- Rewrite 0000 with individual conn.execute() calls (was a single
executescript block)
Deploy hardening:
- Add pre-migration DB backup step to deploy.sh: saves
app.db.pre-deploy-<timestamp> in the volume before every migration
- On health-check failure: restore the backup, then stop + exit
- On success: clean up old backups (keep last 3)
Litestream:
- Enable R2 as primary replica in litestream.yml (env-var placeholders)
- Add local /app/data/backups as secondary replica
- docker-compose: add auto-restore on empty volume (sh entrypoint runs
'litestream restore' before 'litestream replicate' if app.db missing)
- Add LITESTREAM_R2_* vars to .gitlab-ci.yml .env block and .env.example
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GitLab CI runs pytest + ruff on master/MRs, then auto-deploys via SSH.
Blue-green strategy using Docker Compose profiles with an nginx router
on port 5000 for zero-downtime switching between slots.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>