#!/bin/bash # ============================================ # Script de sauvegarde Gitea # ============================================ # Ce script crée une sauvegarde complète de: # - Base de données PostgreSQL # - Dépôts Git # - Configuration Gitea # - Données utilisateur 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 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 # Variables BACKUP_DIR="./backups" 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 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 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" # 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 log "${RED}Erreur: Le conteneur Gitea n'est pas en cours d'exécution${NC}" exit 1 fi 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" 2>> "$LOG_FILE"; then log "${RED}Erreur: pg_dump a échoué${NC}" exit 1 fi 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 if ! docker compose exec -T gitea tar czf - \ -C /data \ --exclude='./log' \ --exclude='./cache' \ --exclude='./tmp' \ --exclude='./sessions' \ . > "${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 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 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 log "${RED}Erreur: L'archive finale est vide${NC}" cd - > /dev/null exit 1 fi # Supprimer le répertoire temporaire rm -rf "${BACKUP_NAME}" cd - > /dev/null BACKUP_SIZE=$(du -h "${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" | cut -f1) 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) REMAINING_BACKUPS=$(find "$BACKUP_DIR" -name "gitea_backup_*.tar.gz" -type f | wc -l) log "${GREEN}✓ Sauvegardes supprimées: $DELETED_COUNT | Restantes: $REMAINING_BACKUPS${NC}" # 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"