33800 Docs

← Retour
#!/bin/bash # ═══════════════════════════════════════════════════════════════════════════ # SMART-DEPLOY.SH - Script de déploiement intelligent # Appelé par GitLab CI/CD, parse conf.gouroubleu.yml et déploie # # Usage: smart-deploy.sh # Variables attendues (définies par GitLab CI): # - CI_PROJECT_DIR : Répertoire du projet # - CI_PROJECT_NAME : Nom du projet # - CI_COMMIT_BRANCH : Branche # - CI_COMMIT_SHORT_SHA : Commit court # - DEPLOY_ENV : Environnement (prod, dev, staging) # - DEPLOY_CONFIG : Fichier de config (conf.prod.gouroubleu.yml) # ═══════════════════════════════════════════════════════════════════════════ set -euo pipefail # ─────────────────────────────────────────────────────────────────────────── # ERROR HANDLER # ─────────────────────────────────────────────────────────────────────────── on_error() { local exit_code=$? local line_no=$1 echo "" echo "═══════════════════════════════════════════════════════════════════════════" echo " DEPLOY FAILED at line $line_no (exit code: $exit_code)" echo "═══════════════════════════════════════════════════════════════════════════" # Notifier l'échec si possible if type notify_deploy_failure &>/dev/null 2>&1; then notify_deploy_failure "${CFG_name:-unknown}" "${CFG_target:-unknown}" "Exit code $exit_code at line $line_no" fi # Mettre à jour le registre si possible if type registry_mark_failed &>/dev/null 2>&1; then registry_mark_failed "${CFG_name:-unknown}" "Exit code $exit_code at line $line_no" fi exit $exit_code } trap 'on_error $LINENO' ERR # ─────────────────────────────────────────────────────────────────────────── # PATHS (auto-detect based on host) # ─────────────────────────────────────────────────────────────────────────── # Détecter le chemin du stockage selon la machine if [[ -d "/stock_8to/33800-stack" ]]; then # PVE (accès direct ZFS) STOCK_BASE="/stock_8to/33800-stack" elif [[ -d "/mnt/stock_8to/33800-stack" ]]; then # prod-portainer (NFS mount) STOCK_BASE="/mnt/stock_8to/33800-stack" else echo "ERROR: Cannot find 33800-stack directory" exit 1 fi SCRIPTS_DIR="${STOCK_BASE}/scripts/deploy" REGISTRY_DIR="${STOCK_BASE}/monitoring" # ─────────────────────────────────────────────────────────────────────────── # LOAD LIBS # ─────────────────────────────────────────────────────────────────────────── source "${SCRIPTS_DIR}/lib/common.sh" source "${SCRIPTS_DIR}/lib/parse-yaml.sh" source "${SCRIPTS_DIR}/lib/targets.sh" source "${SCRIPTS_DIR}/lib/services.sh" # Optionnel: Notifications, Registre et Health Check [[ -f "${SCRIPTS_DIR}/lib/notifications.sh" ]] && source "${SCRIPTS_DIR}/lib/notifications.sh" [[ -f "${SCRIPTS_DIR}/lib/registry.sh" ]] && source "${SCRIPTS_DIR}/lib/registry.sh" [[ -f "${SCRIPTS_DIR}/lib/health.sh" ]] && source "${SCRIPTS_DIR}/lib/health.sh" [[ -f "${SCRIPTS_DIR}/lib/hooks.sh" ]] && source "${SCRIPTS_DIR}/lib/hooks.sh" # Timer pour durée de déploiement DEPLOY_START_TIME=$(date +%s) # ─────────────────────────────────────────────────────────────────────────── # VARIABLES # ─────────────────────────────────────────────────────────────────────────── PROJECT_DIR="${CI_PROJECT_DIR:-.}" PROJECT_NAME="${CI_PROJECT_NAME:-$(basename "$PROJECT_DIR")}" BRANCH="${CI_COMMIT_BRANCH:-main}" COMMIT="${CI_COMMIT_SHORT_SHA:-$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')}" DEPLOY_ENV="${DEPLOY_ENV:-prod}" CONFIG_FILE="${DEPLOY_CONFIG:-conf.prod.gouroubleu.yml}" # ─────────────────────────────────────────────────────────────────────────── # BANNER # ─────────────────────────────────────────────────────────────────────────── echo "═══════════════════════════════════════════════════════════════════════════" echo " SMART-DEPLOY - Stack 33800" echo "═══════════════════════════════════════════════════════════════════════════" echo " Project: $PROJECT_NAME" echo " Branch: $BRANCH" echo " Commit: $COMMIT" echo " Env: $DEPLOY_ENV" echo " Config: $CONFIG_FILE" echo "═══════════════════════════════════════════════════════════════════════════" echo "" # ─────────────────────────────────────────────────────────────────────────── # 1. CHECK CONFIG FILE # ─────────────────────────────────────────────────────────────────────────── log_step "Vérification du fichier de configuration..." CONFIG_PATH="${PROJECT_DIR}/${CONFIG_FILE}" if [[ ! -f "$CONFIG_PATH" ]]; then log_warning "Fichier de config non trouvé: $CONFIG_FILE" log_info "Pas de déploiement automatique pour cette branche." log_info "Pour activer le déploiement, créer: $CONFIG_FILE" exit 0 fi log_success "Config trouvée: $CONFIG_FILE" # ─────────────────────────────────────────────────────────────────────────── # 2. PARSE CONFIG # ─────────────────────────────────────────────────────────────────────────── log_step "Parsing de la configuration..." parse_yaml "$CONFIG_PATH" "CFG_" # Parser les sections imbriquées parse_yaml_section "$CONFIG_PATH" "deploy" "CFG_" parse_yaml_section "$CONFIG_PATH" "nginx" "CFG_" parse_yaml_section "$CONFIG_PATH" "storage" "CFG_" parse_yaml_section "$CONFIG_PATH" "docker" "CFG_" # Parser les sidecars si présents if grep -q "^sidecars:" "$CONFIG_PATH"; then parse_yaml_sidecars "$CONFIG_PATH" fi # Parser les hooks si présents if grep -q "^hooks:" "$CONFIG_PATH"; then parse_yaml_hooks "$CONFIG_PATH" fi # Valider les sidecars (chaque sidecar doit avoir name et image) validate_sidecars() { for ((i=0; i<${SIDECARS_COUNT:-0}; i++)); do local sc_name_var="SIDECAR_${i}_name" local sc_image_var="SIDECAR_${i}_image" if [[ -z "${!sc_name_var:-}" ]]; then log_error "Sidecar #${i}: 'name' manquant" return 1 fi if [[ -z "${!sc_image_var:-}" ]]; then log_error "Sidecar #${i} (${!sc_name_var}): 'image' manquant" return 1 fi done return 0 } if [[ ${SIDECARS_COUNT:-0} -gt 0 ]]; then validate_sidecars || exit 1 log_info " Sidecars: ${SIDECARS_COUNT}" fi # Vérifier les champs obligatoires require_var CFG_name require_var CFG_target log_info " Name: ${CFG_name}" log_info " Target: ${CFG_target}" log_info " Type: ${CFG_type:-auto}" log_info " Port: ${CFG_port:-N/A}" log_info " Domain: ${CFG_nginx_domain:-N/A}" # ─────────────────────────────────────────────────────────────────────────── # 3. RESOLVE TARGET # ─────────────────────────────────────────────────────────────────────────── log_step "Résolution du target..." resolve_target "${CFG_target}" # Vérifier la connectivité si target distant if [[ "$TARGET_LOCAL" != "true" ]]; then check_target_connectivity "${CFG_target}" fi # ─────────────────────────────────────────────────────────────────────────── # 4. INJECT SERVICE VARIABLES # ─────────────────────────────────────────────────────────────────────────── log_step "Injection des variables de services..." inject_services "${PROJECT_DIR}/.env.deploy" # Injecter les variables personnalisées (section env: du yaml avec résolution @VAR) inject_custom_env "$CONFIG_FILE" "${PROJECT_DIR}/.env.deploy" # ─────────────────────────────────────────────────────────────────────────── # 5. DNS WILDCARD (si domain déclaré) # ─────────────────────────────────────────────────────────────────────────── if [[ -n "${CFG_nginx_domain:-}" ]]; then log_step "Vérification DNS..." if [[ -f "${SCRIPTS_DIR}/lib/dns.sh" ]]; then source "${SCRIPTS_DIR}/lib/dns.sh" ensure_dns_wildcard "${CFG_nginx_domain}" else log_warning "lib/dns.sh non trouvé, DNS non vérifié" fi fi # ─────────────────────────────────────────────────────────────────────────── # LOAD STRATEGY # ─────────────────────────────────────────────────────────────────────────── STRATEGY_FILE="${SCRIPTS_DIR}/strategies/${TARGET_TYPE}.sh" if [[ ! -f "$STRATEGY_FILE" ]]; then log_error "Stratégie non trouvée: $STRATEGY_FILE" log_info "Stratégies disponibles:" ls -1 "${SCRIPTS_DIR}/strategies/" | sed 's/\.sh$//' | sed 's/^/ - /' exit 1 fi source "$STRATEGY_FILE" # ─────────────────────────────────────────────────────────────────────────── # HELPER FUNCTIONS (multi-stage) # ─────────────────────────────────────────────────────────────────────────── # Écrire le fichier .deploy-info.env (artifact pour les stages suivants) write_deploy_info() { local info_file="${PROJECT_DIR}/.deploy-info.env" cat > "$info_file" << EOF BUILT_IMAGE_FULL=${BUILT_IMAGE_FULL:-} BUILT_IMAGE_LATEST=${BUILT_IMAGE_LATEST:-} CFG_NAME=${CFG_name} CFG_TARGET=${CFG_target} CFG_PORT=${CFG_port:-} CFG_DEPLOY_STRATEGY=${CFG_deploy_strategy:-simple} CFG_DEPLOY_GREEN_PORT=${CFG_deploy_green_port:-} TARGET_HOST=${TARGET_HOST:-} TARGET_USER=${TARGET_USER:-gouroubleu} TARGET_TYPE=${TARGET_TYPE} TARGET_LOCAL=${TARGET_LOCAL:-false} TARGET_PATH=${TARGET_PATH:-} COMMIT=${COMMIT} DEPLOY_ENV=${DEPLOY_ENV} EOF log_success "Deploy info écrit: $info_file" } # Charger les infos depuis .deploy-info.env (artifact du stage build) load_deploy_info() { local info_file="${PROJECT_DIR}/.deploy-info.env" if [[ -f "$info_file" ]]; then source "$info_file" log_info "Deploy info chargé: IMAGE=${BUILT_IMAGE_FULL:-N/A}" else log_warning "Pas de .deploy-info.env trouvé, utilisation des valeurs par défaut" fi } # Exécuter les tests si configurés run_tests() { local test_cmd="${CFG_deploy_test_command:-}" if [[ -z "$test_cmd" ]]; then log_info "Pas de test_command configuré, skip" return 0 fi log_step "Exécution des tests: ${test_cmd}" # Charger les infos du build pour l'image load_deploy_info if [[ -n "${BUILT_IMAGE_FULL:-}" ]]; then # Exécuter dans le container Docker log_info "Tests dans le container: ${BUILT_IMAGE_FULL}" docker run --rm "${BUILT_IMAGE_FULL}" ${test_cmd} else # Exécuter localement log_info "Tests locaux" cd "$PROJECT_DIR" eval "$test_cmd" fi log_success "Tests OK" } # ─────────────────────────────────────────────────────────────────────────── # DEPLOY_STAGE ROUTING # ─────────────────────────────────────────────────────────────────────────── # Si DEPLOY_STAGE est défini, on exécute seulement la phase correspondante. # Sinon (mode "all"), on exécute tout d'un bloc (rétrocompat). # ─────────────────────────────────────────────────────────────────────────── case "${DEPLOY_STAGE:-all}" in # ═══════════════════════════════════════════════════════════════════════════ # STAGE: build - Build image Docker uniquement # ═══════════════════════════════════════════════════════════════════════════ build) log_step "=== STAGE: BUILD ===" if [[ "$TARGET_TYPE" == "docker" ]]; then build_docker else log_info "Target type ${TARGET_TYPE}: pas de build Docker" fi # Écrire l'artifact pour les stages suivants write_deploy_info ;; # ═══════════════════════════════════════════════════════════════════════════ # STAGE: test - Exécuter les tests si configurés # ═══════════════════════════════════════════════════════════════════════════ test) log_step "=== STAGE: TEST ===" run_tests ;; # ═══════════════════════════════════════════════════════════════════════════ # STAGE: push - Push image vers le registry # ═══════════════════════════════════════════════════════════════════════════ push) log_step "=== STAGE: PUSH ===" if [[ "$TARGET_TYPE" == "docker" ]]; then # Charger les infos du build stage load_deploy_info push_docker else log_info "Target type ${TARGET_TYPE}: pas de push Docker" fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STAGE: deploy - Déploiement sur la cible + registry + nginx init # ═══════════════════════════════════════════════════════════════════════════ deploy) log_step "=== STAGE: DEPLOY ===" # Charger les infos du build stage load_deploy_info # Pre-deploy hooks if type -t run_hooks &>/dev/null; then run_hooks "pre_deploy" || exit 1 fi # Déploiement selon type et stratégie case "$TARGET_TYPE" in docker) deploy_only_docker ;; files) deploy_files ;; remote) deploy_remote ;; *) log_error "Type de déploiement inconnu: $TARGET_TYPE" exit 1 ;; esac # Post-deploy hooks if type -t run_hooks &>/dev/null; then run_hooks "post_deploy" || true fi # Registry update log_step "Mise à jour du registre..." if type registry_update_service &>/dev/null; then registry_update_service "${CFG_name}" "${CFG_target}" "${TARGET_TYPE}" "${COMMIT}" "running" fi # Nginx config (seulement si première fois - blue-green gère le switch dans deploy_bluegreen) STAGE_STRATEGY="${CFG_deploy_strategy:-simple}" if [[ -n "${CFG_nginx_domain:-}" ]] && [[ "$TARGET_TYPE" == "docker" ]] && [[ "$STAGE_STRATEGY" != "blue-green" ]] && [[ "$STAGE_STRATEGY" != "bluegreen" ]]; then if [[ -f "${SCRIPTS_DIR}/lib/nginx.sh" ]]; then source "${SCRIPTS_DIR}/lib/nginx.sh" generate_and_deploy_nginx_config fi fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STAGE: healthcheck - Vérification post-deploy + notification # ═══════════════════════════════════════════════════════════════════════════ healthcheck) log_step "=== STAGE: HEALTHCHECK ===" HEALTH_ENDPOINT="${CFG_health:-/health}" CONTAINER_NAME="${CFG_container_name:-${CFG_name}}" # Pour blue-green : déterminer le port actif depuis le state file HC_CHECK_PORT="${CFG_port}" STAGE_STRATEGY="${CFG_deploy_strategy:-simple}" if [[ "$STAGE_STRATEGY" == "blue-green" || "$STAGE_STRATEGY" == "bluegreen" ]]; then source "$STRATEGY_FILE" read_deploy_state HC_CHECK_PORT="${BG_ACTIVE_PORT}" CONTAINER_NAME="${CFG_container_name:-${CFG_name}}-${BG_ACTIVE_COLOR}" log_info "Blue-green: vérification ${BG_ACTIVE_COLOR}:${HC_CHECK_PORT}" fi if [[ -n "${check_port:-}" ]] && type health_check &>/dev/null; then # 9a. Container Running if [[ "$TARGET_TYPE" == "docker" ]]; then log_step "9a. Container Check..." if ! check_container_running "$CONTAINER_NAME" "$TARGET_HOST" "${TARGET_USER:-gouroubleu}"; then log_error "Container ${CONTAINER_NAME} is not running" DEPLOY_STATUS="failed" if type registry_mark_failed &>/dev/null; then registry_mark_failed "${CFG_name}" "Container not running after deploy" fi if type notify_deploy_failure &>/dev/null; then notify_deploy_failure "${CFG_name}" "${CFG_target}" "Container not running" fi exit 1 fi fi # 9b. Port Readiness log_step "9b. Port Readiness..." if ! check_port_ready "${HC_CHECK_PORT}" "$TARGET_HOST" "${TARGET_USER:-gouroubleu}"; then log_error "Port ${HC_CHECK_PORT} not ready - initiating rollback" if type notify_health_failure &>/dev/null; then notify_health_failure "${CFG_name}" "${CFG_target}" "Port ${HC_CHECK_PORT} not ready" fi # Rollback blue-green if [[ "$STAGE_STRATEGY" == "blue-green" || "$STAGE_STRATEGY" == "bluegreen" ]]; then if [[ -f "${SCRIPTS_DIR}/lib/nginx.sh" ]]; then source "${SCRIPTS_DIR}/lib/nginx.sh" ROLLBACK_DOMAIN=$(resolve_domain) rollback_nginx_upstream "${CFG_name}" "$BG_ACTIVE_PORT" "$ROLLBACK_DOMAIN" fi else case "$TARGET_TYPE" in docker) rollback_docker "${CFG_name}" "${CONFIG_PATH}" ;; files) rollback_files "${CFG_name}" "${CONFIG_PATH}" ;; remote) rollback_remote "${CFG_name}" "${CONFIG_PATH}" ;; esac fi DEPLOY_STATUS="rolled_back" fi # 9c. HTTP Health (interne) if [[ "${DEPLOY_STATUS:-success}" != "rolled_back" ]]; then log_step "9c. Health Check (internal)..." INTERNAL_URL="http://${TARGET_HOST}:${HC_CHECK_PORT}${HEALTH_ENDPOINT}" log_info "URL: $INTERNAL_URL" check_http_health "$INTERNAL_URL" "${CFG_name}" HTTP_RESULT=$? if [[ $HTTP_RESULT -ne 0 ]]; then log_error "Internal health check failed - initiating rollback" if type notify_health_failure &>/dev/null; then notify_health_failure "${CFG_name}" "${CFG_target}" "$HEALTH_ENDPOINT" fi if [[ "$STAGE_STRATEGY" == "blue-green" || "$STAGE_STRATEGY" == "bluegreen" ]]; then if [[ -f "${SCRIPTS_DIR}/lib/nginx.sh" ]]; then source "${SCRIPTS_DIR}/lib/nginx.sh" ROLLBACK_DOMAIN=$(resolve_domain) rollback_nginx_upstream "${CFG_name}" "$BG_ACTIVE_PORT" "$ROLLBACK_DOMAIN" fi else case "$TARGET_TYPE" in docker) rollback_docker "${CFG_name}" "${CONFIG_PATH}" ;; files) rollback_files "${CFG_name}" "${CONFIG_PATH}" ;; remote) rollback_remote "${CFG_name}" "${CONFIG_PATH}" ;; esac fi DEPLOY_STATUS="rolled_back" fi fi # 9d. HTTPS External (si domain) if [[ "${DEPLOY_STATUS:-success}" != "rolled_back" ]] && [[ -n "${CFG_nginx_domain:-}" ]]; then log_step "9d. Health Check (external HTTPS)..." EXTERNAL_URL="https://${CFG_nginx_domain}${HEALTH_ENDPOINT}" log_info "URL: $EXTERNAL_URL" if ! check_https_external "$EXTERNAL_URL" "${CFG_name}"; then log_warning "External HTTPS check failed (nginx/SSL issue, no rollback)" fi fi else log_info "Health check ignoré (pas de port ou lib/health.sh non chargé)" fi # Notification DEPLOY_END_TIME=$(date +%s) DEPLOY_DURATION=$((DEPLOY_END_TIME - DEPLOY_START_TIME)) if [[ "${DEPLOY_STATUS:-success}" == "rolled_back" ]]; then if type notify_deploy_failure &>/dev/null; then notify_deploy_failure "${CFG_name}" "${CFG_target}" "Health check failed, rolled back" fi else if type notify_deploy_success &>/dev/null; then notify_deploy_success "${CFG_name}" "${CFG_target}" "${COMMIT}" "${DEPLOY_DURATION}" fi fi ;; # ═══════════════════════════════════════════════════════════════════════════ # MODE: all - Rétrocompatibilité (comportement monolithique actuel) # ═══════════════════════════════════════════════════════════════════════════ all) log_step "Déploiement complet (stratégie: $TARGET_TYPE)..." # 6. Deploy case "$TARGET_TYPE" in docker) deploy_docker ;; files) deploy_files ;; remote) deploy_remote ;; *) log_error "Type de déploiement inconnu: $TARGET_TYPE" exit 1 ;; esac # 7. Registry log_step "Mise à jour du registre..." if type registry_update_service &>/dev/null; then registry_update_service "${CFG_name}" "${CFG_target}" "${TARGET_TYPE}" "${COMMIT}" "running" else log_warning "Fonction registry_update_service non disponible" fi # 8. Nginx if [[ -n "${CFG_nginx_domain:-}" ]] && [[ "$TARGET_TYPE" == "docker" ]]; then log_step "Configuration Nginx..." if [[ -f "${SCRIPTS_DIR}/lib/nginx.sh" ]]; then source "${SCRIPTS_DIR}/lib/nginx.sh" generate_and_deploy_nginx_config else log_warning "lib/nginx.sh non trouvé, Nginx non configuré" fi fi # 9. Health checks HEALTH_ENDPOINT="${CFG_health:-/health}" CONTAINER_NAME="${CFG_container_name:-${CFG_name}}" if [[ -n "${CFG_port:-}" ]] && type health_check &>/dev/null; then if [[ "$TARGET_TYPE" == "docker" ]]; then log_step "9a. Container Check..." if ! check_container_running "$CONTAINER_NAME" "$TARGET_HOST" "${TARGET_USER:-gouroubleu}"; then log_error "Container ${CONTAINER_NAME} is not running" DEPLOY_STATUS="failed" if type registry_mark_failed &>/dev/null; then registry_mark_failed "${CFG_name}" "Container not running after deploy" fi if type notify_deploy_failure &>/dev/null; then notify_deploy_failure "${CFG_name}" "${CFG_target}" "Container not running" fi exit 1 fi fi log_step "9b. Port Readiness..." if ! check_port_ready "${CFG_port}" "$TARGET_HOST" "${TARGET_USER:-gouroubleu}"; then log_error "Port ${CFG_port} not ready - initiating rollback" if type notify_health_failure &>/dev/null; then notify_health_failure "${CFG_name}" "${CFG_target}" "Port ${CFG_port} not ready" fi case "$TARGET_TYPE" in docker) rollback_docker "${CFG_name}" "${CONFIG_PATH}" ;; files) rollback_files "${CFG_name}" "${CONFIG_PATH}" ;; remote) rollback_remote "${CFG_name}" "${CONFIG_PATH}" ;; esac if [[ $? -eq 0 ]]; then log_warning "Rollback completed (port not ready)" if type notify_rollback &>/dev/null; then notify_rollback "${CFG_name}" "auto" fi DEPLOY_STATUS="rolled_back" else log_error "Rollback failed!" exit 1 fi fi if [[ "${DEPLOY_STATUS:-success}" != "rolled_back" ]]; then log_step "9c. Health Check (internal)..." INTERNAL_URL="http://${TARGET_HOST}:${CFG_port}${HEALTH_ENDPOINT}" log_info "URL: $INTERNAL_URL" check_http_health "$INTERNAL_URL" "${CFG_name}" HTTP_RESULT=$? if [[ $HTTP_RESULT -ne 0 ]]; then log_error "Internal health check failed - initiating rollback" if type notify_health_failure &>/dev/null; then notify_health_failure "${CFG_name}" "${CFG_target}" "$HEALTH_ENDPOINT" fi case "$TARGET_TYPE" in docker) rollback_docker "${CFG_name}" "${CONFIG_PATH}" ;; files) rollback_files "${CFG_name}" "${CONFIG_PATH}" ;; remote) rollback_remote "${CFG_name}" "${CONFIG_PATH}" ;; esac if [[ $? -eq 0 ]]; then log_warning "Rollback completed (health check failed)" if type notify_rollback &>/dev/null; then notify_rollback "${CFG_name}" "auto" fi sleep 5 check_http_health "$INTERNAL_URL" "${CFG_name}" 3 3 if [[ $? -eq 0 ]]; then log_success "Service healthy after rollback" fi DEPLOY_STATUS="rolled_back" else log_error "Rollback failed!" exit 1 fi fi fi if [[ "${DEPLOY_STATUS:-success}" != "rolled_back" ]] && [[ -n "${CFG_nginx_domain:-}" ]]; then log_step "9d. Health Check (external HTTPS)..." EXTERNAL_URL="https://${CFG_nginx_domain}${HEALTH_ENDPOINT}" log_info "URL: $EXTERNAL_URL" if ! check_https_external "$EXTERNAL_URL" "${CFG_name}"; then log_warning "External HTTPS check failed (nginx/SSL issue, no rollback)" fi fi else log_info "Health check ignoré (pas de port ou lib/health.sh non chargé)" fi # 10. Notification DEPLOY_END_TIME=$(date +%s) DEPLOY_DURATION=$((DEPLOY_END_TIME - DEPLOY_START_TIME)) if [[ "${DEPLOY_STATUS:-success}" == "rolled_back" ]]; then if type notify_deploy_failure &>/dev/null; then notify_deploy_failure "${CFG_name}" "${CFG_target}" "Health check failed, rolled back" fi else if type notify_deploy_success &>/dev/null; then notify_deploy_success "${CFG_name}" "${CFG_target}" "${COMMIT}" "${DEPLOY_DURATION}" fi fi ;; *) log_error "DEPLOY_STAGE inconnu: ${DEPLOY_STAGE}" exit 1 ;; esac # ─────────────────────────────────────────────────────────────────────────── # DONE # ─────────────────────────────────────────────────────────────────────────── DEPLOY_END_TIME="${DEPLOY_END_TIME:-$(date +%s)}" DEPLOY_DURATION="${DEPLOY_DURATION:-$((DEPLOY_END_TIME - DEPLOY_START_TIME))}" echo "" echo "═══════════════════════════════════════════════════════════════════════════" if [[ "${DEPLOY_STATUS:-success}" == "rolled_back" ]]; then log_warning "Déploiement annulé (rollback): ${CFG_name} → ${CFG_target} [${DEPLOY_STAGE:-all}] (${DEPLOY_DURATION}s)" exit 1 else log_success "Stage [${DEPLOY_STAGE:-all}] terminé: ${CFG_name} → ${CFG_target} (${DEPLOY_DURATION}s)" fi echo "═══════════════════════════════════════════════════════════════════════════" exit 0