← Retour
#!/bin/bash
# ═══════════════════════════════════════════════════════════════════════════
# FILES.SH - Stratégie de déploiement fichiers
# Releases + Symlinks (pour nginx, win11, pve, etc.)
#
# Structure créée:
# /services/mon-app/
# ├── releases/
# │ ├── 2026-01-20_12-00-00-abc1234/
# │ └── 2026-01-20_13-00-00-def5678/
# ├── shared/
# │ ├── logs/
# │ ├── data/
# │ └── config/
# └── current -> releases/2026-01-20_13-00-00-def5678
# ═══════════════════════════════════════════════════════════════════════════
# ───────────────────────────────────────────────────────────────────────────
# Exclusions par défaut pour rsync
# ───────────────────────────────────────────────────────────────────────────
DEFAULT_EXCLUDES=(
'.git'
'node_modules'
'.env*'
'conf.*.gouroubleu.yml'
'.gitlab-ci.yml'
'__pycache__'
'*.pyc'
'.pytest_cache'
'target/debug'
'.cargo'
'.Dockerfile.generated'
'docker-compose.generated.yml'
)
# ───────────────────────────────────────────────────────────────────────────
# Fonction principale de déploiement fichiers
# ───────────────────────────────────────────────────────────────────────────
deploy_files() {
local service_name="${CFG_name}"
local release_name="$(timestamp)-${COMMIT}"
local remote_base="${TARGET_PATH}/${service_name}"
local remote_release="${remote_base}/releases/${release_name}"
local keep_releases="${CFG_keep_releases:-5}"
log_info "Déploiement fichiers vers ${TARGET_HOST}:${remote_base}"
log_info " Release: ${release_name}"
# 1. Créer la structure de base
log_step "Création de la structure..."
create_directory_structure "$remote_base" "$remote_release"
# 2. Copier les fichiers
log_step "Copie des fichiers..."
copy_files_to_release "$remote_release"
# 3. Lier les dossiers shared
log_step "Liaison des dossiers partagés..."
link_shared_folders "$remote_base" "$remote_release"
# 4. Exécuter pre_deploy si configuré
if [[ -n "${CFG_pre_deploy:-}" ]]; then
log_step "Exécution pre_deploy..."
run_hook "$remote_release" "${CFG_pre_deploy}"
fi
# 5. Switcher le symlink current (atomique)
log_step "Activation de la release..."
activate_release "$remote_base" "$remote_release" "$release_name"
# 6. Exécuter post_deploy si configuré
if [[ -n "${CFG_post_deploy:-}" ]]; then
log_step "Exécution post_deploy..."
run_hook "${remote_base}/current" "${CFG_post_deploy}"
fi
# 7. Cleanup anciennes releases
log_step "Nettoyage (garde: $keep_releases releases)..."
cleanup_old_releases "$remote_base" "$keep_releases"
log_success "Déploiement fichiers terminé: ${service_name}"
}
# ───────────────────────────────────────────────────────────────────────────
# Créer la structure de répertoires
# ───────────────────────────────────────────────────────────────────────────
create_directory_structure() {
local base="$1"
local release="$2"
if [[ "$TARGET_LOCAL" == "true" ]]; then
mkdir -p "${release}"
mkdir -p "${base}/shared/logs"
mkdir -p "${base}/shared/data"
mkdir -p "${base}/shared/config"
else
ssh_exec "$TARGET_HOST" "$TARGET_USER" \
"mkdir -p ${release} ${base}/shared/logs ${base}/shared/data ${base}/shared/config"
fi
}
# ───────────────────────────────────────────────────────────────────────────
# Copier les fichiers vers la release
# ───────────────────────────────────────────────────────────────────────────
copy_files_to_release() {
local release="$1"
# Construire les arguments --exclude
local exclude_args=""
for pattern in "${DEFAULT_EXCLUDES[@]}"; do
exclude_args="$exclude_args --exclude='$pattern'"
done
# Ajouter les exclusions personnalisées si définies
if [[ -n "${CFG_exclude:-}" ]]; then
for pattern in ${CFG_exclude}; do
exclude_args="$exclude_args --exclude='$pattern'"
done
fi
if [[ "$TARGET_LOCAL" == "true" ]]; then
eval "rsync -av $exclude_args '${PROJECT_DIR}/' '${release}/'"
else
eval "rsync -avz $exclude_args '${PROJECT_DIR}/' '${TARGET_USER}@${TARGET_HOST}:${release}/'"
fi
log_success "Fichiers copiés"
}
# ───────────────────────────────────────────────────────────────────────────
# Lier les dossiers partagés
# ───────────────────────────────────────────────────────────────────────────
link_shared_folders() {
local base="$1"
local release="$2"
# Dossiers partagés par défaut
local shared_folders="logs"
# Ajouter les dossiers configurés
if [[ -n "${CFG_shared:-}" ]]; then
shared_folders="${CFG_shared}"
fi
# Ajouter data si storage.data est activé
if [[ "${CFG_storage_data:-false}" == "true" ]]; then
shared_folders="$shared_folders data"
fi
for folder in $shared_folders; do
log_info " Lien: $folder -> shared/$folder"
if [[ "$TARGET_LOCAL" == "true" ]]; then
# Supprimer le dossier s'il existe dans la release
rm -rf "${release}/${folder}" 2>/dev/null || true
# Créer le lien symbolique
ln -sfn "${base}/shared/${folder}" "${release}/${folder}"
else
ssh_exec "$TARGET_HOST" "$TARGET_USER" \
"rm -rf '${release}/${folder}' 2>/dev/null; ln -sfn '${base}/shared/${folder}' '${release}/${folder}'"
fi
done
}
# ───────────────────────────────────────────────────────────────────────────
# Activer une release (switch atomique du symlink)
# ───────────────────────────────────────────────────────────────────────────
activate_release() {
local base="$1"
local release="$2"
local name="$3"
if [[ "$TARGET_LOCAL" == "true" ]]; then
ln -sfn "${release}" "${base}/current"
else
ssh_exec "$TARGET_HOST" "$TARGET_USER" "ln -sfn '${release}' '${base}/current'"
fi
log_success "Release activée: ${name}"
}
# ───────────────────────────────────────────────────────────────────────────
# Exécuter un hook (pre_deploy ou post_deploy)
# ───────────────────────────────────────────────────────────────────────────
run_hook() {
local dir="$1"
local cmd="$2"
if [[ "$TARGET_LOCAL" == "true" ]]; then
(cd "${dir}" && eval "${cmd}")
else
ssh_exec "$TARGET_HOST" "$TARGET_USER" "cd '${dir}' && ${cmd}"
fi
log_success "Hook exécuté"
}
# ───────────────────────────────────────────────────────────────────────────
# Nettoyer les anciennes releases
# ───────────────────────────────────────────────────────────────────────────
cleanup_old_releases() {
local base="$1"
local keep="$2"
if [[ "$TARGET_LOCAL" == "true" ]]; then
if [[ -d "${base}/releases" ]]; then
(cd "${base}/releases" && ls -1t | tail -n +$((keep + 1)) | xargs -r rm -rf)
fi
else
ssh_exec "$TARGET_HOST" "$TARGET_USER" \
"cd '${base}/releases' 2>/dev/null && ls -1t | tail -n +$((keep + 1)) | xargs -r rm -rf"
fi
log_info "Anciennes releases nettoyées"
}
# ───────────────────────────────────────────────────────────────────────────
# Rollback vers une release précédente
# ───────────────────────────────────────────────────────────────────────────
rollback_files() {
local service_name="${CFG_name}"
local remote_base="${TARGET_PATH}/${service_name}"
local steps="${1:-1}"
log_info "Rollback de ${steps} release(s) pour ${service_name}..."
local releases
if [[ "$TARGET_LOCAL" == "true" ]]; then
releases=$(ls -1t "${remote_base}/releases" 2>/dev/null)
else
releases=$(ssh_exec "$TARGET_HOST" "$TARGET_USER" "ls -1t '${remote_base}/releases'" 2>/dev/null)
fi
local target_release=$(echo "$releases" | sed -n "$((steps + 1))p")
if [[ -z "$target_release" ]]; then
log_error "Pas assez de releases pour rollback de ${steps} step(s)"
return 1
fi
log_info "Rollback vers: ${target_release}"
activate_release "$remote_base" "${remote_base}/releases/${target_release}" "$target_release"
log_success "Rollback terminé"
}