#!/usr/bin/env bash # ============================================================================= # Imago — Script de déploiement automatisé # ============================================================================= # Usage : # ./scripts/deploy.sh # Premier déploiement # ./scripts/deploy.sh --update # Mise à jour (pull + recréation) # ./scripts/deploy.sh --status # Afficher l'état des conteneurs # ./scripts/deploy.sh --logs # Logs en temps réel # # Prérequis : Docker + Docker Compose installés (voir scripts/setup.sh) # ============================================================================= set -euo pipefail # ── Couleurs ────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' BLUE='\033[0;34m'; NC='\033[0m' info() { echo -e "${BLUE}[INFO]${NC} $*"; } ok() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } err() { echo -e "${RED}[ERROR]${NC} $*" >&2; } # ── Configuration ───────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" COMPOSE_FILE="${PROJECT_DIR}/docker-compose/docker-compose.prod.yml" ENV_FILE="${PROJECT_DIR}/.env" ENV_EXAMPLE="${PROJECT_DIR}/docker-compose/.env.example" SERVICE_NAME="imago" # ── Fonctions ───────────────────────────────────────────────── check_prerequisites() { info "Vérification des prérequis..." if ! command -v docker &>/dev/null; then err "Docker n'est pas installé. Lancez : scripts/setup.sh" exit 1 fi if ! docker compose version &>/dev/null; then err "Docker Compose (plugin) n'est pas disponible." exit 1 fi if ! docker info &>/dev/null; then err "Docker ne tourne pas ou vous n'avez pas les droits." err "Vérifiez : sudo systemctl start docker && sudo usermod -aG docker $USER" exit 1 fi ok "Docker $(docker --version | awk '{print $3}' | tr -d ',') — Docker Compose disponible" } setup_env() { if [[ -f "$ENV_FILE" ]]; then ok "Fichier .env existant" return fi warn "Fichier .env absent — création depuis le template..." if [[ ! -f "$ENV_EXAMPLE" ]]; then err "Template introuvable : $ENV_EXAMPLE" exit 1 fi cp "$ENV_EXAMPLE" "$ENV_FILE" warn "╔══════════════════════════════════════════════════════════╗" warn "║ Fichier .env créé. ÉDITEZ-LE avant de continuer : ║" warn "║ nano ${ENV_FILE} ║" warn "║ ║" warn "║ Variables OBLIGATOIRES à renseigner : ║" warn "║ SECRET_KEY — clé secrète aléatoire ║" warn "║ SIGNED_URL_SECRET — clé pour URLs signées ║" warn "║ OPENROUTER_API_KEY — clé API OpenRouter ║" warn "║ ADMIN_API_KEY — (optionnel) master key admin ║" warn "╚══════════════════════════════════════════════════════════╝" echo "" read -rp "Avez-vous édité le fichier .env ? (o/N) " confirm if [[ ! "$confirm" =~ ^[oO]$ ]]; then err "Déploiement annulé. Éditez .env puis relancez." exit 1 fi } create_directories() { info "Création des répertoires..." # Répertoire de backups local backup_dir="${BACKUP_DIR:-./backups}" if [[ ! -d "$PROJECT_DIR/$backup_dir" ]]; then mkdir -p "$PROJECT_DIR/$backup_dir" ok "Backups : $backup_dir" fi # Répertoires NFS si configurés local upload_vol="${UPLOAD_VOLUME:-}" if [[ -n "$upload_vol" ]] && [[ ! "$upload_vol" =~ ^[a-zA-Z0-9_-]+$ ]]; then mkdir -p "$upload_vol" 2>/dev/null || warn "Impossible de créer $upload_vol (NFS ?)" fi } pull_images() { info "Récupération des images Docker..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" pull --quiet 2>&1 | tail -3 ok "Images à jour" } start_services() { info "Démarrage des services..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --remove-orphans info "Attente de la santé des services..." local max_wait=60 local waited=0 while [[ $waited -lt $max_wait ]]; do local backend_ok=$(docker compose -f "$COMPOSE_FILE" ps backend --format json 2>/dev/null | grep -c '"Health":"healthy"' || echo 0) if [[ "$backend_ok" -ge 1 ]]; then ok "Backend healthy après ${waited}s" break fi sleep 3 waited=$((waited + 3)) echo -n "." done echo "" if [[ $waited -ge $max_wait ]]; then warn "Le backend tarde à démarrer (>${max_wait}s). Vérifiez les logs :" warn " docker compose -f $COMPOSE_FILE logs backend" fi } show_bootstrap_key() { echo "" info "Clé API bootstrap (admin) — si c'est un premier déploiement :" docker compose -f "$COMPOSE_FILE" logs backend 2>/dev/null | \ grep "API_KEY:" | tail -1 | \ sed 's/.*API_KEY: / /' | sed 's/".*//' || true } show_status() { echo "" info "État des services :" docker compose -f "$COMPOSE_FILE" ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" } show_urls() { echo "" ok "═══ Imago est prêt ═══" local admin_port="${ADMIN_PORT:-3001}" local backend_port="${BACKEND_PORT:-8001}" echo " Admin UI : http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):${admin_port}" echo " API Docs : http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):${backend_port}/docs" echo " Health : http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):${backend_port}/health" } restart_updated() { info "Mise à jour : pull + recréation des services modifiés..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" pull --quiet docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --remove-orphans ok "Services mis à jour" } # ── Point d'entrée ─────────────────────────────────────────── cd "$PROJECT_DIR" case "${1:-}" in --update|-u) check_prerequisites pull_images restart_updated show_status show_urls ;; --status|-s) show_status ;; --logs|-l) docker compose -f "$COMPOSE_FILE" logs -f --tail=50 ;; --help|-h) echo "Usage: $0 [--update|--status|--logs|--help]" echo "" echo " (sans argument) Premier déploiement complet" echo " --update, -u Mise à jour des containers" echo " --status, -s Afficher l'état" echo " --logs, -l Logs en temps réel" echo " --help, -h Cette aide" ;; *) check_prerequisites setup_env create_directories pull_images start_services show_bootstrap_key show_status show_urls ;; esac