Rendre docker-backup.sh entierement generique via DOCKER_APP_NAME

Suppression de tous les hardcodes 'gitea' dans docker-backup.sh :
noms de backup, fichiers DB/volumes, patterns de nettoyage, script
restore genere, metadonnees, logs et notifications.

Autres corrections :
- docker-auto-update.sh : VERSION_ENDPOINT configurable au lieu
  du endpoint Gitea /api/v1/version en dur
- restore.sh : HEALTH_CHECK_URL au lieu de localhost:3000 en dur
- docker-scripts.conf : ajout VERSION_ENDPOINT
- Suppression de backup-docker.sh (script legacy non generique)
- README : synchronisation des noms de fichiers reels
- Correction config file source (gitea-scripts.conf -> docker-scripts.conf)
This commit is contained in:
2026-06-26 14:13:54 -04:00
parent 286d2403ad
commit 6f761fe8ed
6 changed files with 171 additions and 150 deletions
+21 -20
View File
@@ -1,6 +1,6 @@
# Docker Scripts — Auto-Update, Backup, Restore & Déploiement Cron # Docker Scripts — Auto-Update, Backup, Restore & Déploiement Cron
Solution complète et générique pour la maintenance automatisée d'applications Solution complète et **générique** pour la maintenance automatisée d'applications
Docker (Gitea, ou toute autre app MySQL + volumes) : backup, mise à jour, Docker (Gitea, ou toute autre app MySQL + volumes) : backup, mise à jour,
restauration et déploiement. restauration et déploiement.
@@ -147,7 +147,7 @@ bash setup.sh --dry-run # Aperçu sans rien installer
--- ---
### 💾 `gitea-backup.sh` — Backup complet ### 💾 `docker-backup.sh` — Backup complet
Sauvegarde complète et portable : DB MySQL + volumes Docker + configuration. Sauvegarde complète et portable : DB MySQL + volumes Docker + configuration.
@@ -155,8 +155,8 @@ Sauvegarde complète et portable : DB MySQL + volumes Docker + configuration.
| Variable | Défaut | Description | | Variable | Défaut | Description |
|----------|--------|-------------| |----------|--------|-------------|
| `BACKUP_DIR` | `/NFS/BACKUP/DOCKER/dev-prod/gitea` | Destination backups | | `BACKUP_DIR` | `/NFS/BACKUP/DOCKER/$HOSTNAME/gitea` | Destination backups |
| `COMPOSE_DIR` | `~/dev/git/outils/docker/gitea` | Dossier docker-compose | | `COMPOSE_DIR` | `~/dev/git/outils/gitea` | Dossier docker-compose |
| `DB_CONTAINER` | `gitea_db` | Conteneur MySQL | | `DB_CONTAINER` | `gitea_db` | Conteneur MySQL |
| `DB_NAME` | `gitea` | Nom base de données | | `DB_NAME` | `gitea` | Nom base de données |
| `DB_ROOT_PASSWORD` | `gitea` | Mot de passe root MySQL | | `DB_ROOT_PASSWORD` | `gitea` | Mot de passe root MySQL |
@@ -174,7 +174,7 @@ Sauvegarde complète et portable : DB MySQL + volumes Docker + configuration.
```cron ```cron
# Tous les jours à 2h du matin # Tous les jours à 2h du matin
0 2 * * * /home/openclaw/scripts/gitea-backup.sh 0 2 * * * /home/openclaw/scripts/docker-backup.sh
``` ```
#### Structure d'un backup #### Structure d'un backup
@@ -193,7 +193,7 @@ Sauvegarde complète et portable : DB MySQL + volumes Docker + configuration.
#### Backup distant (optionnel) #### Backup distant (optionnel)
Configurer dans `gitea-scripts.conf` : Configurer dans `docker-scripts.conf` :
```bash ```bash
REMOTE_BACKUP_ENABLED=true REMOTE_BACKUP_ENABLED=true
@@ -206,7 +206,7 @@ REMOTE_BACKUP_SSH_KEY=/home/user/.ssh/id_rsa
--- ---
### 🔄 `gitea-auto-update.sh` — Mise à jour automatique ### 🔄 `docker-auto-update.sh` — Mise à jour automatique
Vérifie quotidiennement si une nouvelle image Docker Gitea est disponible. Vérifie quotidiennement si une nouvelle image Docker Gitea est disponible.
@@ -220,6 +220,7 @@ Vérifie quotidiennement si une nouvelle image Docker Gitea est disponible.
| `IMAGE_TAG` | `latest` | Tag (version) | | `IMAGE_TAG` | `latest` | Tag (version) |
| `HEALTH_CHECK_URL` | `http://localhost:3000` | URL vérification santé | | `HEALTH_CHECK_URL` | `http://localhost:3000` | URL vérification santé |
| `HEALTH_CHECK_TIMEOUT` | `30` | Timeout attente (secondes) | | `HEALTH_CHECK_TIMEOUT` | `30` | Timeout attente (secondes) |
| `VERSION_ENDPOINT` | *(vide)* | Endpoint version (ex: `/api/v1/version`) |
#### Fonctionnement #### Fonctionnement
@@ -240,12 +241,12 @@ Vérifie quotidiennement si une nouvelle image Docker Gitea est disponible.
# Telegram # Telegram
ENABLE_NOTIFICATIONS=true NOTIFY_METHOD=telegram \ ENABLE_NOTIFICATIONS=true NOTIFY_METHOD=telegram \
TELEGRAM_BOT_TOKEN=xxx TELEGRAM_CHAT_ID=xxx \ TELEGRAM_BOT_TOKEN=xxx TELEGRAM_CHAT_ID=xxx \
bash gitea-auto-update.sh bash docker-auto-update.sh
# ntfy.sh # ntfy.sh
ENABLE_NOTIFICATIONS=true NOTIFY_METHOD=ntfy \ ENABLE_NOTIFICATIONS=true NOTIFY_METHOD=ntfy \
NTPY_TOPIC=mon-topic \ NTPY_TOPIC=mon-topic \
bash gitea-auto-update.sh bash docker-auto-update.sh
``` ```
--- ---
@@ -293,8 +294,8 @@ Le script gère un **bloc marqué** dans la crontab :
```cron ```cron
# >>> GITEA SCRIPTS (managed by install_cron.sh) <<< # >>> GITEA SCRIPTS (managed by install_cron.sh) <<<
0 2 * * * /home/bruno/scripts/gitea-backup.sh >> /var/log/gitea-backup-cron.log 2>&1 0 2 * * * /home/bruno/scripts/docker-backup.sh >> /var/log/gitea-backup-cron.log 2>&1
0 4 * * * /home/bruno/scripts/gitea-auto-update.sh >> /var/log/gitea-update-cron.log 2>&1 0 4 * * * /home/bruno/scripts/docker-auto-update.sh >> /var/log/gitea-update-cron.log 2>&1
# >>> END GITEA SCRIPTS <<< # >>> END GITEA SCRIPTS <<<
``` ```
@@ -304,7 +305,7 @@ Le `--uninstall` retire uniquement ce bloc, sans toucher au reste de la crontab.
## ⚙️ Configuration centralisée ## ⚙️ Configuration centralisée
Tous les paramètres sont dans `gitea-scripts.conf`. Les scripts chargent ce Tous les paramètres sont dans `docker-scripts.conf`. Les scripts chargent ce
fichier automatiquement s'il est présent dans le même répertoire. fichier automatiquement s'il est présent dans le même répertoire.
Les variables d'environnement **surchargent** les valeurs du fichier de Les variables d'environnement **surchargent** les valeurs du fichier de
@@ -312,13 +313,13 @@ configuration.
```bash ```bash
# Exemple : changer le dossier de backup # Exemple : changer le dossier de backup
BACKUP_DIR=/mnt/big-disk/gitea bash gitea-backup.sh BACKUP_DIR=/mnt/big-disk/gitea bash docker-backup.sh
# Exemple : backup avec arrêt des conteneurs # Exemple : backup avec arrêt des conteneurs
STOP_CONTAINERS=true bash gitea-backup.sh STOP_CONTAINERS=true bash docker-backup.sh
# Exemple : backup + sync distante # Exemple : backup + sync distante
REMOTE_BACKUP_ENABLED=true bash gitea-backup.sh REMOTE_BACKUP_ENABLED=true bash docker-backup.sh
``` ```
--- ---
@@ -342,8 +343,8 @@ logrotate (rotation hebdomadaire, 12 semaines de rétention, compression).
Pour éviter les exécutions concurrentes (backup + update simultanés), chaque Pour éviter les exécutions concurrentes (backup + update simultanés), chaque
script utilise un lock file dans `/tmp` : script utilise un lock file dans `/tmp` :
- `/tmp/gitea-backup.lock` - `/tmp/${DOCKER_APP_NAME}-backup.lock`
- `/tmp/gitea-auto-update.lock` - `/tmp/${DOCKER_APP_NAME}-auto-update.lock`
Si un lock stale est détecté (PID inexistant), il est automatiquement nettoyé. Si un lock stale est détecté (PID inexistant), il est automatiquement nettoyé.
@@ -387,7 +388,7 @@ ls -t /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-* | tail -n +10 | xargs rm
tail -50 /var/log/gitea-update.log tail -50 /var/log/gitea-update.log
# Tester manuellement # Tester manuellement
bash ~/scripts/gitea-auto-update.sh bash ~/scripts/docker-auto-update.sh
``` ```
### Restauration ### Restauration
@@ -403,11 +404,11 @@ bash ~/scripts/restore.sh $(ls -td /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backu
## 🔐 Sécurité ## 🔐 Sécurité
⚠️ Les mots de passe dans `gitea-scripts.conf` et `docker-compose.yml` sont ⚠️ Les mots de passe dans `docker-scripts.conf` et `docker-compose.yml` sont
des valeurs par défaut. **Changez-les avant la mise en production :** des valeurs par défaut. **Changez-les avant la mise en production :**
```bash ```bash
# Dans gitea-scripts.conf # Dans docker-scripts.conf
DB_ROOT_PASSWORD=votre-mot-de-passe-securise DB_ROOT_PASSWORD=votre-mot-de-passe-securise
DB_PASSWORD=votre-autre-mot-de-passe DB_PASSWORD=votre-autre-mot-de-passe
-14
View File
@@ -1,14 +0,0 @@
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/NFS/BACKUPS"
DATE=$(date +%Y%m%d)
mkdir -p $BACKUP_DIR
docker run --rm -v jellyfin_data:/source -v $BACKUP_DIR:/backup alpine \
tar czf /backup/jellyfin-$DATE.tar.gz /source
docker run --rm -v shaarli_data:/source -v $BACKUP_DIR:/backup alpine \
tar czf /backup/shaarli-$DATE.tar.gz /source
# Vérification
ls -lh $BACKUP_DIR | grep -E "jellyfin|shaarli"
+8 -1
View File
@@ -50,6 +50,7 @@ NTPY_TOPIC="${NTPY_TOPIC:-}"
# Santé # Santé
HEALTH_CHECK_URL="${HEALTH_CHECK_URL:-http://localhost:3000}" HEALTH_CHECK_URL="${HEALTH_CHECK_URL:-http://localhost:3000}"
HEALTH_CHECK_TIMEOUT="${HEALTH_CHECK_TIMEOUT:-30}" HEALTH_CHECK_TIMEOUT="${HEALTH_CHECK_TIMEOUT:-30}"
VERSION_ENDPOINT="${VERSION_ENDPOINT:-}"
# Lock file # Lock file
LOCK_DIR="${LOCK_DIR:-/tmp}" LOCK_DIR="${LOCK_DIR:-/tmp}"
@@ -252,7 +253,13 @@ for i in $(seq 1 "$HEALTH_CHECK_TIMEOUT"); do
done done
# ─── Rapport final ────────────────────────────────────────────────────────── # ─── Rapport final ──────────────────────────────────────────────────────────
APP_VERSION=$(curl -s "$HEALTH_CHECK_URL/api/v1/version" 2>/dev/null | grep -oE '"version":"[^"]+"' | cut -d'"' -f4 || echo "inconnue") APP_VERSION="inconnue"
if [[ -n "$VERSION_ENDPOINT" ]]; then
APP_VERSION=$(curl -s "${HEALTH_CHECK_URL}${VERSION_ENDPOINT}" 2>/dev/null | grep -oE '"version":"[^"]+"' | cut -d'"' -f4 || echo "inconnue")
else
# Essayer de détecter la version via l'image Docker
APP_VERSION=$(docker image inspect "$FULL_IMAGE" --format '{{.Id}}' 2>/dev/null | cut -c8-19 || echo "inconnue")
fi
log "✅ Mise à jour réussie !" log "✅ Mise à jour réussie !"
log "📋 Nouvelle version : $APP_VERSION" log "📋 Nouvelle version : $APP_VERSION"
+138 -113
View File
@@ -1,9 +1,9 @@
#!/bin/bash #!/bin/bash
# ============================================================================= # =============================================================================
# gitea-backup.sh — Backup complet Gitea (DB + volumes + config) # docker-backup.sh — Backup complet d'une app Docker (DB + volumes + config)
# ============================================================================= # =============================================================================
# À placer dans /usr/local/bin/ ou ~/scripts/ # À placer dans /usr/local/bin/ ou ~/scripts/
# Cron recommandé : 0 2 * * * /usr/local/bin/gitea-backup.sh # Cron recommandé : 0 2 * * * /usr/local/bin/docker-backup.sh
# #
# Ce script sauvegarde : # Ce script sauvegarde :
# 1. La base de données MySQL (dump SQL complet) # 1. La base de données MySQL (dump SQL complet)
@@ -11,40 +11,44 @@
# 3. Le fichier docker-compose.yml et la config # 3. Le fichier docker-compose.yml et la config
# 4. Génère un script restore.sh autonome # 4. Génère un script restore.sh autonome
# #
# Paramètres configurables : voir gitea-scripts.conf # Paramètres configurables : voir docker-scripts.conf
# Utilisation avec autre app : DOCKER_APP_NAME=monapp bash docker-backup.sh
# ============================================================================= # =============================================================================
set -euo pipefail set -euo pipefail
# ─── Charger la configuration commune ───────────────────────────────────────── # ─── Charger la configuration commune ─────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CONFIG_FILE="${CONFIG_FILE:-$SCRIPT_DIR/gitea-scripts.conf}" CONFIG_FILE="${CONFIG_FILE:-$SCRIPT_DIR/docker-scripts.conf}"
if [[ -f "$CONFIG_FILE" ]]; then if [[ -f "$CONFIG_FILE" ]]; then
# shellcheck source=/dev/null # shellcheck source=/dev/null
source "$CONFIG_FILE" source "$CONFIG_FILE"
fi fi
# ─── Nom de l'application (défaut si pas dans la config) ─────────────────────
DOCKER_APP_NAME="${DOCKER_APP_NAME:-gitea}"
# ─── Configuration locale (après chargement conf, permet surcharge env) ────── # ─── Configuration locale (après chargement conf, permet surcharge env) ──────
BACKUP_DIR="${BACKUP_DIR:-/NFS/BACKUP/DOCKER/dev-prod/gitea}" BACKUP_DIR="${BACKUP_DIR:-/NFS/BACKUP/DOCKER/$HOSTNAME/$DOCKER_APP_NAME}"
COMPOSE_DIR="${COMPOSE_DIR:-$HOME/dev/git/outils/docker/gitea}" COMPOSE_DIR="${COMPOSE_DIR:-$HOME/dev/git/outils/$DOCKER_APP_NAME}"
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}" COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
# Conteneurs # Conteneurs (dérivés de DOCKER_APP_NAME)
DB_CONTAINER="${DB_CONTAINER:-gitea_db}" DB_CONTAINER="${DB_CONTAINER:-${DOCKER_APP_NAME}_db}"
APP_CONTAINER="${APP_CONTAINER:-gitea_server}" APP_CONTAINER="${APP_CONTAINER:-${DOCKER_APP_NAME}_server}"
DB_SERVICE="${DB_SERVICE:-gitea_db}" DB_SERVICE="${DB_SERVICE:-${DOCKER_APP_NAME}_db}"
APP_SERVICE="${APP_SERVICE:-gitea_server}" APP_SERVICE="${APP_SERVICE:-${DOCKER_APP_NAME}_server}"
# Base de données # Base de données (dérivés de DOCKER_APP_NAME)
DB_NAME="${DB_NAME:-gitea}" DB_NAME="${DB_NAME:-$DOCKER_APP_NAME}"
DB_USER="${DB_USER:-gitea}" DB_USER="${DB_USER:-$DOCKER_APP_NAME}"
DB_PASSWORD="${DB_PASSWORD:-chab30}" DB_PASSWORD="${DB_PASSWORD:-chab30}"
DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-gitea}" DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-$DOCKER_APP_NAME}"
# Volumes Docker (chemins HÔTE, pas conteneur) # Volumes Docker - chemins HÔTE (dérivés de DOCKER_APP_NAME)
VOLUME_DATA="${VOLUME_DATA:-/DOCKER_CONFIG/gitea/data}" VOLUME_DATA="${VOLUME_DATA:-/DOCKER_CONFIG/$DOCKER_APP_NAME/data}"
VOLUME_MYSQL="${VOLUME_MYSQL:-/DOCKER_CONFIG/gitea/mysql}" VOLUME_MYSQL="${VOLUME_MYSQL:-/DOCKER_CONFIG/$DOCKER_APP_NAME/mysql}"
# Rétention # Rétention
RETENTION_DAYS="${RETENTION_DAYS:-30}" RETENTION_DAYS="${RETENTION_DAYS:-30}"
@@ -58,12 +62,12 @@ STOP_CONTAINERS="${STOP_CONTAINERS:-false}"
ENABLE_NOTIFICATIONS="${ENABLE_NOTIFICATIONS:-false}" ENABLE_NOTIFICATIONS="${ENABLE_NOTIFICATIONS:-false}"
# Log # Log
LOG_FILE="${LOG_FILE:-/var/log/gitea-backup.log}" LOG_FILE="${LOG_FILE:-/var/log/${DOCKER_APP_NAME}-backup.log}"
LOG_DIR="$(dirname "$LOG_FILE")" LOG_DIR="$(dirname "$LOG_FILE")"
# Lock file # Lock file
LOCK_DIR="${LOCK_DIR:-/tmp}" LOCK_DIR="${LOCK_DIR:-/tmp}"
LOCK_FILE="${LOCK_DIR}/gitea-backup.lock" LOCK_FILE="${LOCK_DIR}/${DOCKER_APP_NAME}-backup.lock"
# Backup distant # Backup distant
REMOTE_BACKUP_ENABLED="${REMOTE_BACKUP_ENABLED:-false}" REMOTE_BACKUP_ENABLED="${REMOTE_BACKUP_ENABLED:-false}"
@@ -101,7 +105,7 @@ notify() {
;; ;;
ntfy) ntfy)
if [[ -n "$NTPY_TOPIC" ]]; then if [[ -n "$NTPY_TOPIC" ]]; then
curl -s -H "Title: Gitea Backup" -d "$msg" "https://ntfy.sh/$NTPY_TOPIC" > /dev/null 2>&1 || true curl -s -H "Title: ${DOCKER_APP_NAME^} Backup" -d "$msg" "https://ntfy.sh/$NTPY_TOPIC" > /dev/null 2>&1 || true
fi fi
;; ;;
esac esac
@@ -222,14 +226,14 @@ DATE_LABEL=$(date +%Y-%m-%d)
DAY_OF_WEEK=$(date +%u) # 1=lundi, 7=dimanche DAY_OF_WEEK=$(date +%u) # 1=lundi, 7=dimanche
DAY_OF_MONTH=$(date +%d) DAY_OF_MONTH=$(date +%d)
BACKUP_NAME="gitea-backup-${TIMESTAMP}" BACKUP_NAME="${DOCKER_APP_NAME}-backup-${TIMESTAMP}"
BACKUP_PATH="${BACKUP_DIR}/${BACKUP_NAME}" BACKUP_PATH="${BACKUP_DIR}/${BACKUP_NAME}"
RESTORE_SCRIPT="${BACKUP_PATH}/restore.sh" RESTORE_SCRIPT="${BACKUP_PATH}/restore.sh"
DOCKER_COMPOSE=$(detect_compose) DOCKER_COMPOSE=$(detect_compose)
log "════════════════════════════════════════════════════════" log "════════════════════════════════════════════════════════"
log "📦 Début backup Gitea → $BACKUP_PATH" log "📦 Début backup ${DOCKER_APP_NAME^} → $BACKUP_PATH"
# ─── Pré-vérifications ────────────────────────────────────────────────────── # ─── Pré-vérifications ──────────────────────────────────────────────────────
mkdir -p "$BACKUP_DIR" || { log "❌ Impossible de créer $BACKUP_DIR"; exit 1; } mkdir -p "$BACKUP_DIR" || { log "❌ Impossible de créer $BACKUP_DIR"; exit 1; }
@@ -269,14 +273,14 @@ if docker ps --format '{{.Names}}' | grep -q "^${DB_CONTAINER}$"; then
--events \ --events \
--hex-blob \ --hex-blob \
--default-character-set=utf8mb4 \ --default-character-set=utf8mb4 \
"$DB_NAME" > "${BACKUP_PATH}/gitea-db.sql" 2>"${BACKUP_PATH}/mysqldump.err" || { "$DB_NAME" > "${BACKUP_PATH}/${DOCKER_APP_NAME}-db.sql" 2>"${BACKUP_PATH}/mysqldump.err" || {
log "❌ Échec du dump MySQL" log "❌ Échec du dump MySQL"
cat "${BACKUP_PATH}/mysqldump.err" >> "$LOG_FILE" 2>/dev/null || true cat "${BACKUP_PATH}/mysqldump.err" >> "$LOG_FILE" 2>/dev/null || true
[[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && $DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true [[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && $DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true
exit 1 exit 1
} }
rm -f "${BACKUP_PATH}/mysqldump.err" rm -f "${BACKUP_PATH}/mysqldump.err"
DB_SIZE=$(wc -c < "${BACKUP_PATH}/gitea-db.sql" 2>/dev/null || echo "0") DB_SIZE=$(wc -c < "${BACKUP_PATH}/${DOCKER_APP_NAME}-db.sql" 2>/dev/null || echo "0")
log " ✅ Dump MySQL OK (${DB_SIZE} bytes)" log " ✅ Dump MySQL OK (${DB_SIZE} bytes)"
else else
log "❌ Conteneur $DB_CONTAINER non trouvé (pas en cours d'exécution)" log "❌ Conteneur $DB_CONTAINER non trouvé (pas en cours d'exécution)"
@@ -286,14 +290,14 @@ fi
# Compresser le dump si demandé # Compresser le dump si demandé
if [[ "$COMPRESS" == "true" ]]; then if [[ "$COMPRESS" == "true" ]]; then
gzip -f "${BACKUP_PATH}/gitea-db.sql" gzip -f "${BACKUP_PATH}/${DOCKER_APP_NAME}-db.sql"
log " 📦 Compressé : $(ls -lh "${BACKUP_PATH}/gitea-db.sql.gz" 2>/dev/null | awk '{print $5}')" log " 📦 Compressé : $(ls -lh "${BACKUP_PATH}/${DOCKER_APP_NAME}-db.sql.gz" 2>/dev/null | awk '{print $5}')"
fi fi
# ─── 2. Backup volumes Docker ─────────────────────────────────────────────── # ─── 2. Backup volumes Docker ───────────────────────────────────────────────
log "📁 Backup volume data ($VOLUME_DATA)..." log "📁 Backup volume data ($VOLUME_DATA)..."
tar -czf "${BACKUP_PATH}/gitea-data.tar.gz" \ tar -czf "${BACKUP_PATH}/${DOCKER_APP_NAME}-data.tar.gz" \
-C "$(dirname "$VOLUME_DATA")" \ -C "$(dirname "$VOLUME_DATA")" \
--exclude='*.log' \ --exclude='*.log' \
--exclude='log/*' \ --exclude='log/*' \
@@ -304,30 +308,36 @@ tar -czf "${BACKUP_PATH}/gitea-data.tar.gz" \
[[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && $DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true [[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && $DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true
exit 1 exit 1
} }
DATA_SIZE=$(ls -lh "${BACKUP_PATH}/gitea-data.tar.gz" 2>/dev/null | awk '{print $5}') DATA_SIZE=$(ls -lh "${BACKUP_PATH}/${DOCKER_APP_NAME}-data.tar.gz" 2>/dev/null | awk '{print $5}')
log " ✅ Volume data sauvegardé ($DATA_SIZE)" log " ✅ Volume data sauvegardé ($DATA_SIZE)"
log "📁 Backup volume MySQL ($VOLUME_MYSQL)..." log "📁 Backup volume MySQL ($VOLUME_MYSQL)..."
tar -czf "${BACKUP_PATH}/gitea-mysql.tar.gz" \ tar -czf "${BACKUP_PATH}/${DOCKER_APP_NAME}-mysql.tar.gz" \
-C "$(dirname "$VOLUME_MYSQL")" \ -C "$(dirname "$VOLUME_MYSQL")" \
"$(basename "$VOLUME_MYSQL")" 2>&1 || { "$(basename "$VOLUME_MYSQL")" 2>&1 || {
log "❌ Échec tar du volume MySQL" log "❌ Échec tar du volume MySQL"
[[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && $DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true [[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && $DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true
exit 1 exit 1
} }
MYSQL_SIZE=$(ls -lh "${BACKUP_PATH}/gitea-mysql.tar.gz" 2>/dev/null | awk '{print $5}') MYSQL_SIZE=$(ls -lh "${BACKUP_PATH}/${DOCKER_APP_NAME}-mysql.tar.gz" 2>/dev/null | awk '{print $5}')
log " ✅ Volume MySQL sauvegardé ($MYSQL_SIZE)" log " ✅ Volume MySQL sauvegardé ($MYSQL_SIZE)"
# ─── 3. Sauvegarde docker-compose + config ────────────────────────────────── # ─── 3. Sauvegarde docker-compose + config ──────────────────────────────────
log "📋 Sauvegarde docker-compose et configuration..." log "📋 Sauvegarde docker-compose et configuration..."
cp "$COMPOSE_DIR/$COMPOSE_FILE" "${BACKUP_PATH}/docker-compose.yml" cp "$COMPOSE_DIR/$COMPOSE_FILE" "${BACKUP_PATH}/docker-compose.yml"
# Sauvegarder aussi le app.ini si présent # Sauvegarder la config applicative si présente (app.ini ou équivalent)
if [[ -f "${VOLUME_DATA}/gitea/conf/app.ini" ]]; then APP_CONFIG_FOUND=false
cp "${VOLUME_DATA}/gitea/conf/app.ini" "${BACKUP_PATH}/app.ini" for config_path in "${VOLUME_DATA}/${DOCKER_APP_NAME}/conf/app.ini" "${VOLUME_DATA}/conf/app.ini" "${VOLUME_DATA}/app.ini"; do
log " ✅ app.ini sauvegardé" if [[ -f "$config_path" ]]; then
else cp "$config_path" "${BACKUP_PATH}/app.ini"
log " ⚠️ app.ini non trouvé dans ${VOLUME_DATA}/gitea/conf/" log " ✅ app.ini sauvegardé depuis $config_path"
APP_CONFIG_FOUND=true
break
fi
done
if [[ "$APP_CONFIG_FOUND" != "true" ]]; then
log " ⚠️ app.ini non trouvé (cherché dans ${VOLUME_DATA}/)"
fi fi
# ─── Redémarrer les conteneurs si on les avait arrêtés ────────────────────── # ─── Redémarrer les conteneurs si on les avait arrêtés ──────────────────────
@@ -335,10 +345,10 @@ if [[ "$CONTAINERS_WERE_STOPPED" == "true" ]]; then
log "▶️ Redémarrage des conteneurs..." log "▶️ Redémarrage des conteneurs..."
$DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true $DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true
# Attendre que Gitea réponde # Attendre que l'app réponde
for i in $(seq 1 30); do for i in $(seq 1 30); do
if curl -s -o /dev/null -w "%{http_code}" "http://localhost:3000" 2>/dev/null | grep -qE "200|302|303"; then if curl -s -o /dev/null -w "%{http_code}" "${HEALTH_CHECK_URL:-http://localhost:3000}" 2>/dev/null | grep -qE "200|302|303"; then
log "✅ Gitea répond après ${i}s" log "✅ ${DOCKER_APP_NAME^} répond après ${i}s"
break break
fi fi
sleep 1 sleep 1
@@ -348,14 +358,14 @@ fi
# ─── 4. Générer le script de restore ──────────────────────────────────────── # ─── 4. Générer le script de restore ────────────────────────────────────────
log "📝 Génération du script de restore..." log "📝 Génération du script de restore..."
DB_DUMP_FILE="gitea-db.sql" DB_DUMP_FILE="${DOCKER_APP_NAME}-db.sql"
[[ "$COMPRESS" == "true" ]] && DB_DUMP_FILE="gitea-db.sql.gz" [[ "$COMPRESS" == "true" ]] && DB_DUMP_FILE="${DOCKER_APP_NAME}-db.sql.gz"
cat > "$RESTORE_SCRIPT" << RESTORE_EOF cat > "$RESTORE_SCRIPT" << 'RESTORE_SCRIPT_EOF'
#!/bin/bash #!/bin/bash
# ============================================================================= # =============================================================================
# RESTORE SCRIPT — Gitea Backup $TIMESTAMP # RESTORE SCRIPT — Backup __DOCKER_APP_NAME__ __TIMESTAMP__
# Généré le $(date) # Généré le __GEN_DATE__
# ============================================================================= # =============================================================================
# Usage : bash restore.sh [OPTIONS] # Usage : bash restore.sh [OPTIONS]
# #
@@ -368,59 +378,60 @@ cat > "$RESTORE_SCRIPT" << RESTORE_EOF
set -euo pipefail set -euo pipefail
# ─── Configuration cible ──────────────────────────────────────────────────── # ─── Configuration cible ────────────────────────────────────────────────────
TARGET_COMPOSE_DIR="\${TARGET_COMPOSE_DIR:-\$HOME/dev/git/outils/docker/gitea}" DOCKER_APP_NAME="${DOCKER_APP_NAME:-__DOCKER_APP_NAME__}"
TARGET_VOLUME_DATA="\${TARGET_VOLUME_DATA:-/DOCKER_CONFIG/gitea/data}" TARGET_COMPOSE_DIR="${TARGET_COMPOSE_DIR:-$HOME/dev/git/outils/$DOCKER_APP_NAME}"
TARGET_VOLUME_MYSQL="\${TARGET_VOLUME_MYSQL:-/DOCKER_CONFIG/gitea/mysql}" TARGET_VOLUME_DATA="${TARGET_VOLUME_DATA:-/DOCKER_CONFIG/$DOCKER_APP_NAME/data}"
DB_ROOT_PASSWORD="\${DB_ROOT_PASSWORD:-gitea}" TARGET_VOLUME_MYSQL="${TARGET_VOLUME_MYSQL:-/DOCKER_CONFIG/$DOCKER_APP_NAME/mysql}"
DB_NAME="\${DB_NAME:-gitea}" DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-$DOCKER_APP_NAME}"
DB_USER="\${DB_USER:-gitea}" DB_NAME="${DB_NAME:-$DOCKER_APP_NAME}"
DB_PASSWORD="\${DB_PASSWORD:-chab30}" DB_USER="${DB_USER:-$DOCKER_APP_NAME}"
DRY_RUN=false DRY_RUN=false
FORCE=false FORCE=false
for arg in "\$@"; do for arg in "$@"; do
case "\$arg" in case "$arg" in
--dry-run) DRY_RUN=true ;; --dry-run) DRY_RUN=true ;;
--force) FORCE=true ;; --force) FORCE=true ;;
--help|-h) head -50 "\$0"; exit 0 ;; --help|-h) head -50 "$0"; exit 0 ;;
esac esac
done done
SCRIPT_DIR="\$(cd "\$(dirname "\$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BACKUP_DIR="\$SCRIPT_DIR" BACKUP_DIR="$SCRIPT_DIR"
DB_CONTAINER="${DOCKER_APP_NAME}_db"
echo "════════════════════════════════════════════════════════" echo "════════════════════════════════════════════════════════"
echo "🔄 RESTAURATION GITEA" echo "🔄 RESTAURATION ${DOCKER_APP_NAME^^}"
echo " Source : \$BACKUP_DIR" echo " Source : $BACKUP_DIR"
echo " Date : $TIMESTAMP" echo " Date : __TIMESTAMP__"
echo " Cible : \$TARGET_COMPOSE_DIR" echo " Cible : $TARGET_COMPOSE_DIR"
echo "════════════════════════════════════════════════════════" echo "════════════════════════════════════════════════════════"
if [[ "\$DRY_RUN" == "true" ]]; then if [[ "$DRY_RUN" == "true" ]]; then
echo "" echo ""
echo "🔍 DRY RUN — aucune action ne sera effectuée" echo "🔍 DRY RUN — aucune action ne sera effectuée"
echo "" echo ""
echo "Actions qui seraient exécutées :" echo "Actions qui seraient exécutées :"
echo " 1. mkdir -p \$TARGET_VOLUME_DATA \$TARGET_VOLUME_MYSQL" echo " 1. mkdir -p $TARGET_VOLUME_DATA $TARGET_VOLUME_MYSQL"
echo " 2. Extraire gitea-mysql.tar.gz → \$TARGET_VOLUME_MYSQL" echo " 2. Extraire ${DOCKER_APP_NAME}-mysql.tar.gz → $TARGET_VOLUME_MYSQL"
echo " 3. Extraire gitea-data.tar.gz → \$TARGET_VOLUME_DATA" echo " 3. Extraire ${DOCKER_APP_NAME}-data.tar.gz → $TARGET_VOLUME_DATA"
echo " 4. Copier docker-compose.yml → \$TARGET_COMPOSE_DIR/" echo " 4. Copier docker-compose.yml → $TARGET_COMPOSE_DIR/"
echo " 5. docker compose up -d" echo " 5. docker compose up -d"
echo " 6. Attendre MySQL prêt" echo " 6. Attendre MySQL prêt"
echo " 7. docker exec gitea_db mysql ... < $DB_DUMP_FILE" echo " 7. docker exec ${DB_CONTAINER} mysql ... < __DB_DUMP_FILE__"
echo " 8. docker compose restart" echo " 8. docker compose restart"
echo "" echo ""
exit 0 exit 0
fi fi
if [[ "\$FORCE" != "true" ]]; then if [[ "$FORCE" != "true" ]]; then
echo "" echo ""
echo "⚠️ ATTENTION : Ceci va ÉCRASER les données Gitea existantes !" echo "⚠️ ATTENTION : Ceci va ÉCRASER les données ${DOCKER_APP_NAME} existantes !"
echo " Toute donnée non sauvegardée sera PERDUE." echo " Toute donnée non sauvegardée sera PERDUE."
echo "" echo ""
read -p "Confirmer la restauration ? (taper 'RESTORE' en majuscules) : " CONFIRM read -p "Confirmer la restauration ? (taper 'RESTORE' en majuscules) : " CONFIRM
if [[ "\$CONFIRM" != "RESTORE" ]]; then if [[ "$CONFIRM" != "RESTORE" ]]; then
echo "❌ Restauration annulée." echo "❌ Restauration annulée."
exit 0 exit 0
fi fi
@@ -428,23 +439,23 @@ fi
echo "" echo ""
echo "📦 Étape 1/6 : Création des répertoires..." echo "📦 Étape 1/6 : Création des répertoires..."
mkdir -p "\$TARGET_VOLUME_DATA" "\$TARGET_VOLUME_MYSQL" "\$TARGET_COMPOSE_DIR" mkdir -p "$TARGET_VOLUME_DATA" "$TARGET_VOLUME_MYSQL" "$TARGET_COMPOSE_DIR"
echo "📦 Étape 2/6 : Restauration volume MySQL..." echo "📦 Étape 2/6 : Restauration volume MySQL..."
rm -rf "\${TARGET_VOLUME_MYSQL:?}"/* rm -rf "${TARGET_VOLUME_MYSQL:?}"/*
tar -xzf "\$BACKUP_DIR/gitea-mysql.tar.gz" -C "\$(dirname "\$TARGET_VOLUME_MYSQL")" tar -xzf "$BACKUP_DIR/${DOCKER_APP_NAME}-mysql.tar.gz" -C "$(dirname "$TARGET_VOLUME_MYSQL")"
echo "📦 Étape 3/6 : Restauration volume Gitea..." echo "📦 Étape 3/6 : Restauration volume ${DOCKER_APP_NAME}..."
rm -rf "\${TARGET_VOLUME_DATA:?}"/* rm -rf "${TARGET_VOLUME_DATA:?}"/*
tar -xzf "\$BACKUP_DIR/gitea-data.tar.gz" -C "\$(dirname "\$TARGET_VOLUME_DATA")" tar -xzf "$BACKUP_DIR/${DOCKER_APP_NAME}-data.tar.gz" -C "$(dirname "$TARGET_VOLUME_DATA")"
echo "📦 Étape 4/6 : Copie docker-compose..." echo "📦 Étape 4/6 : Copie docker-compose..."
cp "\$BACKUP_DIR/docker-compose.yml" "\$TARGET_COMPOSE_DIR/" cp "$BACKUP_DIR/docker-compose.yml" "$TARGET_COMPOSE_DIR/"
# Restaurer app.ini si présent # Restaurer app.ini si présent
if [[ -f "\$BACKUP_DIR/app.ini" ]]; then if [[ -f "$BACKUP_DIR/app.ini" ]]; then
mkdir -p "\${TARGET_VOLUME_DATA}/gitea/conf" mkdir -p "${TARGET_VOLUME_DATA}/${DOCKER_APP_NAME}/conf"
cp "\$BACKUP_DIR/app.ini" "\${TARGET_VOLUME_DATA}/gitea/conf/app.ini" cp "$BACKUP_DIR/app.ini" "${TARGET_VOLUME_DATA}/${DOCKER_APP_NAME}/conf/app.ini"
echo " ✅ app.ini restauré" echo " ✅ app.ini restauré"
fi fi
@@ -459,48 +470,59 @@ else
fi fi
echo "📦 Étape 5/6 : Démarrage des conteneurs..." echo "📦 Étape 5/6 : Démarrage des conteneurs..."
cd "\$TARGET_COMPOSE_DIR" cd "$TARGET_COMPOSE_DIR"
\$DC up -d $DC up -d
echo " ⏳ Attente démarrage MySQL (30s)..." echo " ⏳ Attente démarrage MySQL (30s)..."
sleep 10 sleep 10
MYSQL_READY=false MYSQL_READY=false
for i in \$(seq 1 20); do for i in $(seq 1 20); do
if docker exec gitea_db mysqladmin ping -u root -p"\$DB_ROOT_PASSWORD" --silent 2>/dev/null; then if docker exec "$DB_CONTAINER" mysqladmin ping -u root -p"$DB_ROOT_PASSWORD" --silent 2>/dev/null; then
echo " ✅ MySQL prêt après \$((10 + i))s" echo " ✅ MySQL prêt après $((10 + i))s"
MYSQL_READY=true MYSQL_READY=true
break break
fi fi
sleep 1 sleep 1
done done
if [[ "\$MYSQL_READY" != "true" ]]; then if [[ "$MYSQL_READY" != "true" ]]; then
echo "⚠️ MySQL n'a pas répondu dans le temps imparti, tentative de restore DB quand même..." echo "⚠️ MySQL n'a pas répondu dans le temps imparti, tentative de restore DB quand même..."
fi fi
echo "📦 Étape 6/6 : Restauration base de données..." echo "📦 Étape 6/6 : Restauration base de données..."
DB_DUMP="\$BACKUP_DIR/$DB_DUMP_FILE" # Créer la base si elle n'existe pas
if [[ -f "\${DB_DUMP}.gz" ]]; then docker exec "$DB_CONTAINER" mysql -u root -p"$DB_ROOT_PASSWORD" -e "CREATE DATABASE IF NOT EXISTS \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" 2>/dev/null || true
gunzip -c "\${DB_DUMP}.gz" | docker exec -i gitea_db mysql -u root -p"\$DB_ROOT_PASSWORD" "\$DB_NAME"
elif [[ -f "\$DB_DUMP" ]]; then DB_DUMP="$BACKUP_DIR/__DB_DUMP_FILE__"
docker exec -i gitea_db mysql -u root -p"\$DB_ROOT_PASSWORD" "\$DB_NAME" < "\$DB_DUMP" if [[ -f "${DB_DUMP}" ]]; then
if [[ "$DB_DUMP" == *.gz ]]; then
gunzip -c "$DB_DUMP" | docker exec -i "$DB_CONTAINER" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME"
else
docker exec -i "$DB_CONTAINER" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" < "$DB_DUMP"
fi
else else
echo "❌ Fichier dump introuvable : \$DB_DUMP" echo "❌ Fichier dump introuvable : $DB_DUMP"
exit 1 exit 1
fi fi
echo "" echo ""
echo "🔄 Redémarrage final des conteneurs..." echo "🔄 Redémarrage final des conteneurs..."
\$DC restart $DC restart
echo "" echo ""
echo "════════════════════════════════════════════════════════" echo "════════════════════════════════════════════════════════"
echo "✅ RESTAURATION TERMINÉE !" echo "✅ RESTAURATION TERMINÉE !"
echo "" echo ""
echo " Gitea : http://localhost:3000" echo " ${DOCKER_APP_NAME^} est maintenant opérationnel"
echo " Vérifiez les logs : \$DC logs -f" echo " Vérifiez les logs : $DC logs -f"
echo "════════════════════════════════════════════════════════" echo "════════════════════════════════════════════════════════"
RESTORE_EOF RESTORE_SCRIPT_EOF
# Remplacer les placeholders dans le script généré
sed -i "s/__DOCKER_APP_NAME__/$DOCKER_APP_NAME/g" "$RESTORE_SCRIPT"
sed -i "s/__TIMESTAMP__/$TIMESTAMP/g" "$RESTORE_SCRIPT"
sed -i "s/__GEN_DATE__/$(date)/g" "$RESTORE_SCRIPT"
sed -i "s/__DB_DUMP_FILE__/$DB_DUMP_FILE/g" "$RESTORE_SCRIPT"
chmod +x "$RESTORE_SCRIPT" chmod +x "$RESTORE_SCRIPT"
log " ✅ Script restore créé : restore.sh" log " ✅ Script restore créé : restore.sh"
@@ -521,7 +543,7 @@ if [[ "$VERIFY_BACKUP" == "true" ]]; then
shopt -u nullglob shopt -u nullglob
if [[ "$COMPRESS" == "true" ]]; then if [[ "$COMPRESS" == "true" ]]; then
if ! gunzip -t "${BACKUP_PATH}/gitea-db.sql.gz" 2>/dev/null; then if ! gunzip -t "${BACKUP_PATH}/${DOCKER_APP_NAME}-db.sql.gz" 2>/dev/null; then
log " ❌ Dump SQL corrompu" log " ❌ Dump SQL corrompu"
ERRORS=$((ERRORS + 1)) ERRORS=$((ERRORS + 1))
fi fi
@@ -535,18 +557,19 @@ if [[ "$VERIFY_BACKUP" == "true" ]]; then
fi fi
# ─── 6. Métadonnées du backup ─────────────────────────────────────────────── # ─── 6. Métadonnées du backup ───────────────────────────────────────────────
GITEA_VERSION="inconnue" APP_VERSION="inconnue"
if docker ps --format '{{.Names}}' | grep -q "^${APP_CONTAINER}$"; then if docker ps --format '{{.Names}}' | grep -q "^${APP_CONTAINER}$"; then
GITEA_VERSION=$(docker exec "$APP_CONTAINER" gitea --version 2>/dev/null || echo "inconnue") APP_VERSION=$(docker inspect "$APP_CONTAINER" --format '{{.Config.Image}}' 2>/dev/null || echo "inconnue")
fi fi
DB_DISPLAY_SIZE=$(ls -lh "${BACKUP_PATH}"/gitea-db.sql* 2>/dev/null | awk '{print $5}' | head -1) DB_DISPLAY_SIZE=$(ls -lh "${BACKUP_PATH}"/${DOCKER_APP_NAME}-db.sql* 2>/dev/null | awk '{print $5}' | head -1)
cat > "${BACKUP_PATH}/backup-info.txt" << INFO cat > "${BACKUP_PATH}/backup-info.txt" << INFO
Date : $(date) Date : $(date)
Hôte : $(hostname) Hôte : $(hostname)
Gitea version : $GITEA_VERSION Application : ${DOCKER_APP_NAME^}
DB type : MySQL 8 App version : $APP_VERSION
DB type : MySQL
DB name : $DB_NAME DB name : $DB_NAME
Compression : $COMPRESS Compression : $COMPRESS
Containers stopped : $CONTAINERS_WERE_STOPPED Containers stopped : $CONTAINERS_WERE_STOPPED
@@ -559,11 +582,11 @@ INFO
log "🧹 Nettoyage backups (rétention: ${RETENTION_DAYS}j, ${RETENTION_WEEKLY} hebdo, ${RETENTION_MONTHLY} mensuels)..." log "🧹 Nettoyage backups (rétention: ${RETENTION_DAYS}j, ${RETENTION_WEEKLY} hebdo, ${RETENTION_MONTHLY} mensuels)..."
# Pour chaque backup, déterminer s'il doit être gardé # Pour chaque backup, déterminer s'il doit être gardé
find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -printf '%f\n' 2>/dev/null | sort -r | while read -r backup_name; do find "$BACKUP_DIR" -maxdepth 1 -type d -name "${DOCKER_APP_NAME}-backup-*" -printf '%f\n' 2>/dev/null | sort -r | while read -r backup_name; do
backup_path="$BACKUP_DIR/$backup_name" backup_path="$BACKUP_DIR/$backup_name"
# Déterminer le jour du mois et le jour de la semaine à partir du nom # Déterminer le jour du mois et le jour de la semaine à partir du nom
# Format: gitea-backup-YYYYMMDD-HHMMSS # Format: APP-backup-YYYYMMDD-HHMMSS
local_date=$(echo "$backup_name" | grep -oE '[0-9]{8}' | head -1) local_date=$(echo "$backup_name" | grep -oE '[0-9]{8}' | head -1)
if [[ -z "$local_date" ]]; then if [[ -z "$local_date" ]]; then
continue continue
@@ -593,8 +616,9 @@ find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -printf '%f\n' 2>/
# Toujours garder : backups du dimanche (hebdomadaire) dans la limite RETENTION_WEEKLY # Toujours garder : backups du dimanche (hebdomadaire) dans la limite RETENTION_WEEKLY
if [[ "$local_dow" == "7" ]]; then if [[ "$local_dow" == "7" ]]; then
WEEKLY_PATTERN="^${DOCKER_APP_NAME}-backup-[0-9]{8}"
# Compter combien de backups du dimanche on garde déjà # Compter combien de backups du dimanche on garde déjà
weekly_count=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -printf '%f\n' 2>/dev/null | grep -oE '^gitea-backup-[0-9]{8}' | while read -r wb; do weekly_count=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "${DOCKER_APP_NAME}-backup-*" -printf '%f\n' 2>/dev/null | grep -oE "$WEEKLY_PATTERN" | while read -r wb; do
wb_date=$(echo "$wb" | grep -oE '[0-9]{8}') wb_date=$(echo "$wb" | grep -oE '[0-9]{8}')
wb_dow=$(date -d "${wb_date:0:4}-${wb_date:4:2}-${wb_date:6:2}" +%u 2>/dev/null || echo "0") wb_dow=$(date -d "${wb_date:0:4}-${wb_date:4:2}-${wb_date:6:2}" +%u 2>/dev/null || echo "0")
if [[ "$wb_dow" == "7" ]]; then if [[ "$wb_dow" == "7" ]]; then
@@ -603,7 +627,7 @@ find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -printf '%f\n' 2>/
done | sort -r | head -n "$RETENTION_WEEKLY" | wc -l) done | sort -r | head -n "$RETENTION_WEEKLY" | wc -l)
# Si ce backup fait partie des N derniers dimanches, on garde # Si ce backup fait partie des N derniers dimanches, on garde
weekly_list=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -printf '%f\n' 2>/dev/null | grep -oE '^gitea-backup-[0-9]{8}' | while read -r wb; do weekly_list=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "${DOCKER_APP_NAME}-backup-*" -printf '%f\n' 2>/dev/null | grep -oE "$WEEKLY_PATTERN" | while read -r wb; do
wb_date=$(echo "$wb" | grep -oE '[0-9]{8}') wb_date=$(echo "$wb" | grep -oE '[0-9]{8}')
wb_dow=$(date -d "${wb_date:0:4}-${wb_date:4:2}-${wb_date:6:2}" +%u 2>/dev/null || echo "0") wb_dow=$(date -d "${wb_date:0:4}-${wb_date:4:2}-${wb_date:6:2}" +%u 2>/dev/null || echo "0")
if [[ "$wb_dow" == "7" ]]; then if [[ "$wb_dow" == "7" ]]; then
@@ -619,7 +643,8 @@ find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -printf '%f\n' 2>/
# Toujours garder : backups du 1er du mois (mensuel) dans la limite RETENTION_MONTHLY # Toujours garder : backups du 1er du mois (mensuel) dans la limite RETENTION_MONTHLY
if [[ "$local_day" == "01" ]]; then if [[ "$local_day" == "01" ]]; then
monthly_list=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -printf '%f\n' 2>/dev/null | grep -oE '^gitea-backup-[0-9]{8}' | while read -r mb; do MONTHLY_PATTERN="^${DOCKER_APP_NAME}-backup-[0-9]{8}"
monthly_list=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "${DOCKER_APP_NAME}-backup-*" -printf '%f\n' 2>/dev/null | grep -oE "$MONTHLY_PATTERN" | while read -r mb; do
mb_date=$(echo "$mb" | grep -oE '[0-9]{8}') mb_date=$(echo "$mb" | grep -oE '[0-9]{8}')
mb_day=${mb_date:6:2} mb_day=${mb_date:6:2}
if [[ "$mb_day" == "01" ]]; then if [[ "$mb_day" == "01" ]]; then
@@ -646,7 +671,7 @@ sync_remote "$BACKUP_PATH" || true
# ─── 9. Rapport final ─────────────────────────────────────────────────────── # ─── 9. Rapport final ───────────────────────────────────────────────────────
TOTAL_SIZE=$(du -sh "$BACKUP_PATH" 2>/dev/null | cut -f1) TOTAL_SIZE=$(du -sh "$BACKUP_PATH" 2>/dev/null | cut -f1)
BACKUP_COUNT=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" 2>/dev/null | wc -l) BACKUP_COUNT=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "${DOCKER_APP_NAME}-backup-*" 2>/dev/null | wc -l)
log "✅ Backup terminé !" log "✅ Backup terminé !"
log "📊 Total : ${TOTAL_SIZE:-N/A}" log "📊 Total : ${TOTAL_SIZE:-N/A}"
@@ -656,14 +681,14 @@ log "═══ Fin ────────────────────
echo "" echo ""
echo "📂 $BACKUP_PATH" echo "📂 $BACKUP_PATH"
echo " ├── gitea-db.sql.gz ← Dump MySQL" echo " ├── ${DOCKER_APP_NAME}-db.sql.gz ← Dump MySQL"
echo " ├── gitea-data.tar.gz ← Volume /data" echo " ├── ${DOCKER_APP_NAME}-data.tar.gz ← Volume /data"
echo " ├── gitea-mysql.tar.gz ← Volume MySQL" echo " ├── ${DOCKER_APP_NAME}-mysql.tar.gz ← Volume MySQL"
echo " ├── docker-compose.yml ← Stack Docker" echo " ├── docker-compose.yml ← Stack Docker"
echo " ├── app.ini ← Configuration Gitea" echo " ├── app.ini ← Configuration ${DOCKER_APP_NAME^}"
echo " ├── restore.sh ← Script de restore" echo " ├── restore.sh ← Script de restore"
echo " └── backup-info.txt ← Métadonnées" echo " └── backup-info.txt ← Métadonnées"
notify "✅ Backup Gitea terminé sur $(hostname) — $TOTAL_SIZE — $BACKUP_PATH" notify "✅ Backup ${DOCKER_APP_NAME^} terminé sur $(hostname) — $TOTAL_SIZE — $BACKUP_PATH"
exit 0 exit 0
+2
View File
@@ -45,6 +45,8 @@ VOLUME_MYSQL="${VOLUME_MYSQL:-/DOCKER_CONFIG/$DOCKER_APP_NAME/mysql}"
# ─── URLs ───────────────────────────────────────────────────────────────────── # ─── URLs ─────────────────────────────────────────────────────────────────────
HEALTH_CHECK_URL="${HEALTH_CHECK_URL:-http://localhost:3000}" HEALTH_CHECK_URL="${HEALTH_CHECK_URL:-http://localhost:3000}"
HEALTH_CHECK_TIMEOUT="${HEALTH_CHECK_TIMEOUT:-30}" HEALTH_CHECK_TIMEOUT="${HEALTH_CHECK_TIMEOUT:-30}"
# Endpoint version (ex: /api/v1/version pour Gitea). Laisser vide pour détection auto.
VERSION_ENDPOINT="${VERSION_ENDPOINT:-}"
# ─── Logs (dérivés de DOCKER_APP_NAME) ──────────────────────────────────────── # ─── Logs (dérivés de DOCKER_APP_NAME) ────────────────────────────────────────
LOG_DIR="${LOG_DIR:-/var/log}" LOG_DIR="${LOG_DIR:-/var/log}"
+2 -2
View File
@@ -394,7 +394,7 @@ $DOCKER_COMPOSE restart 2>&1 || true
echo "" echo ""
echo "🏥 Vérification que ${DOCKER_APP_NAME} répond..." echo "🏥 Vérification que ${DOCKER_APP_NAME} répond..."
for i in $(seq 1 30); do for i in $(seq 1 30); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:3000" 2>/dev/null || echo "000") STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${HEALTH_CHECK_URL:-http://localhost:3000}" 2>/dev/null || echo "000")
if [[ "$STATUS" == "200" || "$STATUS" == "302" || "$STATUS" == "303" ]]; then if [[ "$STATUS" == "200" || "$STATUS" == "302" || "$STATUS" == "303" ]]; then
ok "${DOCKER_APP_NAME} répond (HTTP $STATUS) après ${i}s" ok "${DOCKER_APP_NAME} répond (HTTP $STATUS) après ${i}s"
break break
@@ -416,7 +416,7 @@ echo ""
echo " Durée : ${DURATION}s" echo " Durée : ${DURATION}s"
echo " Backup : $BACKUP_NAME" echo " Backup : $BACKUP_NAME"
echo " Cible : $TARGET_COMPOSE_DIR" echo " Cible : $TARGET_COMPOSE_DIR"
echo " ${DOCKER_APP_NAME} : http://localhost:3000" echo " ${DOCKER_APP_NAME} : ${HEALTH_CHECK_URL:-http://localhost:3000}"
echo "" echo ""
echo " Logs : $DOCKER_COMPOSE logs -f" echo " Logs : $DOCKER_COMPOSE logs -f"
echo " Status : $DOCKER_COMPOSE ps" echo " Status : $DOCKER_COMPOSE ps"