fix: automatic DB migration — run before backend starts
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 3s

Three fixes to ensure alembic migrations always run in production:

1. entrypoint.sh: add `exec "$@"` passthrough so `docker compose run
   --rm backend alembic upgrade head` actually runs alembic instead of
   ignoring args and starting uvicorn.

2. entrypoint.sh: remove `|| echo` fallback on alembic — if migration
   fails the container must stop, not silently continue.

3. deploy.yml: start only postgres+redis first, run migration, THEN
   start backend. Previously the backend started before migration,
   ran with stale schema, and the separate migration step was broken
   by the entrypoint bug.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 11:03:42 +02:00
parent 413f610f1a
commit 6c0ecded47
2 changed files with 34 additions and 26 deletions

View File

@@ -42,23 +42,23 @@ jobs:
docker compose exec -T postgres pg_dumpall -U translate | gzip > "$BACKUP_FILE" docker compose exec -T postgres pg_dumpall -U translate | gzip > "$BACKUP_FILE"
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1) BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo " Backup saved: ${BACKUP_FILE} (${BACKUP_SIZE})" echo " Backup saved: ${BACKUP_FILE} (${BACKUP_SIZE})"
# Keep only last 10 backups # Keep only last 10 backups
ls -t "${BACKUP_DIR}"/translate_db_*.sql.gz | tail -n +11 | xargs -r rm -- ls -t "${BACKUP_DIR}"/translate_db_*.sql.gz | tail -n +11 | xargs -r rm --
echo " 📦 Retained last 10 backups" echo " Retained last 10 backups"
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# 3. BUILD IMAGES # 3. BUILD IMAGES (--no-cache for fresh deps)
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
echo "=== [3/8] Building images ===" echo "=== [3/8] Building images ==="
docker compose build backend frontend docker compose build --no-cache backend frontend
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# 4. START SERVICES # 4. START INFRASTRUCTURE ONLY (postgres + redis)
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
echo "=== [4/8] Starting services ===" echo "=== [4/8] Starting postgres and redis ==="
docker compose up -d --remove-orphans docker compose up -d postgres redis
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# 5. WAIT FOR POSTGRES # 5. WAIT FOR POSTGRES
@@ -66,11 +66,11 @@ jobs:
echo "=== [5/8] Waiting for postgres ===" echo "=== [5/8] Waiting for postgres ==="
for i in $(seq 1 30); do for i in $(seq 1 30); do
if docker compose exec -T postgres pg_isready -U translate >/dev/null 2>&1; then if docker compose exec -T postgres pg_isready -U translate >/dev/null 2>&1; then
echo " Postgres ready after $((i * 2))s" echo " Postgres ready after $((i * 2))s"
break break
fi fi
if [ "$i" -eq 30 ]; then if [ "$i" -eq 30 ]; then
echo " Postgres not ready after 60s" echo " FATAL: Postgres not ready after 60s"
docker compose logs postgres --tail=30 docker compose logs postgres --tail=30
exit 1 exit 1
fi fi
@@ -78,38 +78,40 @@ jobs:
done done
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# 6. RUN MIGRATIONS (one-shot container) # 6. RUN MIGRATIONS (BEFORE starting backend)
# entrypoint passes through args via exec "$@"
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
echo "=== [6/8] Running database migrations ===" echo "=== [6/8] Running database migrations ==="
if ! docker compose run --rm backend alembic upgrade head; then if ! docker compose run --rm backend alembic upgrade head; then
echo " Migration FAILED!" echo " FATAL: Migration FAILED!"
echo " ⚠️ Restoring database from backup..." echo " Restoring database from backup..."
gunzip -c "$BACKUP_FILE" | docker compose exec -T postgres psql -U translate -d translate_db >/dev/null 2>&1 || true gunzip -c "$BACKUP_FILE" | docker compose exec -T postgres psql -U translate -d translate_db >/dev/null 2>&1 || true
echo " 🔄 Database restored. Restarting services..." echo " Database restored."
docker compose restart backend echo " Deploy ABORTED."
echo " ❌ Deploy aborted. Database restored from backup."
exit 1 exit 1
fi fi
echo " Migrations applied" echo " Migrations applied successfully"
# Restart backend to pick up new schema
docker compose restart backend
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# 7. HEALTH CHECK # 7. START ALL SERVICES (backend + frontend)
# backend entrypoint will see alembic is already at head → no-op
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
echo "=== [7/8] Waiting for backend healthy ===" echo "=== [7/8] Starting all services ==="
docker compose up -d --remove-orphans
# Wait for backend healthy
echo " Waiting for backend healthy..."
for i in $(seq 1 20); do for i in $(seq 1 20); do
if curl -sf http://localhost:8001/health >/dev/null 2>&1; then if curl -sf http://localhost:8001/health >/dev/null 2>&1; then
echo " Backend healthy after $((i * 5))s" echo " Backend healthy after $((i * 5))s"
break break
fi fi
if [ "$i" -eq 20 ]; then if [ "$i" -eq 20 ]; then
echo " Backend not healthy after 100s" echo " FATAL: Backend not healthy after 100s"
echo " ⚠️ Rolling back database..." echo " Rolling back database..."
gunzip -c "$BACKUP_FILE" | docker compose exec -T postgres psql -U translate -d translate_db >/dev/null 2>&1 || true gunzip -c "$BACKUP_FILE" | docker compose exec -T postgres psql -U translate -d translate_db >/dev/null 2>&1 || true
docker compose restart backend docker compose restart backend
echo " Deploy failed. Database restored." echo " Deploy FAILED. Database restored."
docker compose logs backend --tail=50 docker compose logs backend --tail=50
exit 1 exit 1
fi fi

View File

@@ -1,6 +1,12 @@
#!/bin/bash #!/bin/bash
set -e set -e
# If a command is passed (e.g. `docker compose run --rm backend alembic upgrade head`),
# run it directly instead of the full startup sequence.
if [ $# -gt 0 ]; then
exec "$@"
fi
echo "🚀 Starting Document Translation API..." echo "🚀 Starting Document Translation API..."
# Wait for database to be ready (if DATABASE_URL is set) # Wait for database to be ready (if DATABASE_URL is set)
@@ -42,7 +48,7 @@ except:
# Run database migrations # Run database migrations
echo "📦 Running database migrations..." echo "📦 Running database migrations..."
alembic upgrade head || echo "⚠️ Migration skipped (may already be up to date)" alembic upgrade head
fi fi
# Wait for Redis if configured # Wait for Redis if configured