From eff651e720b3c7274d01d422fde66df1dd499f30 Mon Sep 17 00:00:00 2001 From: BeauTroll <-> Date: Wed, 17 Dec 2025 05:42:54 +0100 Subject: [PATCH] Enhance backup/restore/update scripts with security and best practices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Security improvements: set -euo pipefail, secure .env loading, safe PGPASSWORD handling - Add comprehensive logging to ./logs/ directory for all operations - Implement SHA256 checksums for backup integrity verification - Add lock file mechanism to prevent concurrent backups - Improve error handling with detailed exit codes and cleanup functions - Add safety backup of current DB before restore operations - Backup docker-compose.yml before updates with auto-restore on failure - Replace wget with curl for better reliability in health checks - Use find -mindepth for safer data directory cleanup - Add progress indicators with file sizes and operation statistics - Validate paths and checksums before restore operations - All operations now log to timestamped files with full traceability đŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- scripts/backup.sh | 148 +++++++++++++++++++++------- scripts/restore.sh | 235 ++++++++++++++++++++++++++++++++------------- scripts/update.sh | 175 +++++++++++++++++++++++---------- 3 files changed, 405 insertions(+), 153 deletions(-) diff --git a/scripts/backup.sh b/scripts/backup.sh index 2e7a56e..789f90e 100755 --- a/scripts/backup.sh +++ b/scripts/backup.sh @@ -8,17 +8,20 @@ # - Configuration Gitea # - DonnĂ©es utilisateur -set -e # ArrĂȘter en cas d'erreur +set -euo pipefail # ArrĂȘter en cas d'erreur, variables non dĂ©finies, erreurs dans pipes # Couleurs pour l'affichage RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' +BLUE='\033[0;34m' NC='\033[0m' # No Color -# Charger les variables d'environnement +# Charger les variables d'environnement de maniĂšre sĂ©curisĂ©e if [ -f .env ]; then + set -a source .env + set +a else echo -e "${RED}Erreur: Fichier .env non trouvĂ©${NC}" exit 1 @@ -30,23 +33,48 @@ TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_NAME="gitea_backup_${TIMESTAMP}" TEMP_DIR="${BACKUP_DIR}/${BACKUP_NAME}" RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-7} +LOCK_FILE="/tmp/gitea_backup.lock" +LOG_DIR="./logs" +LOG_FILE="${LOG_DIR}/backup_${TIMESTAMP}.log" # VĂ©rifier que les variables nĂ©cessaires sont dĂ©finies -if [ -z "$POSTGRES_DATABASE" ] || [ -z "$POSTGRES_USER" ] || [ -z "$POSTGRES_PASSWORD" ]; then +if [ -z "${POSTGRES_DATABASE:-}" ] || [ -z "${POSTGRES_USER:-}" ] || [ -z "${POSTGRES_PASSWORD:-}" ]; then echo -e "${RED}Erreur: Variables PostgreSQL non dĂ©finies dans .env${NC}" exit 1 fi +# VĂ©rifier qu'une sauvegarde n'est pas dĂ©jĂ  en cours +if [ -f "$LOCK_FILE" ]; then + echo -e "${RED}Erreur: Une sauvegarde est dĂ©jĂ  en cours${NC}" + echo "Si aucune sauvegarde n'est en cours, supprimez le fichier: $LOCK_FILE" + exit 1 +fi + +# CrĂ©er le fichier de lock +touch "$LOCK_FILE" + # Fonction de nettoyage en cas d'erreur cleanup() { + local exit_code=$? if [ -d "$TEMP_DIR" ]; then echo -e "${YELLOW}Nettoyage des fichiers temporaires...${NC}" rm -rf "$TEMP_DIR" fi + # Supprimer le fichier de lock + rm -f "$LOCK_FILE" + + if [ $exit_code -ne 0 ]; then + echo -e "${RED}Sauvegarde Ă©chouĂ©e avec le code: $exit_code${NC}" + fi } -# PiĂšge pour nettoyer en cas d'erreur -trap cleanup EXIT +# PiĂšge pour nettoyer en cas d'erreur ou de fin +trap cleanup EXIT INT TERM + +# Fonction de logging +log() { + echo -e "$@" | tee -a "$LOG_FILE" +} echo -e "${GREEN}=== DĂ©but de la sauvegarde Gitea ===${NC}" echo "Timestamp: $TIMESTAMP" @@ -54,52 +82,77 @@ echo "Timestamp: $TIMESTAMP" # CrĂ©er les rĂ©pertoires si nĂ©cessaire mkdir -p "$BACKUP_DIR" mkdir -p "$TEMP_DIR" +mkdir -p "$LOG_DIR" + +# Initialiser le fichier de log +log "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] DĂ©but de la sauvegarde${NC}" +log "Backup name: $BACKUP_NAME" # VĂ©rifier que les conteneurs sont en cours d'exĂ©cution -if ! docker compose ps | grep -q "gitea.*running"; then - echo -e "${RED}Erreur: Le conteneur Gitea n'est pas en cours d'exĂ©cution${NC}" +if ! docker compose ps | grep -q 'gitea.*running'; then + log "${RED}Erreur: Le conteneur Gitea n'est pas en cours d'exĂ©cution${NC}" exit 1 fi -echo -e "${YELLOW}[1/4] Sauvegarde de la base de donnĂ©es PostgreSQL...${NC}" -# Exporter la base de donnĂ©es -export PGPASSWORD="$POSTGRES_PASSWORD" -docker compose exec -T db pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" \ +log "${YELLOW}[1/5] Sauvegarde de la base de donnĂ©es PostgreSQL...${NC}" +# Exporter la base de donnĂ©es (PGPASSWORD passĂ© de maniĂšre sĂ©curisĂ©e) +if ! docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_dump \ + -U "$POSTGRES_USER" \ + -d "$POSTGRES_DATABASE" \ --format=custom \ --compress=9 \ --no-owner \ --no-acl \ - > "${TEMP_DIR}/database.dump" - -if [ ! -s "${TEMP_DIR}/database.dump" ]; then - echo -e "${RED}Erreur: Le dump de la base de donnĂ©es est vide${NC}" + > "${TEMP_DIR}/database.dump" 2>> "$LOG_FILE"; then + log "${RED}Erreur: pg_dump a Ă©chouĂ©${NC}" exit 1 fi -echo -e "${GREEN}✓ Base de donnĂ©es sauvegardĂ©e${NC}" -echo -e "${YELLOW}[2/4] Sauvegarde des dĂ©pĂŽts Git et donnĂ©es...${NC}" +if [ ! -s "${TEMP_DIR}/database.dump" ]; then + log "${RED}Erreur: Le dump de la base de donnĂ©es est vide${NC}" + exit 1 +fi + +DB_SIZE=$(du -h "${TEMP_DIR}/database.dump" | cut -f1) +log "${GREEN}✓ Base de donnĂ©es sauvegardĂ©e (${DB_SIZE})${NC}" + +log "${YELLOW}[2/5] Sauvegarde des dĂ©pĂŽts Git et donnĂ©es...${NC}" # CrĂ©er une archive des dĂ©pĂŽts et donnĂ©es -docker compose exec -T gitea tar czf - \ +if ! docker compose exec -T gitea tar czf - \ -C /data \ --exclude='./log' \ --exclude='./cache' \ --exclude='./tmp' \ --exclude='./sessions' \ - . > "${TEMP_DIR}/gitea_data.tar.gz" - -if [ ! -s "${TEMP_DIR}/gitea_data.tar.gz" ]; then - echo -e "${RED}Erreur: L'archive des donnĂ©es est vide${NC}" + . > "${TEMP_DIR}/gitea_data.tar.gz" 2>> "$LOG_FILE"; then + log "${RED}Erreur: CrĂ©ation de l'archive des donnĂ©es a Ă©chouĂ©${NC}" exit 1 fi -echo -e "${GREEN}✓ DĂ©pĂŽts et donnĂ©es sauvegardĂ©s${NC}" -echo -e "${YELLOW}[3/4] CrĂ©ation de l'archive finale...${NC}" +if [ ! -s "${TEMP_DIR}/gitea_data.tar.gz" ]; then + log "${RED}Erreur: L'archive des donnĂ©es est vide${NC}" + exit 1 +fi + +DATA_SIZE=$(du -h "${TEMP_DIR}/gitea_data.tar.gz" | cut -f1) +log "${GREEN}✓ DĂ©pĂŽts et donnĂ©es sauvegardĂ©s (${DATA_SIZE})${NC}" + +log "${YELLOW}[3/5] CrĂ©ation de l'archive finale...${NC}" # CrĂ©er l'archive finale -cd "$BACKUP_DIR" -tar czf "${BACKUP_NAME}.tar.gz" "${BACKUP_NAME}/" +if ! cd "$BACKUP_DIR"; then + log "${RED}Erreur: Impossible d'accĂ©der au rĂ©pertoire $BACKUP_DIR${NC}" + exit 1 +fi + +if ! tar czf "${BACKUP_NAME}.tar.gz" "${BACKUP_NAME}/"; then + log "${RED}Erreur: CrĂ©ation de l'archive finale a Ă©chouĂ©${NC}" + cd - > /dev/null + exit 1 +fi if [ ! -s "${BACKUP_NAME}.tar.gz" ]; then - echo -e "${RED}Erreur: L'archive finale est vide${NC}" + log "${RED}Erreur: L'archive finale est vide${NC}" + cd - > /dev/null exit 1 fi @@ -108,14 +161,39 @@ rm -rf "${BACKUP_NAME}" cd - > /dev/null BACKUP_SIZE=$(du -h "${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" | cut -f1) -echo -e "${GREEN}✓ Archive créée: ${BACKUP_NAME}.tar.gz (${BACKUP_SIZE})${NC}" +log "${GREEN}✓ Archive créée: ${BACKUP_NAME}.tar.gz (${BACKUP_SIZE})${NC}" + +log "${YELLOW}[4/5] GĂ©nĂ©ration du checksum de sĂ©curitĂ©...${NC}" +# GĂ©nĂ©rer le checksum SHA256 +if ! cd "$BACKUP_DIR"; then + log "${RED}Erreur: Impossible d'accĂ©der au rĂ©pertoire $BACKUP_DIR${NC}" + exit 1 +fi + +sha256sum "${BACKUP_NAME}.tar.gz" > "${BACKUP_NAME}.tar.gz.sha256" +CHECKSUM=$(cut -d' ' -f1 "${BACKUP_NAME}.tar.gz.sha256") +cd - > /dev/null + +log "${GREEN}✓ Checksum: ${CHECKSUM:0:16}...${NC}" + +log "${YELLOW}[5/5] Nettoyage des anciennes sauvegardes (>$RETENTION_DAYS jours)...${NC}" +# Supprimer les sauvegardes plus anciennes que RETENTION_DAYS (fichiers .tar.gz et .sha256) +DELETED_COUNT=0 +while IFS= read -r -d '' old_backup; do + log " Suppression: $(basename "$old_backup")" + rm -f "$old_backup" "${old_backup}.sha256" + DELETED_COUNT=$((DELETED_COUNT + 1)) +done < <(find "$BACKUP_DIR" -name "gitea_backup_*.tar.gz" -type f -mtime +"$RETENTION_DAYS" -print0) -echo -e "${YELLOW}[4/4] Nettoyage des anciennes sauvegardes (>$RETENTION_DAYS jours)...${NC}" -# Supprimer les sauvegardes plus anciennes que RETENTION_DAYS -find "$BACKUP_DIR" -name "gitea_backup_*.tar.gz" -type f -mtime +$RETENTION_DAYS -delete REMAINING_BACKUPS=$(find "$BACKUP_DIR" -name "gitea_backup_*.tar.gz" -type f | wc -l) -echo -e "${GREEN}✓ Sauvegardes restantes: $REMAINING_BACKUPS${NC}" +log "${GREEN}✓ Sauvegardes supprimĂ©es: $DELETED_COUNT | Restantes: $REMAINING_BACKUPS${NC}" -echo -e "${GREEN}=== Sauvegarde terminĂ©e avec succĂšs ===${NC}" -echo "Fichier: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" -echo "Taille: $BACKUP_SIZE" +# Statistiques finales +END_TIME=$(date '+%Y-%m-%d %H:%M:%S') +log "" +log "${GREEN}=== Sauvegarde terminĂ©e avec succĂšs ===${NC}" +log "Fichier: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" +log "Taille: $BACKUP_SIZE" +log "Checksum: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz.sha256" +log "Log: $LOG_FILE" +log "TerminĂ©: $END_TIME" diff --git a/scripts/restore.sh b/scripts/restore.sh index 750e289..2bf1260 100755 --- a/scripts/restore.sh +++ b/scripts/restore.sh @@ -5,16 +5,17 @@ # Ce script restaure une sauvegarde complĂšte de Gitea # ATTENTION: Cette opĂ©ration est destructive! -set -e # ArrĂȘter en cas d'erreur +set -euo pipefail # ArrĂȘter en cas d'erreur, variables non dĂ©finies, erreurs dans pipes # Couleurs pour l'affichage RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' +BLUE='\033[0;34m' NC='\033[0m' # No Color # VĂ©rifier qu'un fichier de sauvegarde a Ă©tĂ© fourni -if [ -z "$1" ]; then +if [ -z "${1:-}" ]; then echo -e "${RED}Erreur: Aucun fichier de sauvegarde spĂ©cifiĂ©${NC}" echo "Usage: $0 " echo "Exemple: $0 backups/gitea_backup_20240101_120000.tar.gz" @@ -37,21 +38,48 @@ fi # Validation du chemin (sĂ©curitĂ© contre path traversal) BACKUP_FILE=$(realpath "$BACKUP_FILE") -if [[ "$BACKUP_FILE" == *".."* ]]; then - echo -e "${RED}Erreur: Chemin invalide (path traversal dĂ©tectĂ©)${NC}" - exit 1 +BACKUPS_DIR=$(realpath "./backups" 2>/dev/null || echo "") +if [ -n "$BACKUPS_DIR" ] && [[ "$BACKUP_FILE" != "$BACKUPS_DIR"* ]]; then + echo -e "${YELLOW}Avertissement: Le fichier n'est pas dans le rĂ©pertoire backups${NC}" + read -p "Continuer quand mĂȘme? (oui/non) " -r + if [[ ! $REPLY =~ ^[Oo][Uu][Ii]$ ]]; then + echo "Restauration annulĂ©e." + exit 0 + fi fi -# Charger les variables d'environnement +# VĂ©rifier le checksum si disponible +CHECKSUM_FILE="${BACKUP_FILE}.sha256" +if [ -f "$CHECKSUM_FILE" ]; then + echo -e "${BLUE}VĂ©rification du checksum...${NC}" + if cd "$(dirname "$BACKUP_FILE")" && sha256sum -c "$(basename "$CHECKSUM_FILE")" > /dev/null 2>&1; then + echo -e "${GREEN}✓ Checksum valide${NC}" + cd - > /dev/null + else + echo -e "${RED}Erreur: Checksum invalide! Le fichier peut ĂȘtre corrompu.${NC}" + cd - > /dev/null 2>&1 || true + read -p "Continuer quand mĂȘme? (oui/non) " -r + if [[ ! $REPLY =~ ^[Oo][Uu][Ii]$ ]]; then + echo "Restauration annulĂ©e." + exit 0 + fi + fi +else + echo -e "${YELLOW}Avertissement: Aucun fichier checksum trouvĂ©${NC}" +fi + +# Charger les variables d'environnement de maniĂšre sĂ©curisĂ©e if [ -f .env ]; then + set -a source .env + set +a else echo -e "${RED}Erreur: Fichier .env non trouvĂ©${NC}" exit 1 fi # VĂ©rifier que les variables nĂ©cessaires sont dĂ©finies -if [ -z "$POSTGRES_DATABASE" ] || [ -z "$POSTGRES_USER" ] || [ -z "$POSTGRES_PASSWORD" ]; then +if [ -z "${POSTGRES_DATABASE:-}" ] || [ -z "${POSTGRES_USER:-}" ] || [ -z "${POSTGRES_PASSWORD:-}" ]; then echo -e "${RED}Erreur: Variables PostgreSQL non dĂ©finies dans .env${NC}" exit 1 fi @@ -59,55 +87,83 @@ fi # Variables TEMP_DIR=$(mktemp -d) BACKUP_NAME=$(basename "$BACKUP_FILE" .tar.gz) +LOG_DIR="./logs" +LOG_FILE="${LOG_DIR}/restore_$(date +%Y%m%d_%H%M%S).log" + +# CrĂ©er le rĂ©pertoire de logs +mkdir -p "$LOG_DIR" + +# Fonction de logging +log() { + echo -e "$@" | tee -a "$LOG_FILE" +} # Fonction de nettoyage cleanup() { + local exit_code=$? if [ -d "$TEMP_DIR" ]; then - echo -e "${YELLOW}Nettoyage des fichiers temporaires...${NC}" + log "${YELLOW}Nettoyage des fichiers temporaires...${NC}" rm -rf "$TEMP_DIR" fi + + if [ $exit_code -ne 0 ]; then + log "${RED}Restauration Ă©chouĂ©e avec le code: $exit_code${NC}" + log "${YELLOW}Les services peuvent ĂȘtre dans un Ă©tat incohĂ©rent.${NC}" + log "Consultez le log: $LOG_FILE" + fi } # PiĂšge pour nettoyer en cas d'erreur ou d'interruption trap cleanup EXIT INT TERM -echo -e "${RED}=== ATTENTION: Restauration de Gitea ===${NC}" -echo "Fichier de sauvegarde: $BACKUP_FILE" -echo -e "${RED}Cette opĂ©ration va ÉCRASER toutes les donnĂ©es actuelles!${NC}" +log "${RED}=== ATTENTION: Restauration de Gitea ===${NC}" +log "Fichier de sauvegarde: $BACKUP_FILE" +log "${RED}Cette opĂ©ration va ÉCRASER toutes les donnĂ©es actuelles!${NC}" echo "" read -p "Êtes-vous sĂ»r de vouloir continuer? (oui/non) " -r if [[ ! $REPLY =~ ^[Oo][Uu][Ii]$ ]]; then - echo "Restauration annulĂ©e." + log "Restauration annulĂ©e par l'utilisateur." exit 0 fi -echo -e "${GREEN}=== DĂ©but de la restauration ===${NC}" +# CrĂ©er un backup de sĂ©curitĂ© de la DB actuelle avant de la dropper +SAFETY_BACKUP_DIR="${TEMP_DIR}/safety_backup" +mkdir -p "$SAFETY_BACKUP_DIR" -echo -e "${YELLOW}[1/5] Extraction de l'archive...${NC}" -tar xzf "$BACKUP_FILE" -C "$TEMP_DIR" +log "" +log "${GREEN}=== DĂ©but de la restauration ===${NC}" +log "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC}" + +log "${YELLOW}[1/6] Extraction de l'archive...${NC}" +if ! tar xzf "$BACKUP_FILE" -C "$TEMP_DIR" 2>> "$LOG_FILE"; then + log "${RED}Erreur: Échec de l'extraction de l'archive${NC}" + exit 1 +fi # Trouver le rĂ©pertoire de sauvegarde BACKUP_DIR=$(find "$TEMP_DIR" -maxdepth 1 -type d -name "gitea_backup_*" | head -n 1) if [ -z "$BACKUP_DIR" ]; then - echo -e "${RED}Erreur: Structure de sauvegarde invalide${NC}" + log "${RED}Erreur: Structure de sauvegarde invalide${NC}" exit 1 fi # VĂ©rifier que les fichiers nĂ©cessaires existent if [ ! -f "$BACKUP_DIR/database.dump" ] || [ ! -f "$BACKUP_DIR/gitea_data.tar.gz" ]; then - echo -e "${RED}Erreur: Fichiers de sauvegarde manquants${NC}" + log "${RED}Erreur: Fichiers de sauvegarde manquants${NC}" + log "Contenu trouvĂ©: $(ls -la "$BACKUP_DIR" 2>&1)" exit 1 fi -echo -e "${GREEN}✓ Archive extraite${NC}" +log "${GREEN}✓ Archive extraite et vĂ©rifiĂ©e${NC}" -echo -e "${YELLOW}[2/5] ArrĂȘt des services...${NC}" -docker compose down -echo -e "${GREEN}✓ Services arrĂȘtĂ©s${NC}" +log "${YELLOW}[2/6] ArrĂȘt des services...${NC}" +if ! docker compose down 2>> "$LOG_FILE"; then + log "${YELLOW}Avertissement: Erreur lors de l'arrĂȘt des services${NC}" +fi +log "${GREEN}✓ Services arrĂȘtĂ©s${NC}" -echo -e "${YELLOW}[3/5] DĂ©marrage de la base de donnĂ©es...${NC}" +log "${YELLOW}[3/6] DĂ©marrage de PostgreSQL et backup de sĂ©curitĂ©...${NC}" docker compose up -d db -echo "Attente du dĂ©marrage de PostgreSQL..." -sleep 10 +log "Attente du dĂ©marrage de PostgreSQL..." # Attendre que PostgreSQL soit prĂȘt MAX_TRIES=30 @@ -115,77 +171,124 @@ COUNTER=0 until docker compose exec -T db pg_isready -U "$POSTGRES_USER" > /dev/null 2>&1; do COUNTER=$((COUNTER + 1)) if [ $COUNTER -gt $MAX_TRIES ]; then - echo -e "${RED}Erreur: PostgreSQL ne dĂ©marre pas${NC}" + log "${RED}Erreur: PostgreSQL ne dĂ©marre pas${NC}" exit 1 fi - echo "En attente de PostgreSQL... ($COUNTER/$MAX_TRIES)" + log "En attente de PostgreSQL... ($COUNTER/$MAX_TRIES)" sleep 2 done -echo -e "${GREEN}✓ PostgreSQL prĂȘt${NC}" +log "${GREEN}✓ PostgreSQL prĂȘt${NC}" -echo -e "${YELLOW}[4/5] Restauration de la base de donnĂ©es...${NC}" +# CrĂ©er un backup de sĂ©curitĂ© de la DB actuelle +log "${BLUE}CrĂ©ation d'un backup de sĂ©curitĂ© de la DB actuelle...${NC}" +if docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_dump \ + -U "$POSTGRES_USER" \ + -d "$POSTGRES_DATABASE" \ + --format=custom \ + > "$SAFETY_BACKUP_DIR/current_db.dump" 2>> "$LOG_FILE"; then + log "${GREEN}✓ Backup de sĂ©curitĂ© créé${NC}" +else + log "${YELLOW}Avertissement: Impossible de crĂ©er le backup de sĂ©curitĂ© (DB peut ne pas exister)${NC}" +fi + +log "${YELLOW}[4/6] Restauration de la base de donnĂ©es...${NC}" # Supprimer et recrĂ©er la base de donnĂ©es -export PGPASSWORD="$POSTGRES_PASSWORD" -docker compose exec -T db psql -U "$POSTGRES_USER" -d postgres -c "DROP DATABASE IF EXISTS $POSTGRES_DATABASE;" -docker compose exec -T db psql -U "$POSTGRES_USER" -d postgres -c "CREATE DATABASE $POSTGRES_DATABASE OWNER $POSTGRES_USER;" +if ! docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db psql \ + -U "$POSTGRES_USER" -d postgres \ + -c "DROP DATABASE IF EXISTS $POSTGRES_DATABASE;" 2>> "$LOG_FILE"; then + log "${RED}Erreur: Échec de la suppression de la base de donnĂ©es${NC}" + exit 1 +fi + +if ! docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db psql \ + -U "$POSTGRES_USER" -d postgres \ + -c "CREATE DATABASE $POSTGRES_DATABASE OWNER $POSTGRES_USER;" 2>> "$LOG_FILE"; then + log "${RED}Erreur: Échec de la crĂ©ation de la base de donnĂ©es${NC}" + exit 1 +fi # Restaurer le dump -docker compose exec -T db pg_restore \ +if ! docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_restore \ -U "$POSTGRES_USER" \ -d "$POSTGRES_DATABASE" \ --clean \ --if-exists \ --no-owner \ --no-acl \ - < "$BACKUP_DIR/database.dump" - -echo -e "${GREEN}✓ Base de donnĂ©es restaurĂ©e${NC}" - -echo -e "${YELLOW}[5/5] Restauration des donnĂ©es Gitea...${NC}" -# DĂ©marrer Gitea -docker compose up -d gitea redis - -# Attendre que Gitea soit prĂȘt -echo "Attente du dĂ©marrage de Gitea..." -sleep 15 - -# ArrĂȘter temporairement Gitea pour la restauration des fichiers -docker compose stop gitea - -# Nettoyer les donnĂ©es existantes -if [ -d "./data" ]; then - echo "Suppression des anciennes donnĂ©es..." - rm -rf ./data/* + < "$BACKUP_DIR/database.dump" 2>> "$LOG_FILE"; then + log "${RED}Erreur: Échec de la restauration de la base de donnĂ©es${NC}" + log "${YELLOW}Le backup de sĂ©curitĂ© est disponible dans: $SAFETY_BACKUP_DIR/current_db.dump${NC}" + exit 1 fi +log "${GREEN}✓ Base de donnĂ©es restaurĂ©e${NC}" + +log "${YELLOW}[5/6] Restauration des donnĂ©es Gitea...${NC}" +# ArrĂȘter Gitea s'il tourne +docker compose stop gitea 2>/dev/null || true + +# Nettoyer les donnĂ©es existantes de maniĂšre sĂ©curisĂ©e +DATA_DIR="./data" +if [ -d "$DATA_DIR" ]; then + # VĂ©rifier que c'est bien le bon rĂ©pertoire + if [ "$DATA_DIR" = "./data" ]; then + log "Suppression des anciennes donnĂ©es..." + # Utiliser find pour une suppression plus sĂ»re + find "$DATA_DIR" -mindepth 1 -delete 2>> "$LOG_FILE" || { + log "${YELLOW}Avertissement: Échec du nettoyage partiel${NC}" + } + else + log "${RED}Erreur: RĂ©pertoire de donnĂ©es invalide: $DATA_DIR${NC}" + exit 1 + fi +fi + +# CrĂ©er le rĂ©pertoire de donnĂ©es +mkdir -p "$DATA_DIR" + # Extraire les donnĂ©es depuis l'archive -mkdir -p ./data -tar xzf "$BACKUP_DIR/gitea_data.tar.gz" -C ./data +log "Extraction des donnĂ©es Gitea..." +if ! tar xzf "$BACKUP_DIR/gitea_data.tar.gz" -C "$DATA_DIR" 2>> "$LOG_FILE"; then + log "${RED}Erreur: Échec de l'extraction des donnĂ©es Gitea${NC}" + exit 1 +fi -echo -e "${GREEN}✓ DonnĂ©es restaurĂ©es${NC}" +# VĂ©rifier les permissions +log "VĂ©rification des permissions..." +if [ -d "$DATA_DIR/gitea" ]; then + log "${GREEN}✓ DonnĂ©es restaurĂ©es${NC}" +else + log "${YELLOW}Avertissement: Structure de donnĂ©es inattendue${NC}" +fi -echo -e "${YELLOW}RedĂ©marrage de tous les services...${NC}" +log "${YELLOW}[6/6] RedĂ©marrage de tous les services...${NC}" +# DĂ©marrer tous les services (db, redis, gitea) docker compose up -d # Attendre que les services soient prĂȘts +log "Attente du dĂ©marrage complet..." sleep 10 MAX_TRIES=30 COUNTER=0 -until docker compose ps | grep -q "gitea.*running"; do +until docker compose ps | grep -q 'gitea.*running'; do COUNTER=$((COUNTER + 1)) if [ $COUNTER -gt $MAX_TRIES ]; then - echo -e "${YELLOW}Avertissement: Gitea met du temps Ă  dĂ©marrer${NC}" + log "${YELLOW}Avertissement: Gitea met du temps Ă  dĂ©marrer${NC}" break fi - echo "En attente de Gitea... ($COUNTER/$MAX_TRIES)" + log "En attente de Gitea... ($COUNTER/$MAX_TRIES)" sleep 2 done -echo -e "${GREEN}=== Restauration terminĂ©e avec succĂšs ===${NC}" -echo "Gitea devrait ĂȘtre accessible sur: http://localhost:3000" -echo "" -echo "VĂ©rifications recommandĂ©es:" -echo " - VĂ©rifier l'accĂšs web" -echo " - Tester l'authentification" -echo " - VĂ©rifier les dĂ©pĂŽts" -echo " - Consulter les logs: docker compose logs -f gitea" +log "" +log "${GREEN}=== Restauration terminĂ©e avec succĂšs ===${NC}" +log "Gitea devrait ĂȘtre accessible sur: http://localhost:3000" +log "" +log "VĂ©rifications recommandĂ©es:" +log " - VĂ©rifier l'accĂšs web" +log " - Tester l'authentification" +log " - VĂ©rifier les dĂ©pĂŽts" +log " - Consulter les logs: docker compose logs -f gitea" +log "" +log "Log de restauration: $LOG_FILE" +log "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] Restauration terminĂ©e${NC}" diff --git a/scripts/update.sh b/scripts/update.sh index e7b3f46..58bbc43 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -5,102 +5,173 @@ # Ce script met Ă  jour Gitea de maniĂšre sĂ©curisĂ©e # avec une sauvegarde automatique avant la mise Ă  jour -set -e # ArrĂȘter en cas d'erreur +set -euo pipefail # ArrĂȘter en cas d'erreur, variables non dĂ©finies, erreurs dans pipes # Couleurs pour l'affichage RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' +BLUE='\033[0;34m' NC='\033[0m' # No Color -# Charger les variables d'environnement +# Charger les variables d'environnement de maniĂšre sĂ©curisĂ©e if [ -f .env ]; then + set -a source .env + set +a else echo -e "${RED}Erreur: Fichier .env non trouvĂ©${NC}" exit 1 fi -# Fonction de nettoyage en cas d'erreur -cleanup() { - echo -e "${YELLOW}Une erreur s'est produite. VĂ©rifiez l'Ă©tat des services.${NC}" - echo "Pour revenir Ă  l'Ă©tat prĂ©cĂ©dent, utilisez la derniĂšre sauvegarde:" - echo " make restore FILE=backups/gitea_backup_*.tar.gz" +# Variables +LOG_DIR="./logs" +LOG_FILE="${LOG_DIR}/update_$(date +%Y%m%d_%H%M%S).log" + +# CrĂ©er le rĂ©pertoire de logs +mkdir -p "$LOG_DIR" + +# Fonction de logging +log() { + echo -e "$@" | tee -a "$LOG_FILE" } -trap cleanup ERR +# Fonction de nettoyage en cas d'erreur +cleanup() { + local exit_code=$? + if [ $exit_code -ne 0 ]; then + log "${RED}Erreur lors de la mise Ă  jour (code: $exit_code)${NC}" + log "${YELLOW}VĂ©rifiez l'Ă©tat des services: docker compose ps${NC}" + log "Pour revenir Ă  l'Ă©tat prĂ©cĂ©dent, utilisez la derniĂšre sauvegarde:" + LATEST_BACKUP=$(ls -t backups/gitea_backup_*.tar.gz 2>/dev/null | head -n 1) + if [ -n "$LATEST_BACKUP" ]; then + log " make restore FILE=$LATEST_BACKUP" + fi + log "Log: $LOG_FILE" + fi +} -echo -e "${GREEN}=== Mise Ă  jour de Gitea ===${NC}" -echo "" +trap cleanup EXIT INT TERM + +log "${GREEN}=== Mise Ă  jour de Gitea ===${NC}" +log "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] DĂ©but de la mise Ă  jour${NC}" +log "" + +# Sauvegarder la version actuelle +CURRENT_VERSION="" +if docker compose ps | grep -q 'gitea.*running'; then + CURRENT_VERSION=$(docker compose exec -T gitea gitea --version 2>/dev/null | head -n 1 || echo "Inconnue") + log "Version actuelle: $CURRENT_VERSION" +fi # VĂ©rifier que les services tournent -if ! docker compose ps | grep -q "gitea.*running"; then - echo -e "${YELLOW}Avertissement: Gitea ne semble pas ĂȘtre en cours d'exĂ©cution${NC}" +if ! docker compose ps | grep -q 'gitea.*running'; then + log "${YELLOW}Avertissement: Gitea ne semble pas ĂȘtre en cours d'exĂ©cution${NC}" read -p "Continuer quand mĂȘme? (oui/non) " -r if [[ ! $REPLY =~ ^[Oo][Uu][Ii]$ ]]; then - echo "Mise Ă  jour annulĂ©e." + log "Mise Ă  jour annulĂ©e par l'utilisateur." exit 0 fi fi -echo -e "${YELLOW}[1/6] CrĂ©ation d'une sauvegarde de sĂ©curitĂ©...${NC}" -if ! bash scripts/backup.sh; then - echo -e "${RED}Erreur: La sauvegarde a Ă©chouĂ©. Mise Ă  jour annulĂ©e.${NC}" +log "${YELLOW}[1/7] Sauvegarde du docker-compose.yml...${NC}" +# Sauvegarder docker-compose.yml au cas oĂč +COMPOSE_BACKUP="docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)" +if [ -f docker-compose.yml ]; then + cp docker-compose.yml "$COMPOSE_BACKUP" + log "${GREEN}✓ docker-compose.yml sauvegardĂ©: $COMPOSE_BACKUP${NC}" +else + log "${YELLOW}Avertissement: docker-compose.yml non trouvĂ©${NC}" +fi + +log "${YELLOW}[2/7] CrĂ©ation d'une sauvegarde de sĂ©curitĂ©...${NC}" +if ! bash scripts/backup.sh 2>&1 | tee -a "$LOG_FILE"; then + log "${RED}Erreur: La sauvegarde a Ă©chouĂ©. Mise Ă  jour annulĂ©e.${NC}" exit 1 fi -echo -e "${GREEN}✓ Sauvegarde créée${NC}" +log "${GREEN}✓ Sauvegarde créée${NC}" -echo -e "${YELLOW}[2/6] TĂ©lĂ©chargement des nouvelles images...${NC}" -docker compose pull -echo -e "${GREEN}✓ Images tĂ©lĂ©chargĂ©es${NC}" +log "${YELLOW}[3/7] TĂ©lĂ©chargement des nouvelles images...${NC}" +if ! docker compose pull 2>&1 | tee -a "$LOG_FILE"; then + log "${RED}Erreur: Échec du tĂ©lĂ©chargement des images${NC}" + exit 1 +fi +log "${GREEN}✓ Images tĂ©lĂ©chargĂ©es${NC}" -echo -e "${YELLOW}[3/6] ArrĂȘt des services...${NC}" -docker compose down -echo -e "${GREEN}✓ Services arrĂȘtĂ©s${NC}" +log "${YELLOW}[4/7] ArrĂȘt des services...${NC}" +if ! docker compose down 2>&1 | tee -a "$LOG_FILE"; then + log "${YELLOW}Avertissement: Erreur lors de l'arrĂȘt des services${NC}" +fi +log "${GREEN}✓ Services arrĂȘtĂ©s${NC}" -echo -e "${YELLOW}[4/6] DĂ©marrage avec les nouvelles versions...${NC}" -docker compose up -d -echo -e "${GREEN}✓ Services dĂ©marrĂ©s${NC}" +log "${YELLOW}[5/7] DĂ©marrage avec les nouvelles versions...${NC}" +if ! docker compose up -d 2>&1 | tee -a "$LOG_FILE"; then + log "${RED}Erreur: Échec du dĂ©marrage des services${NC}" + exit 1 +fi +log "${GREEN}✓ Services dĂ©marrĂ©s${NC}" -echo -e "${YELLOW}[5/6] Attente du dĂ©marrage complet...${NC}" +log "${YELLOW}[6/7] Attente du dĂ©marrage complet et health check...${NC}" sleep 10 -# Attendre que Gitea soit prĂȘt +# Attendre que Gitea soit prĂȘt (utiliser curl au lieu de wget) MAX_TRIES=60 COUNTER=0 -until docker compose exec -T gitea wget -q --spider http://localhost:3000/api/healthz 2>/dev/null; do +until docker compose exec -T gitea curl -sf http://localhost:3000/api/healthz >/dev/null 2>&1; do COUNTER=$((COUNTER + 1)) if [ $COUNTER -gt $MAX_TRIES ]; then - echo -e "${RED}Erreur: Gitea ne rĂ©pond pas aprĂšs la mise Ă  jour${NC}" - echo "VĂ©rifiez les logs: docker compose logs gitea" + log "${RED}Erreur: Gitea ne rĂ©pond pas aprĂšs la mise Ă  jour${NC}" + log "VĂ©rifiez les logs: docker compose logs gitea" + log "${YELLOW}Tentative de restauration automatique...${NC}" + + # Essayer de restaurer le docker-compose.yml + if [ -f "$COMPOSE_BACKUP" ]; then + cp "$COMPOSE_BACKUP" docker-compose.yml + log "docker-compose.yml restaurĂ©" + fi + exit 1 fi - echo "En attente de Gitea... ($COUNTER/$MAX_TRIES)" + log "En attente de Gitea... ($COUNTER/$MAX_TRIES)" sleep 2 done -echo -e "${GREEN}✓ Gitea rĂ©pond${NC}" +log "${GREEN}✓ Gitea rĂ©pond et est opĂ©rationnel${NC}" -echo -e "${YELLOW}[6/6] VĂ©rification de l'Ă©tat...${NC}" -# Afficher les versions -echo "" -echo "Versions actuelles:" -docker compose images +log "${YELLOW}[7/7] VĂ©rification de l'Ă©tat et de la version...${NC}" -echo "" -echo "État des services:" -docker compose ps +# Obtenir la nouvelle version +NEW_VERSION=$(docker compose exec -T gitea gitea --version 2>/dev/null | head -n 1 || echo "Inconnue") +log "Nouvelle version: $NEW_VERSION" -echo "" -echo -e "${GREEN}=== Mise Ă  jour terminĂ©e avec succĂšs ===${NC}" -echo "" -echo "VĂ©rifications recommandĂ©es:" -echo " 1. AccĂ©der Ă  l'interface web: http://localhost:3000" -echo " 2. VĂ©rifier les dĂ©pĂŽts existants" -echo " 3. Tester les fonctionnalitĂ©s principales" -echo " 4. Consulter les logs: docker compose logs -f gitea" -echo "" -echo "En cas de problĂšme, restaurez la sauvegarde:" +# Afficher les versions des images +log "" +log "Images Docker:" +docker compose images 2>&1 | tee -a "$LOG_FILE" + +log "" +log "État des services:" +docker compose ps 2>&1 | tee -a "$LOG_FILE" + +# Supprimer l'ancien backup docker-compose.yml si tout s'est bien passĂ© +if [ -f "$COMPOSE_BACKUP" ]; then + rm -f "$COMPOSE_BACKUP" + log "Backup temporaire docker-compose.yml supprimĂ©" +fi + +log "" +log "${GREEN}=== Mise Ă  jour terminĂ©e avec succĂšs ===${NC}" +log "" +log "VĂ©rifications recommandĂ©es:" +log " 1. AccĂ©der Ă  l'interface web: http://localhost:3000" +log " 2. VĂ©rifier les dĂ©pĂŽts existants" +log " 3. Tester les fonctionnalitĂ©s principales" +log " 4. Consulter les logs: docker compose logs -f gitea" +log "" +log "En cas de problĂšme, restaurez la sauvegarde:" LATEST_BACKUP=$(ls -t backups/gitea_backup_*.tar.gz 2>/dev/null | head -n 1) if [ -n "$LATEST_BACKUP" ]; then - echo " make restore FILE=$LATEST_BACKUP" + log " make restore FILE=$LATEST_BACKUP" fi +log "" +log "Log de mise Ă  jour: $LOG_FILE" +log "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] Mise Ă  jour terminĂ©e${NC}"