#!/bin/bash # Gravl deployment script # Prevents stale containers by always building fresh with --no-cache # # Usage: # ./scripts/deploy.sh # # What it does: # 1. Pulls latest code from git # 2. Captures build metadata (commit SHA, timestamp) # 3. Builds fresh Docker images with --no-cache (no layer caching) # 4. Restarts containers to use new images # 5. Polls /api/health endpoint until backend is ready # 6. Logs all steps to logs/deploy.log # # Rationale for --no-cache: # Docker caching can hide stale assets (JS, CSS, images) when source files change. # Using --no-cache ensures all layers rebuild fresh, guaranteeing new code is deployed. # Trade-off: Slightly slower builds (30-60s vs 10-20s with cache), but safer. # # See: /docs/DEPLOYMENT.md for troubleshooting set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_DIR="$(dirname "$SCRIPT_DIR")" LOG_FILE="$REPO_DIR/logs/deploy.log" BACKEND_HEALTH="http://localhost:3001/api/health" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" } mkdir -p "$REPO_DIR/logs" cd "$REPO_DIR" log "=== Deploy started ===" # Pull latest code from remote # Fails if there are local changes or merge conflicts log "Pulling latest code..." git pull # Capture build metadata to embed in Docker image labels # These labels allow build-check.sh to verify deployed containers match local code GIT_COMMIT=$(git rev-parse HEAD) BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") log "Commit: $(git rev-parse --short HEAD) | Date: $BUILD_DATE" # Build fresh images — no-cache prevents Docker layer caching # This is critical for frontend deployments where CSS/JS changes might not be obvious # to Docker's layer detection algorithm log "Building images (--no-cache)..." export GIT_COMMIT BUILD_DATE docker compose build --no-cache # Restart containers with new images # --force-recreate stops old containers and removes them before starting new ones log "Starting containers..." docker compose up -d --force-recreate # Health check: poll /api/health endpoint until it responds with 200 OK # Timeout: 60 seconds (12 retries × 5 seconds each) # This prevents deployment from completing if the backend is broken log "Health check..." for i in $(seq 1 12); do if curl -sf "$BACKEND_HEALTH" >/dev/null 2>&1; then log "Backend healthy" break fi [ "$i" -eq 12 ] && { log "ERROR: Health check failed after 60s"; exit 1; } log "Waiting... ($i/12)" sleep 5 done log "=== Deploy complete: ${GIT_COMMIT:0:7} ==="