Seed staging databases from a prod snapshot on first deploy

When a branch's staging volume doesn't exist yet, take a WAL-safe
online snapshot of the prod SQLite database (sqlite3.backup() in a
throwaway container, prod is only read) into the new volume. Later
pushes keep the staging data; deleting the branch (or the volume)
causes a fresh seed next time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 22:00:27 +02:00
parent ae7fa5bae7
commit d45ae357c4
+25
View File
@@ -26,6 +26,31 @@ jobs:
- name: Build image - name: Build image
run: docker build -t "timetracker:staging-${SLUG}" . run: docker build -t "timetracker:staging-${SLUG}" .
- name: Seed database from prod (first deploy of this branch only)
run: |
if docker volume inspect "timetracker-staging-${SLUG}" >/dev/null 2>&1; then
echo "Volume exists, keeping current staging data"
exit 0
fi
docker volume create "timetracker-staging-${SLUG}"
# sqlite3.backup() takes a consistent online snapshot (WAL-safe);
# prod is only read, never written.
docker run --rm \
-v /docker/timetracker/data:/prod \
-v "timetracker-staging-${SLUG}:/dest" \
python:3.14-slim-bookworm sh -c "
python -c \"
import sqlite3
source = sqlite3.connect('file:/prod/db.sqlite3?mode=ro', uri=True)
destination = sqlite3.connect('/dest/db.sqlite3')
source.backup(destination)
games = destination.execute('select count(*) from games_game').fetchone()[0]
sessions = destination.execute('select count(*) from games_session').fetchone()[0]
print(f'Seeded staging database: {games} games, {sessions} sessions')
destination.close()
source.close()
\" && chown 1000:100 /dest/db.sqlite3"
- name: Deploy staging container - name: Deploy staging container
run: | run: |
docker rm -f "timetracker-staging-${SLUG}" 2>/dev/null || true docker rm -f "timetracker-staging-${SLUG}" 2>/dev/null || true