- deploy.sh dev|prod with --dry-run, auto-rollback if /health fails
within 60s; checkpoint saved to .last_deploy_rollback before reset
- deploy/rollback.sh last|<sha> with the same Docker compose dance
- deploy/health-check.sh — curl wrapper for monitoring/oncall
- deploy/apache-{dev,prod}.conf — Location blocks proxying /hm-aiqc/
to gunicorn on 127.0.0.1:5050 with X-Script-Name set so wsgi.py's
ReverseProxied middleware emits prefixed URLs
- deploy/.env.{dev,prod}.example — starter envs with Azure SSO config
75 lines
2.1 KiB
Bash
Executable file
75 lines
2.1 KiB
Bash
Executable file
#!/bin/bash
|
|
# Emergency rollback for HM AI QC.
|
|
#
|
|
# Usage:
|
|
# rollback.sh last Roll back to the checkpoint saved by deploy.sh
|
|
# rollback.sh <commit-hash> Roll back to an explicit commit
|
|
#
|
|
# Note: Alembic downgrade is intentionally NOT run here — schema rollbacks
|
|
# are risky on data-bearing tables. If the bad deploy added a column the
|
|
# rolled-back code doesn't know about, that's almost always fine. If it
|
|
# dropped or renamed a column, restore from the daily DB backup.
|
|
|
|
set -euo pipefail
|
|
|
|
APP_DIR=${APP_DIR:-/opt/hm-aiqc}
|
|
HEALTH_URL=${HEALTH_URL:-http://127.0.0.1:5050/health}
|
|
ROLLBACK_FILE="$APP_DIR/.last_deploy_rollback"
|
|
|
|
TARGET=${1:-}
|
|
|
|
if [[ -z "$TARGET" || "$TARGET" == "last" ]]; then
|
|
if [[ ! -f "$ROLLBACK_FILE" ]]; then
|
|
echo "No .last_deploy_rollback file. Specify a commit hash explicitly."
|
|
echo "Usage: $(basename "$0") last | <commit-hash>"
|
|
exit 1
|
|
fi
|
|
TARGET=$(cat "$ROLLBACK_FILE")
|
|
fi
|
|
|
|
cd "$APP_DIR"
|
|
|
|
if ! git rev-parse --verify --quiet "$TARGET^{commit}" > /dev/null; then
|
|
echo "ERROR: Commit '$TARGET' not found"
|
|
exit 1
|
|
fi
|
|
|
|
CURRENT_REV=$(git rev-parse HEAD)
|
|
CURRENT_SHORT=$(git rev-parse --short HEAD)
|
|
TARGET_REV=$(git rev-parse "$TARGET")
|
|
TARGET_SHORT=$(git rev-parse --short "$TARGET")
|
|
|
|
if [[ "$CURRENT_REV" == "$TARGET_REV" ]]; then
|
|
echo "Already at $TARGET_SHORT — nothing to do."
|
|
exit 0
|
|
fi
|
|
|
|
echo "============================================"
|
|
echo " HM AI QC rollback"
|
|
echo "============================================"
|
|
echo "Current: $CURRENT_SHORT $(git log -1 --format='%s' HEAD)"
|
|
echo "Target: $TARGET_SHORT $(git log -1 --format='%s' "$TARGET")"
|
|
echo ""
|
|
|
|
read -r -p "Proceed? (y/N): " confirm
|
|
if [[ ! $confirm =~ ^[Yy]$ ]]; then
|
|
echo "Cancelled."
|
|
exit 0
|
|
fi
|
|
|
|
git reset --hard "$TARGET_REV"
|
|
docker compose build
|
|
docker compose up -d
|
|
|
|
# 60s window — same as deploy.sh
|
|
for i in {1..30}; do
|
|
sleep 2
|
|
if curl -sf -o /dev/null "$HEALTH_URL"; then
|
|
echo "Rollback OK. Now at $TARGET_SHORT."
|
|
exit 0
|
|
fi
|
|
done
|
|
|
|
echo "Service unhealthy after rollback."
|
|
echo "docker compose logs --tail=200 web"
|
|
exit 1
|