Handles initial deploy and updates: git pull via SSH, docker compose rebuild, health check with timeout, pre-flight .env validation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
96 lines
4.8 KiB
Bash
96 lines
4.8 KiB
Bash
#!/usr/bin/env bash
|
|
# deploy.sh — idempotent deploy script for hp-prod-tracker
|
|
# Usage: bash deploy.sh [--skip-pull]
|
|
#
|
|
# Works for both initial deployment and updates.
|
|
# Run from /opt/hp-prod-tracker as any user with docker + git access.
|
|
|
|
set -euo pipefail
|
|
|
|
# ─── Config ──────────────────────────────────────────────────────────────────
|
|
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
COMPOSE_FILE="$APP_DIR/docker-compose.yml"
|
|
HEALTH_URL="http://localhost:3001/api/health"
|
|
HEALTH_RETRIES=30
|
|
HEALTH_INTERVAL=3 # seconds between retries
|
|
|
|
# ─── Colours ─────────────────────────────────────────────────────────────────
|
|
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
|
|
log() { echo -e "${GREEN}[deploy]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[deploy]${NC} $*"; }
|
|
fail() { echo -e "${RED}[deploy]${NC} $*" >&2; exit 1; }
|
|
|
|
# ─── Args ─────────────────────────────────────────────────────────────────────
|
|
SKIP_PULL=false
|
|
for arg in "$@"; do
|
|
[[ "$arg" == "--skip-pull" ]] && SKIP_PULL=true
|
|
done
|
|
|
|
cd "$APP_DIR"
|
|
|
|
# ─── Pre-flight checks ───────────────────────────────────────────────────────
|
|
log "Running pre-flight checks..."
|
|
|
|
command -v docker >/dev/null 2>&1 || fail "docker is not installed"
|
|
docker compose version >/dev/null 2>&1 || fail "docker compose plugin is not installed"
|
|
command -v git >/dev/null 2>&1 || fail "git is not installed"
|
|
|
|
[[ -f "$APP_DIR/.env" ]] || fail ".env file not found at $APP_DIR/.env — copy .env.example and fill in the values"
|
|
|
|
# Warn if critical vars are empty
|
|
for var in AUTH_SECRET AUTH_MICROSOFT_ENTRA_ID_ID AUTH_MICROSOFT_ENTRA_ID_SECRET AUTH_MICROSOFT_ENTRA_ID_TENANT_ID AUTH_URL; do
|
|
val=$(grep -E "^${var}=" "$APP_DIR/.env" | cut -d= -f2- | tr -d '"' || true)
|
|
[[ -z "$val" ]] && warn "WARNING: $var is not set in .env"
|
|
done
|
|
|
|
# ─── Pull latest code ────────────────────────────────────────────────────────
|
|
if [[ "$SKIP_PULL" == false ]]; then
|
|
log "Pulling latest code..."
|
|
# Ensure remote uses SSH (avoids HTTPS credential prompts)
|
|
current_remote=$(git remote get-url origin 2>/dev/null || true)
|
|
if [[ "$current_remote" == https://* ]]; then
|
|
ssh_remote=$(echo "$current_remote" \
|
|
| sed 's|https://bitbucket.org/|git@bitbucket.org:|' \
|
|
| sed 's|\.git$||')
|
|
git remote set-url origin "${ssh_remote}.git"
|
|
log "Switched remote to SSH: $(git remote get-url origin)"
|
|
fi
|
|
|
|
git fetch origin
|
|
LOCAL=$(git rev-parse HEAD)
|
|
REMOTE=$(git rev-parse @{u} 2>/dev/null || echo "")
|
|
if [[ "$LOCAL" == "$REMOTE" ]]; then
|
|
log "Already up to date ($(git rev-parse --short HEAD))"
|
|
else
|
|
git pull --ff-only || fail "git pull failed — resolve conflicts manually then re-run"
|
|
log "Updated to $(git rev-parse --short HEAD)"
|
|
fi
|
|
else
|
|
warn "Skipping git pull (--skip-pull)"
|
|
fi
|
|
|
|
# ─── Deploy ──────────────────────────────────────────────────────────────────
|
|
log "Stopping existing containers..."
|
|
docker compose -f "$COMPOSE_FILE" down --remove-orphans
|
|
|
|
log "Building and starting containers (this may take a few minutes)..."
|
|
docker compose -f "$COMPOSE_FILE" up -d --build
|
|
|
|
# ─── Health check ────────────────────────────────────────────────────────────
|
|
log "Waiting for app to be healthy..."
|
|
attempt=0
|
|
until curl -sf "$HEALTH_URL" >/dev/null 2>&1; do
|
|
attempt=$((attempt + 1))
|
|
if [[ $attempt -ge $HEALTH_RETRIES ]]; then
|
|
fail "App did not become healthy after $((HEALTH_RETRIES * HEALTH_INTERVAL))s — check logs:\n docker compose logs app --tail 50"
|
|
fi
|
|
echo -n "."
|
|
sleep $HEALTH_INTERVAL
|
|
done
|
|
echo ""
|
|
|
|
# ─── Done ────────────────────────────────────────────────────────────────────
|
|
log "Deploy complete!"
|
|
log " Commit : $(git rev-parse --short HEAD) — $(git log -1 --pretty=%s)"
|
|
log " App : https://optical-dev.oliver.solutions/hp-prod-tracker"
|
|
log " Logs : docker compose logs -f app"
|