feat: production CI/CD with DB backup and auto-rollback
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2s
8-step pipeline: 1. Git pull 2. pg_dumpall + gzip backup (keep last 10) 3. Build images (with cache) 4. Start services 5. Wait for postgres 6. Run alembic migration (one-shot container) - On failure: auto-restore DB from backup 7. Health check backend - On failure: auto-rollback DB + restart 8. Summary with backup path Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -24,54 +24,106 @@ jobs:
|
|||||||
set -e
|
set -e
|
||||||
cd /opt/wordly
|
cd /opt/wordly
|
||||||
|
|
||||||
echo "=== Git pull ==="
|
# ──────────────────────────────────────────────
|
||||||
|
# 1. GIT PULL
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
echo "=== [1/8] Git pull ==="
|
||||||
git config --global --add safe.directory /opt/wordly
|
git config --global --add safe.directory /opt/wordly
|
||||||
git fetch origin production-deployment
|
git fetch origin production-deployment
|
||||||
git reset --hard origin/production-deployment
|
git reset --hard origin/production-deployment
|
||||||
|
|
||||||
echo "=== Building images ==="
|
# ──────────────────────────────────────────────
|
||||||
|
# 2. DATABASE BACKUP (before anything else)
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
echo "=== [2/8] Database backup ==="
|
||||||
|
BACKUP_DIR="/opt/backups/postgres"
|
||||||
|
BACKUP_FILE="${BACKUP_DIR}/translate_db_$(date +%Y%m%d_%H%M%S).sql.gz"
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
docker compose exec -T postgres pg_dumpall -U translate | gzip > "$BACKUP_FILE"
|
||||||
|
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||||
|
echo " ✅ Backup saved: ${BACKUP_FILE} (${BACKUP_SIZE})"
|
||||||
|
|
||||||
|
# Keep only last 10 backups
|
||||||
|
ls -t "${BACKUP_DIR}"/translate_db_*.sql.gz | tail -n +11 | xargs -r rm --
|
||||||
|
echo " 📦 Retained last 10 backups"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# 3. BUILD IMAGES
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
echo "=== [3/8] Building images ==="
|
||||||
docker compose build backend frontend
|
docker compose build backend frontend
|
||||||
|
|
||||||
echo "=== Starting services ==="
|
# ──────────────────────────────────────────────
|
||||||
|
# 4. START SERVICES
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
echo "=== [4/8] Starting services ==="
|
||||||
docker compose up -d --remove-orphans
|
docker compose up -d --remove-orphans
|
||||||
|
|
||||||
echo "=== Waiting for postgres ==="
|
# ──────────────────────────────────────────────
|
||||||
|
# 5. WAIT 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 "ERROR: Postgres not ready after 60s"
|
echo " ❌ Postgres not ready after 60s"
|
||||||
docker compose logs postgres --tail=30
|
docker compose logs postgres --tail=30
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "=== Running database migrations (one-shot container) ==="
|
# ──────────────────────────────────────────────
|
||||||
docker compose run --rm backend alembic upgrade head
|
# 6. RUN MIGRATIONS (one-shot container)
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
echo "=== [6/8] Running database migrations ==="
|
||||||
|
if ! docker compose run --rm backend alembic upgrade head; then
|
||||||
|
echo " ❌ Migration FAILED!"
|
||||||
|
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
|
||||||
|
echo " 🔄 Database restored. Restarting services..."
|
||||||
|
docker compose restart backend
|
||||||
|
echo " ❌ Deploy aborted. Database restored from backup."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✅ Migrations applied"
|
||||||
|
|
||||||
echo "=== Restarting backend to pick up migration ==="
|
# Restart backend to pick up new schema
|
||||||
docker compose restart backend
|
docker compose restart backend
|
||||||
|
|
||||||
echo "=== Waiting for backend healthy ==="
|
# ──────────────────────────────────────────────
|
||||||
|
# 7. HEALTH CHECK
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
echo "=== [7/8] 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 "ERROR: Backend not healthy after 100s"
|
echo " ❌ Backend not healthy after 100s"
|
||||||
|
echo " ⚠️ Rolling back database..."
|
||||||
|
gunzip -c "$BACKUP_FILE" | docker compose exec -T postgres psql -U translate -d translate_db >/dev/null 2>&1 || true
|
||||||
|
docker compose restart backend
|
||||||
|
echo " ❌ Deploy failed. Database restored."
|
||||||
docker compose logs backend --tail=50
|
docker compose logs backend --tail=50
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
sleep 5
|
sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "=== Deploy summary ==="
|
# ──────────────────────────────────────────────
|
||||||
docker compose ps
|
# 8. SUMMARY
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
echo "=== [8/8] Deploy summary ==="
|
||||||
|
docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
|
||||||
|
echo ""
|
||||||
echo "Health: $(curl -sf http://localhost:8001/health 2>/dev/null || echo 'FAILED')"
|
echo "Health: $(curl -sf http://localhost:8001/health 2>/dev/null || echo 'FAILED')"
|
||||||
|
echo "Backup: ${BACKUP_FILE} (${BACKUP_SIZE})"
|
||||||
ENDSSH
|
ENDSSH
|
||||||
|
|
||||||
- name: Wait for frontend
|
- name: Wait for frontend
|
||||||
|
|||||||
Reference in New Issue
Block a user