33800 Docs

← Retour
#!/bin/bash # ═══════════════════════════════════════════════════════════════════════════ # REMOTE.SH - Stratégie de déploiement distant # rsync ou git push (pour O2switch, hébergeurs mutualisés, etc.) # # Méthodes supportées: # - rsync : Copie directe via rsync (recommandé) # - git : Push vers un bare repo avec post-receive hook # # Configuration YAML: # deploy: # method: rsync|git # source: "." # Sous-dossier à déployer # dest: "mon-app" # Dossier destination # exclude: "*.log tmp/" # Exclusions additionnelles # pre_deploy: "cmd..." # Avant le déploiement # post_deploy: "cmd..." # Après le déploiement # backup: true # Créer backup avant deploy # ═══════════════════════════════════════════════════════════════════════════ # ─────────────────────────────────────────────────────────────────────────── # Exclusions par défaut pour rsync # ─────────────────────────────────────────────────────────────────────────── REMOTE_DEFAULT_EXCLUDES=( '.git' '.gitignore' 'node_modules' '.env*' 'conf.*.gouroubleu.yml' '.gitlab-ci.yml' '__pycache__' '*.pyc' '.pytest_cache' 'target/debug' '.cargo' '.Dockerfile.generated' 'docker-compose.generated.yml' '.DS_Store' 'Thumbs.db' '*.log' '.vscode' '.idea' ) # ─────────────────────────────────────────────────────────────────────────── # Fonction principale de déploiement remote # ─────────────────────────────────────────────────────────────────────────── deploy_remote() { local method="${CFG_deploy_method:-${CFG_method:-rsync}}" local source="${CFG_deploy_source:-${CFG_source:-.}}" local dest="${CFG_deploy_dest:-${CFG_dest:-${CFG_name}}}" log_info "Déploiement remote (méthode: $method)" log_info " Source: $source" log_info " Dest: ${TARGET_USER}@${TARGET_HOST}:${dest}" # 1. Vérifier la connexion SSH log_step "Vérification connexion SSH..." if ! verify_ssh_connection; then log_error "Impossible de se connecter à ${TARGET_HOST}" return 1 fi # 2. Créer un backup si demandé if [[ "${CFG_deploy_backup:-false}" == "true" ]]; then log_step "Création du backup..." create_remote_backup "$dest" fi # 3. Exécuter pre_deploy si configuré if [[ -n "${CFG_deploy_pre_deploy:-${CFG_pre_deploy:-}}" ]]; then log_step "Exécution pre_deploy..." run_remote_hook "$dest" "${CFG_deploy_pre_deploy:-${CFG_pre_deploy}}" fi # 4. Déployer selon la méthode case "$method" in rsync) deploy_rsync "$source" "$dest" ;; git) deploy_git "$dest" ;; *) log_error "Méthode de déploiement inconnue: $method" return 1 ;; esac # 5. Exécuter post_deploy si configuré if [[ -n "${CFG_deploy_post_deploy:-${CFG_post_deploy:-}}" ]]; then log_step "Exécution post_deploy..." run_remote_hook "$dest" "${CFG_deploy_post_deploy:-${CFG_post_deploy}}" fi log_success "Déploiement remote terminé: ${CFG_name}" } # ─────────────────────────────────────────────────────────────────────────── # Helper: Obtenir la destination SSH (utilise alias si disponible) # ─────────────────────────────────────────────────────────────────────────── get_ssh_dest() { if [[ -n "${TARGET_SSH_ALIAS:-}" ]]; then echo "${TARGET_SSH_ALIAS}" else echo "${TARGET_USER}@${TARGET_HOST}" fi } # ─────────────────────────────────────────────────────────────────────────── # Vérifier la connexion SSH # ─────────────────────────────────────────────────────────────────────────── verify_ssh_connection() { local ssh_dest=$(get_ssh_dest) ssh -o ConnectTimeout=10 -o BatchMode=yes \ "$ssh_dest" "echo ok" >/dev/null 2>&1 } # ─────────────────────────────────────────────────────────────────────────── # Créer un backup du dossier distant # ─────────────────────────────────────────────────────────────────────────── create_remote_backup() { local dest="$1" local backup_name="${dest}.backup-$(date +%Y%m%d-%H%M%S)" local ssh_dest=$(get_ssh_dest) ssh "$ssh_dest" \ "if [ -d '${dest}' ]; then cp -r '${dest}' '${backup_name}'; fi" log_info "Backup créé: ${backup_name}" } # ─────────────────────────────────────────────────────────────────────────── # Exécuter un hook sur le serveur distant # ─────────────────────────────────────────────────────────────────────────── run_remote_hook() { local dest="$1" local cmd="$2" local ssh_dest=$(get_ssh_dest) log_info " Hook: ${cmd}" ssh "$ssh_dest" "cd '${dest}' 2>/dev/null && ${cmd}" log_success "Hook exécuté" } # ─────────────────────────────────────────────────────────────────────────── # Construire les arguments d'exclusion rsync # ─────────────────────────────────────────────────────────────────────────── build_exclude_args() { local exclude_args="" # Exclusions par défaut for pattern in "${REMOTE_DEFAULT_EXCLUDES[@]}"; do exclude_args="$exclude_args --exclude='$pattern'" done # Exclusions personnalisées (CFG_exclude ou CFG_deploy_exclude) local custom_excludes="${CFG_deploy_exclude:-${CFG_exclude:-}}" if [[ -n "$custom_excludes" ]]; then for pattern in $custom_excludes; do exclude_args="$exclude_args --exclude='$pattern'" done fi echo "$exclude_args" } # ─────────────────────────────────────────────────────────────────────────── # Déploiement via rsync # ─────────────────────────────────────────────────────────────────────────── deploy_rsync() { local source="$1" local dest="$2" local ssh_dest=$(get_ssh_dest) log_step "rsync vers ${ssh_dest}:${dest}..." # Construire le chemin source complet local src_path="${PROJECT_DIR}/${source}" [[ "$source" == "." ]] && src_path="${PROJECT_DIR}/" # S'assurer que le chemin se termine par / [[ "${src_path}" != */ ]] && src_path="${src_path}/" # Construire les exclusions local exclude_args exclude_args=$(build_exclude_args) # Options rsync local rsync_opts="-avz --delete --checksum" # Mode dry-run si DRY_RUN=true if [[ "${DRY_RUN:-false}" == "true" ]]; then rsync_opts="$rsync_opts --dry-run" log_info " (mode dry-run activé)" fi # Exécuter rsync eval "rsync $rsync_opts $exclude_args '${src_path}' '${ssh_dest}:${dest}/'" log_success "rsync terminé" } # ─────────────────────────────────────────────────────────────────────────── # Déploiement via git push # ─────────────────────────────────────────────────────────────────────────── deploy_git() { local dest="$1" local remote_name="deploy-remote" local target_branch="${CFG_deploy_branch:-main}" local ssh_dest=$(get_ssh_dest) log_step "git push vers ${ssh_dest}:${dest}..." cd "$PROJECT_DIR" # Vérifier qu'on est dans un repo git if ! git rev-parse --git-dir >/dev/null 2>&1; then log_error "Le dossier n'est pas un repository git" return 1 fi # Configurer le remote # Utiliser l'alias SSH si disponible, sinon le format user@host local remote_url if [[ -n "${TARGET_SSH_ALIAS:-}" ]]; then remote_url="${TARGET_SSH_ALIAS}:~/${dest}.git" else remote_url="ssh://${TARGET_USER}@${TARGET_HOST}/~/${dest}.git" fi log_info " Remote URL: $remote_url" log_info " Branche: ${BRANCH:-HEAD} → ${target_branch}" # Supprimer le remote s'il existe déjà git remote remove "$remote_name" 2>/dev/null || true # Ajouter le remote git remote add "$remote_name" "$remote_url" # Vérifier/créer le bare repo sur le serveur si besoin if [[ "${CFG_deploy_init_repo:-false}" == "true" ]]; then init_git_bare_repo "$dest" fi # Push vers le remote local push_opts="" if [[ "${CFG_deploy_force:-true}" == "true" ]]; then push_opts="--force" fi git push $push_opts "$remote_name" "${BRANCH:-HEAD}:${target_branch}" log_success "git push terminé" # Cleanup git remote remove "$remote_name" 2>/dev/null || true } # ─────────────────────────────────────────────────────────────────────────── # Initialiser un bare repo git sur le serveur distant # ─────────────────────────────────────────────────────────────────────────── init_git_bare_repo() { local dest="$1" local repo_path="~/${dest}.git" local work_tree="~/${dest}" local ssh_dest=$(get_ssh_dest) log_info " Initialisation bare repo: ${repo_path}" ssh "$ssh_dest" bash << EOF # Créer le bare repo si nécessaire if [ ! -d "${repo_path}" ]; then mkdir -p "${repo_path}" cd "${repo_path}" git init --bare # Créer le post-receive hook cat > hooks/post-receive << 'HOOK' #!/bin/bash GIT_WORK_TREE="${work_tree}" git checkout -f main cd "${work_tree}" # Exécuter le script de déploiement si présent if [ -f ".deploy.sh" ]; then bash .deploy.sh fi HOOK chmod +x hooks/post-receive echo "Bare repo créé: ${repo_path}" fi # Créer le dossier de travail si nécessaire mkdir -p "${work_tree}" EOF log_success "Bare repo initialisé" } # ─────────────────────────────────────────────────────────────────────────── # Rollback vers le backup précédent # ─────────────────────────────────────────────────────────────────────────── rollback_remote() { local dest="${CFG_deploy_dest:-${CFG_dest:-${CFG_name}}}" local ssh_dest=$(get_ssh_dest) log_info "Rollback pour ${dest}..." # Trouver le dernier backup local latest_backup latest_backup=$(ssh "$ssh_dest" \ "ls -1td ${dest}.backup-* 2>/dev/null | head -1") if [[ -z "$latest_backup" ]]; then log_error "Aucun backup trouvé pour ${dest}" return 1 fi log_info "Restauration depuis: ${latest_backup}" ssh "$ssh_dest" bash << EOF # Supprimer le dossier actuel rm -rf "${dest}" # Restaurer le backup mv "${latest_backup}" "${dest}" EOF log_success "Rollback terminé" }