#!/usr/bin/env bash # ═══════════════════════════════════════════════════════ # deploy.sh — Jool International · déploiement production # Usage : ./deploy.sh # Prérequis : docker, docker compose, git, accès sudo # ═══════════════════════════════════════════════════════ set -euo pipefail # ── Couleurs pour la lisibilité ──────────────────────── RESET="\033[0m" BOLD="\033[1m" GREEN="\033[32m" YELLOW="\033[33m" RED="\033[31m" CYAN="\033[36m" log() { echo -e "${CYAN}[DEPLOY]${RESET} $*"; } ok() { echo -e "${GREEN}[OK]${RESET} $*"; } warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; } error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; } title() { echo -e "\n${BOLD}━━━ $* ━━━${RESET}"; } # ── Variables ────────────────────────────────────────── COMPOSE_FILE="docker-compose.prod.yml" ENV_FILE=".env.prod" APP_SERVICE="web" BRANCH="${DEPLOY_BRANCH:-main}" # ── Vérifications préalables ─────────────────────────── title "Vérifications" command -v docker >/dev/null 2>&1 || error "docker non trouvé" command -v git >/dev/null 2>&1 || error "git non trouvé" [[ -f "$ENV_FILE" ]] || error "Fichier $ENV_FILE introuvable — créer d'abord .env.prod" [[ -f "$COMPOSE_FILE" ]] || error "Fichier $COMPOSE_FILE introuvable" [[ -f "Caddyfile" ]] || error "Fichier Caddyfile introuvable" ok "Docker, git, .env.prod et Caddyfile présents" # ── Pull du code ─────────────────────────────────────── title "Récupération du code" git fetch origin "$BRANCH" LOCAL=$(git rev-parse HEAD) REMOTE=$(git rev-parse "origin/$BRANCH") if [[ "$LOCAL" == "$REMOTE" ]]; then warn "Déjà à jour ($(git rev-parse --short HEAD)) — rebuild quand même" else log "Mise à jour : $(git rev-parse --short HEAD) → $(git rev-parse --short "origin/$BRANCH")" git pull --ff-only origin "$BRANCH" fi ok "Code à jour : $(git rev-parse --short HEAD)" # ── Build de l'image ─────────────────────────────────── title "Build Docker" docker compose -f "$COMPOSE_FILE" build --no-cache "$APP_SERVICE" ok "Image $APP_SERVICE construite" # ── Vérification de la config Caddy ──────────────────── title "Validation Caddyfile" docker run --rm -v "$(pwd)/Caddyfile:/etc/caddy/Caddyfile:ro" \ caddy:2-alpine caddy validate --config /etc/caddy/Caddyfile \ && ok "Caddyfile valide" \ || error "Caddyfile invalide — déploiement annulé" # ── Déploiement sans interruption ───────────────────── title "Déploiement" docker compose -f "$COMPOSE_FILE" up -d --remove-orphans ok "Conteneurs démarrés" # ── Attente de la santé du service web ───────────────── title "Healthcheck" MAX_WAIT=60 ELAPSED=0 log "En attente que '$APP_SERVICE' soit healthy (max ${MAX_WAIT}s)…" until docker compose -f "$COMPOSE_FILE" ps "$APP_SERVICE" | grep -q "healthy"; do if [[ $ELAPSED -ge $MAX_WAIT ]]; then warn "Healthcheck timeout — vérifier : docker compose logs $APP_SERVICE" break fi sleep 3 ELAPSED=$((ELAPSED + 3)) done ok "$APP_SERVICE opérationnel (${ELAPSED}s)" # ── Nettoyage des images orphelines ──────────────────── title "Nettoyage" docker image prune -f --filter "until=24h" >/dev/null 2>&1 || true ok "Images inutilisées supprimées" # ── Résumé ───────────────────────────────────────────── title "Déploiement terminé" echo -e " Commit : ${BOLD}$(git rev-parse --short HEAD)${RESET}" echo -e " Site : ${BOLD}https://jool-international.com/${RESET}" echo -e " Logs : docker compose logs -f" echo -e " Rollback : git checkout && ./deploy.sh"