Correction et enrichissement complet des scripts Gitea
- Corrections de bugs (14): grep portable, df robuste, rollback reel, retention intelligente, nettoyage cible, lock anti-concurrence, etc. - Nouveaux fichiers: setup.sh (installateur tout-en-un), restore.sh (restauration standalone), gitea-scripts.conf (config unifiee) - Features: backup distant (rsync/SCP), lock files, logrotate, dry-run, healthchecks Docker, notifications Telegram/ntfy.sh - Documentation enrichie avec diagrammes d'architecture mermaid
This commit is contained in:
+303
-69
@@ -1,71 +1,151 @@
|
|||||||
# Scripts Gitea — Auto-Update, Backup & Déploiement Cron
|
# Scripts Gitea — Auto-Update, Backup, Restore & Déploiement Cron
|
||||||
|
|
||||||
Trois scripts pour la maintenance automatisée de Gitea Docker.
|
Solution complète pour la maintenance automatisée de Gitea Docker :
|
||||||
|
backup, mise à jour, restauration et déploiement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Structure du projet
|
||||||
|
|
||||||
|
```
|
||||||
|
gitea-scripts/
|
||||||
|
├── setup.sh ← 🚀 Installateur tout-en-un
|
||||||
|
├── gitea-scripts.conf ← ⚙️ Configuration unifiée
|
||||||
|
├── gitea-backup.sh ← 💾 Backup complet (DB + volumes + config)
|
||||||
|
├── gitea-auto-update.sh ← 🔄 Mise à jour automatique
|
||||||
|
├── restore.sh ← ♻️ Restauration standalone
|
||||||
|
├── install_cron.sh ← 🕐 Gestion des crons
|
||||||
|
└── docker-compose.yml ← 🐳 Stack Docker de référence
|
||||||
|
```
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph Config["⚙️ Configuration"]
|
||||||
|
CONF["gitea-scripts.conf<br/>Configuration unifiée"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Core["🛠️ Scripts core"]
|
||||||
|
BACKUP["gitea-backup.sh<br/>💾 Backup quotidien"]
|
||||||
|
UPDATE["gitea-auto-update.sh<br/>🔄 Mise à jour auto"]
|
||||||
|
RESTORE["restore.sh<br/>♻️ Restauration"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Deploy["🚀 Déploiement"]
|
||||||
|
SETUP["setup.sh<br/>Installateur complet"]
|
||||||
|
CRON["install_cron.sh<br/>Gestion crontab"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Runtime["⚡ Runtime"]
|
||||||
|
CRONTAB["Crontab<br/>Backup 2h → Update 4h"]
|
||||||
|
DOCKER["Docker Compose<br/>Gitea + MySQL"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Storage["💾 Stockage"]
|
||||||
|
LOCAL["Backups locaux<br/>/NFS/BACKUP/..."]
|
||||||
|
REMOTE["Backup distant<br/>rsync / SCP"]
|
||||||
|
end
|
||||||
|
|
||||||
|
CONF --> BACKUP
|
||||||
|
CONF --> UPDATE
|
||||||
|
CONF --> RESTORE
|
||||||
|
CONF --> CRON
|
||||||
|
|
||||||
|
SETUP --> CRON
|
||||||
|
SETUP --> DOCKER
|
||||||
|
CRON --> CRONTAB
|
||||||
|
CRONTAB --> BACKUP
|
||||||
|
CRONTAB --> UPDATE
|
||||||
|
|
||||||
|
BACKUP --> LOCAL
|
||||||
|
BACKUP --> REMOTE
|
||||||
|
RESTORE --> LOCAL
|
||||||
|
RESTORE --> DOCKER
|
||||||
|
UPDATE --> DOCKER
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph Daily["📅 Quotidien (cron)"]
|
||||||
|
direction TB
|
||||||
|
A["🕐 2h00"] --> B["gitea-backup.sh"]
|
||||||
|
B --> C["💾 Backup DB + volumes"]
|
||||||
|
C --> D["📝 Génère restore.sh"]
|
||||||
|
C --> E["☁️ Sync distante"]
|
||||||
|
C --> F["🧹 Nettoie vieux backups"]
|
||||||
|
|
||||||
|
G["🕐 4h00"] --> H["gitea-auto-update.sh"]
|
||||||
|
H --> I{"Nouvelle image ?"}
|
||||||
|
I -->|"Non"| J["✅ Rien à faire"]
|
||||||
|
I -->|"Oui"| K["💾 Backup DB rapide"]
|
||||||
|
K --> L["🚀 docker compose up -d"]
|
||||||
|
L --> M{"Santé OK ?"}
|
||||||
|
M -->|"Oui"| N["✅ Mise à jour réussie"]
|
||||||
|
M -->|"Non"| O["🔄 Rollback auto"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Manual["🖐️ Manuel"]
|
||||||
|
direction TB
|
||||||
|
P["setup.sh"] --> Q["Installation complète"]
|
||||||
|
R["restore.sh"] --> S["Restauration backup"]
|
||||||
|
T["setup.sh --update"] --> U["Màj scripts (git pull)"]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Installation rapide
|
## 🚀 Installation rapide
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Cloner les scripts
|
# Cloner et installer en une commande
|
||||||
git clone https://git.dracodev.net/Outils/gitea-scripts.git ~/scripts
|
git clone https://git.dracodev.net/Outils/gitea-scripts.git ~/scripts
|
||||||
|
bash ~/scripts/setup.sh
|
||||||
# Installer dans le cron (interactif)
|
|
||||||
bash ~/scripts/install_cron.sh
|
|
||||||
|
|
||||||
# Ou forcer sans confirmation
|
# Ou forcer sans confirmation
|
||||||
bash ~/scripts/install_cron.sh --force
|
bash ~/scripts/setup.sh --force
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 Prérequis
|
## 📋 Prérequis
|
||||||
|
|
||||||
- Docker + Docker Compose
|
- Docker + Docker Compose (plugin ou standalone)
|
||||||
- `curl`, `tar`, `gzip`, `mysqldump` (dans le conteneur MySQL)
|
- `git`, `curl`, `tar`, `gzip`, `crontab`
|
||||||
- Droits d'écriture sur `/NFS/BACKUP/DOCKER/dev-prod/gitea/` (backup)
|
- `mysqldump` (dans le conteneur MySQL)
|
||||||
|
- Droits d'écriture sur le répertoire de backup
|
||||||
- Droits sudo si les volumes sont protégés
|
- Droits sudo si les volumes sont protégés
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔄 gitea-auto-update.sh
|
## 🛠️ Scripts
|
||||||
|
|
||||||
Vérifie quotidiennement si une nouvelle image Docker Gitea est disponible et applique la mise à jour.
|
### 🚀 `setup.sh` — Installation complète
|
||||||
|
|
||||||
### Paramètres configurables
|
Installateur tout-en-un qui configure l'ensemble du système :
|
||||||
|
|
||||||
| Variable | Défaut | Description |
|
```bash
|
||||||
|----------|--------|-------------|
|
bash setup.sh # Installation interactive
|
||||||
| `COMPOSE_DIR` | `~/dev/git/outils/docker/gitea` | Dossier du docker-compose |
|
bash setup.sh --force # Sans confirmation
|
||||||
| `COMPOSE_FILE` | `docker-compose.yml` | Nom du fichier compose |
|
bash setup.sh --update # Mise à jour des scripts (git pull)
|
||||||
| `CONTAINER_NAME` | `gitea_server` | Conteneur Gitea |
|
bash setup.sh --dry-run # Aperçu sans rien installer
|
||||||
| `IMAGE_NAME` | `gitea/gitea` | Image Docker |
|
|
||||||
| `IMAGE_TAG` | `latest` | Tag (version) |
|
|
||||||
| `HEALTH_CHECK_URL` | `http://localhost:3000` | URL vérification santé |
|
|
||||||
| `HEALTH_CHECK_TIMEOUT` | `30` | Timeout attente (secondes) |
|
|
||||||
|
|
||||||
### Cron recommandé
|
|
||||||
|
|
||||||
```cron
|
|
||||||
# Tous les jours à 4h du matin
|
|
||||||
0 4 * * * /home/openclaw/scripts/gitea-auto-update.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fonctionnement
|
**Étapes exécutées :**
|
||||||
|
1. Vérification des prérequis système
|
||||||
1. Récupère le digest SHA256 de l'image actuelle
|
2. Clone/mise à jour depuis Git
|
||||||
2. `docker compose pull` pour télécharger la dernière version
|
3. Permissions d'exécution
|
||||||
3. Compare les digests → si identique, rien à faire
|
4. Configuration des logs
|
||||||
4. Si différent : backup rapide DB → `docker compose up -d` → vérification santé
|
5. Configuration logrotate (rotation automatique des logs)
|
||||||
5. Nettoie les vieilles images Docker (`image prune`)
|
6. Installation des tâches cron
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 💾 gitea-backup.sh
|
### 💾 `gitea-backup.sh` — Backup complet
|
||||||
|
|
||||||
Backup complet et portable de Gitea : DB MySQL + volumes Docker + config.
|
Sauvegarde complète et portable : DB MySQL + volumes Docker + configuration.
|
||||||
|
|
||||||
### Paramètres configurables
|
#### Paramètres configurables
|
||||||
|
|
||||||
| Variable | Défaut | Description |
|
| Variable | Défaut | Description |
|
||||||
|----------|--------|-------------|
|
|----------|--------|-------------|
|
||||||
@@ -82,15 +162,16 @@ Backup complet et portable de Gitea : DB MySQL + volumes Docker + config.
|
|||||||
| `COMPRESS` | `true` | Compression gzip |
|
| `COMPRESS` | `true` | Compression gzip |
|
||||||
| `VERIFY_BACKUP` | `true` | Vérifier intégrité après backup |
|
| `VERIFY_BACKUP` | `true` | Vérifier intégrité après backup |
|
||||||
| `STOP_CONTAINERS` | `false` | Arrêter conteneurs (plus sûr) |
|
| `STOP_CONTAINERS` | `false` | Arrêter conteneurs (plus sûr) |
|
||||||
|
| `REMOTE_BACKUP_ENABLED` | `false` | Activer backup distant (rsync/SCP) |
|
||||||
|
|
||||||
### Cron recommandé
|
#### Cron recommandé
|
||||||
|
|
||||||
```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/gitea-backup.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Structure d'un backup
|
#### Structure d'un backup
|
||||||
|
|
||||||
```
|
```
|
||||||
/NFS/BACKUP/DOCKER/dev-prod/gitea/
|
/NFS/BACKUP/DOCKER/dev-prod/gitea/
|
||||||
@@ -104,48 +185,105 @@ Backup complet et portable de Gitea : DB MySQL + volumes Docker + config.
|
|||||||
└── backup-info.txt ← Métadonnées
|
└── backup-info.txt ← Métadonnées
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restauration
|
#### Backup distant (optionnel)
|
||||||
|
|
||||||
|
Configurer dans `gitea-scripts.conf` :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-20260626-020000/
|
REMOTE_BACKUP_ENABLED=true
|
||||||
|
REMOTE_BACKUP_METHOD=rsync # ou scp
|
||||||
# Dry-run (vérifier sans exécuter)
|
REMOTE_BACKUP_HOST=backup.example.com
|
||||||
bash restore.sh --dry-run
|
REMOTE_BACKUP_USER=backup
|
||||||
|
REMOTE_BACKUP_PATH=/backups/gitea
|
||||||
# Restauration réelle
|
REMOTE_BACKUP_SSH_KEY=/home/user/.ssh/id_rsa
|
||||||
bash restore.sh --force
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Le script `restore.sh` est **auto-généré** à chaque backup et contient TOUT ce qu'il faut pour restaurer sur n'importe quel hôte Docker.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 install_cron.sh — Déploiement automatisé
|
### 🔄 `gitea-auto-update.sh` — Mise à jour automatique
|
||||||
|
|
||||||
Installe (ou désinstalle) les crons d'un seul coup.
|
Vérifie quotidiennement si une nouvelle image Docker Gitea est disponible.
|
||||||
|
|
||||||
### Commandes
|
#### Paramètres configurables
|
||||||
|
|
||||||
|
| Variable | Défaut | Description |
|
||||||
|
|----------|--------|-------------|
|
||||||
|
| `COMPOSE_DIR` | `~/dev/git/outils/docker/gitea` | Dossier du docker-compose |
|
||||||
|
| `APP_SERVICE` | `gitea_server` | Nom du service dans compose |
|
||||||
|
| `IMAGE_NAME` | `gitea/gitea` | Image Docker |
|
||||||
|
| `IMAGE_TAG` | `latest` | Tag (version) |
|
||||||
|
| `HEALTH_CHECK_URL` | `http://localhost:3000` | URL vérification santé |
|
||||||
|
| `HEALTH_CHECK_TIMEOUT` | `30` | Timeout attente (secondes) |
|
||||||
|
|
||||||
|
#### Fonctionnement
|
||||||
|
|
||||||
|
1. Récupère le digest SHA256 de l'image actuelle
|
||||||
|
2. `docker compose pull` pour télécharger la dernière version
|
||||||
|
3. Compare les digests → si identique, rien à faire
|
||||||
|
4. Si différent :
|
||||||
|
- Backup rapide de la DB
|
||||||
|
- Sauvegarde de l'image actuelle pour rollback
|
||||||
|
- `docker compose up -d` avec la nouvelle image
|
||||||
|
- Vérification santé
|
||||||
|
- **Rollback automatique** en cas d'échec
|
||||||
|
5. Nettoyage ciblé des anciennes images Gitea
|
||||||
|
|
||||||
|
#### Notifications (optionnel)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Telegram
|
||||||
|
ENABLE_NOTIFICATIONS=true NOTIFY_METHOD=telegram \
|
||||||
|
TELEGRAM_BOT_TOKEN=xxx TELEGRAM_CHAT_ID=xxx \
|
||||||
|
bash gitea-auto-update.sh
|
||||||
|
|
||||||
|
# ntfy.sh
|
||||||
|
ENABLE_NOTIFICATIONS=true NOTIFY_METHOD=ntfy \
|
||||||
|
NTPY_TOPIC=mon-topic \
|
||||||
|
bash gitea-auto-update.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ♻️ `restore.sh` — Restauration standalone
|
||||||
|
|
||||||
|
Restaure un backup Gitea sur n'importe quel hôte Docker.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lister les backups disponibles
|
||||||
|
bash restore.sh
|
||||||
|
|
||||||
|
# Restauration interactive
|
||||||
|
bash restore.sh /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-20260626-020000
|
||||||
|
|
||||||
|
# Dry-run (vérifier sans exécuter)
|
||||||
|
bash restore.sh /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-20260626-020000 --dry-run
|
||||||
|
|
||||||
|
# Restauration forcée (sans confirmation)
|
||||||
|
bash restore.sh /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-20260626-020000 --force
|
||||||
|
```
|
||||||
|
|
||||||
|
**Étapes de restauration :**
|
||||||
|
1. Arrêt des conteneurs existants
|
||||||
|
2. Création des répertoires cibles
|
||||||
|
3. Restauration du volume MySQL
|
||||||
|
4. Restauration du volume Gitea
|
||||||
|
5. Copie du docker-compose.yml et app.ini
|
||||||
|
6. Démarrage des conteneurs
|
||||||
|
7. Restauration de la base de données + redémarrage final
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🕐 `install_cron.sh` — Gestion des crons
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash install_cron.sh # Installation interactive
|
bash install_cron.sh # Installation interactive
|
||||||
bash install_cron.sh --force # Sans confirmation
|
bash install_cron.sh --force # Sans confirmation
|
||||||
|
bash install_cron.sh --dry-run # Aperçu sans rien installer
|
||||||
bash install_cron.sh --uninstall # Retirer du cron
|
bash install_cron.sh --uninstall # Retirer du cron
|
||||||
bash install_cron.sh --status # Vérifier l'état
|
bash install_cron.sh --status # Vérifier l'état
|
||||||
```
|
```
|
||||||
|
|
||||||
### Paramètres configurables
|
Le script gère un **bloc marqué** dans la crontab :
|
||||||
|
|
||||||
| Variable | Défaut | Description |
|
|
||||||
|----------|--------|-------------|
|
|
||||||
| `SCRIPTS_DIR` | `$HOME/scripts` | Dossier des scripts |
|
|
||||||
| `CRON_BACKUP_SCHEDULE` | `0 2 * * *` | Horaire backup |
|
|
||||||
| `CRON_UPDATE_SCHEDULE` | `0 4 * * *` | Horaire update |
|
|
||||||
| `BACKUP_LOG` | `/var/log/gitea-backup-cron.log` | Log backup |
|
|
||||||
| `UPDATE_LOG` | `/var/log/gitea-update-cron.log` | Log update |
|
|
||||||
|
|
||||||
### Gestion propre
|
|
||||||
|
|
||||||
Le script ajoute un **bloc marqué** dans la crontab :
|
|
||||||
|
|
||||||
```cron
|
```cron
|
||||||
# >>> GITEA SCRIPTS (managed by install_cron.sh) <<<
|
# >>> GITEA SCRIPTS (managed by install_cron.sh) <<<
|
||||||
@@ -158,14 +296,23 @@ Le `--uninstall` retire uniquement ce bloc, sans toucher au reste de la crontab.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔧 Surcharger les paramètres
|
## ⚙️ Configuration centralisée
|
||||||
|
|
||||||
|
Tous les paramètres sont dans `gitea-scripts.conf`. Les scripts chargent ce
|
||||||
|
fichier automatiquement s'il est présent dans le même répertoire.
|
||||||
|
|
||||||
|
Les variables d'environnement **surchargent** les valeurs du fichier de
|
||||||
|
configuration.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Exemple : changer le dossier de backup
|
# Exemple : changer le dossier de backup
|
||||||
BACKUP_DIR=/mnt/big-disk/gitea /home/openclaw/scripts/gitea-backup.sh
|
BACKUP_DIR=/mnt/big-disk/gitea bash gitea-backup.sh
|
||||||
|
|
||||||
# Exemple : backup avec arrêt des conteneurs
|
# Exemple : backup avec arrêt des conteneurs
|
||||||
STOP_CONTAINERS=true /home/openclaw/scripts/gitea-backup.sh
|
STOP_CONTAINERS=true bash gitea-backup.sh
|
||||||
|
|
||||||
|
# Exemple : backup + sync distante
|
||||||
|
REMOTE_BACKUP_ENABLED=true bash gitea-backup.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -176,3 +323,90 @@ STOP_CONTAINERS=true /home/openclaw/scripts/gitea-backup.sh
|
|||||||
|--------|-----|
|
|--------|-----|
|
||||||
| Auto-update | `/var/log/gitea-update.log` |
|
| Auto-update | `/var/log/gitea-update.log` |
|
||||||
| Backup | `/var/log/gitea-backup.log` |
|
| Backup | `/var/log/gitea-backup.log` |
|
||||||
|
| Cron backup | `~/gitea-backup-cron.log` |
|
||||||
|
| Cron update | `~/gitea-update-cron.log` |
|
||||||
|
|
||||||
|
La rotation des logs est configurée automatiquement par `setup.sh` via
|
||||||
|
logrotate (rotation hebdomadaire, 12 semaines de rétention, compression).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Lock files
|
||||||
|
|
||||||
|
Pour éviter les exécutions concurrentes (backup + update simultanés), chaque
|
||||||
|
script utilise un lock file dans `/tmp` :
|
||||||
|
|
||||||
|
- `/tmp/gitea-backup.lock`
|
||||||
|
- `/tmp/gitea-auto-update.lock`
|
||||||
|
|
||||||
|
Si un lock stale est détecté (PID inexistant), il est automatiquement nettoyé.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Mise à jour périodique des scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Mise à jour manuelle
|
||||||
|
bash ~/scripts/setup.sh --update
|
||||||
|
|
||||||
|
# Ajouter au cron pour mise à jour automatique (optionnel)
|
||||||
|
# 0 3 * * 0 bash ~/scripts/setup.sh --update --force
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Docker Compose
|
||||||
|
|
||||||
|
Le fichier `docker-compose.yml` fourni inclut :
|
||||||
|
- **Healthchecks** sur les deux services (MySQL + Gitea)
|
||||||
|
- `depends_on` avec `condition: service_healthy` pour MySQL
|
||||||
|
- Timezone mapping pour les logs horodatés correctement
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Dépannage
|
||||||
|
|
||||||
|
### Le backup échoue par manque d'espace
|
||||||
|
```bash
|
||||||
|
# Vérifier l'espace disponible
|
||||||
|
df -h /NFS/BACKUP/DOCKER/dev-prod/gitea
|
||||||
|
|
||||||
|
# Nettoyer les vieux backups manuellement
|
||||||
|
ls -t /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-* | tail -n +10 | xargs rm -rf
|
||||||
|
```
|
||||||
|
|
||||||
|
### L'auto-update échoue
|
||||||
|
```bash
|
||||||
|
# Vérifier les logs
|
||||||
|
tail -50 /var/log/gitea-update.log
|
||||||
|
|
||||||
|
# Tester manuellement
|
||||||
|
bash ~/scripts/gitea-auto-update.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restauration
|
||||||
|
```bash
|
||||||
|
# Lister les backups disponibles
|
||||||
|
bash ~/scripts/restore.sh
|
||||||
|
|
||||||
|
# Restaurer le plus récent
|
||||||
|
bash ~/scripts/restore.sh $(ls -td /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-* | head -1)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Sécurité
|
||||||
|
|
||||||
|
⚠️ Les mots de passe dans `gitea-scripts.conf` et `docker-compose.yml` sont
|
||||||
|
des valeurs par défaut. **Changez-les avant la mise en production :**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dans gitea-scripts.conf
|
||||||
|
DB_ROOT_PASSWORD=votre-mot-de-passe-securise
|
||||||
|
DB_PASSWORD=votre-autre-mot-de-passe
|
||||||
|
|
||||||
|
# Dans docker-compose.yml
|
||||||
|
MYSQL_ROOT_PASSWORD=votre-mot-de-passe-securise
|
||||||
|
MYSQL_PASSWORD=votre-autre-mot-de-passe
|
||||||
|
GITEA__database__PASSWD=votre-autre-mot-de-passe
|
||||||
|
```
|
||||||
|
|||||||
+24
-1
@@ -28,7 +28,14 @@ services:
|
|||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
- "222:22"
|
- "222:22"
|
||||||
depends_on:
|
depends_on:
|
||||||
- gitea_db
|
gitea_db:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
gitea_db:
|
gitea_db:
|
||||||
image: mysql:8
|
image: mysql:8
|
||||||
@@ -43,3 +50,19 @@ services:
|
|||||||
- gitea
|
- gitea
|
||||||
volumes:
|
volumes:
|
||||||
- /DOCKER_CONFIG/gitea/mysql:/var/lib/mysql
|
- /DOCKER_CONFIG/gitea/mysql:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"mysqladmin",
|
||||||
|
"ping",
|
||||||
|
"-h",
|
||||||
|
"localhost",
|
||||||
|
"-u",
|
||||||
|
"root",
|
||||||
|
"-p${MYSQL_ROOT_PASSWORD:-gitea}",
|
||||||
|
]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|||||||
+174
-34
@@ -5,43 +5,127 @@
|
|||||||
# À placer dans /usr/local/bin/ ou ~/scripts/
|
# À placer dans /usr/local/bin/ ou ~/scripts/
|
||||||
# Cron recommandé : 0 4 * * * /usr/local/bin/gitea-auto-update.sh
|
# Cron recommandé : 0 4 * * * /usr/local/bin/gitea-auto-update.sh
|
||||||
#
|
#
|
||||||
# Paramètres configurables (modifier ci-dessous ou passer en variables d'env)
|
# Paramètres configurables : voir gitea-scripts.conf
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ─── Configuration (modifiable) ─────────────────────────────────────────────
|
# ─── Charger la configuration commune ─────────────────────────────────────────
|
||||||
COMPOSE_DIR="${COMPOSE_DIR:-$HOME/dev/git/outils/gitea}"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
CONFIG_FILE="${CONFIG_FILE:-$SCRIPT_DIR/gitea-scripts.conf}"
|
||||||
|
|
||||||
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Configuration locale (après chargement conf, permet surcharge env) ──────
|
||||||
|
# Chemins et conteneurs
|
||||||
|
COMPOSE_DIR="${COMPOSE_DIR:-$HOME/dev/git/outils/docker/gitea}"
|
||||||
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||||
CONTAINER_NAME="${CONTAINER_NAME:-gitea_server}"
|
APP_SERVICE="${APP_SERVICE:-gitea_server}"
|
||||||
|
APP_CONTAINER="${APP_CONTAINER:-gitea_server}"
|
||||||
|
DB_CONTAINER="${DB_CONTAINER:-gitea_db}"
|
||||||
IMAGE_NAME="${IMAGE_NAME:-gitea/gitea}"
|
IMAGE_NAME="${IMAGE_NAME:-gitea/gitea}"
|
||||||
IMAGE_TAG="${IMAGE_TAG:-latest}" # "latest" ou version spécifique
|
IMAGE_TAG="${IMAGE_TAG:-latest}"
|
||||||
|
|
||||||
|
# Base de données
|
||||||
|
DB_NAME="${DB_NAME:-gitea}"
|
||||||
|
DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-gitea}"
|
||||||
|
|
||||||
|
# Logs
|
||||||
LOG_FILE="${LOG_FILE:-/var/log/gitea-update.log}"
|
LOG_FILE="${LOG_FILE:-/var/log/gitea-update.log}"
|
||||||
ENABLE_NOTIFICATIONS="${ENABLE_NOTIFICATIONS:-false}" # true pour envoyer un résumé
|
LOG_DIR="$(dirname "$LOG_FILE")"
|
||||||
NOTIFY_METHOD="${NOTIFY_METHOD:-log}" # log, telegram, ntfy
|
|
||||||
|
# Notifications
|
||||||
|
ENABLE_NOTIFICATIONS="${ENABLE_NOTIFICATIONS:-false}"
|
||||||
|
NOTIFY_METHOD="${NOTIFY_METHOD:-log}"
|
||||||
|
TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}"
|
||||||
TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID:-}"
|
TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID:-}"
|
||||||
NTPY_TOPIC="${NTPY_TOPIC:-}"
|
NTPY_TOPIC="${NTPY_TOPIC:-}"
|
||||||
|
|
||||||
|
# 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}"
|
||||||
|
|
||||||
|
# Lock file
|
||||||
|
LOCK_DIR="${LOCK_DIR:-/tmp}"
|
||||||
|
LOCK_FILE="${LOCK_DIR}/gitea-auto-update.lock"
|
||||||
|
|
||||||
|
# ─── Prérequis : s'assurer que le dossier de log existe ──────────────────────
|
||||||
|
mkdir -p "$LOG_DIR" 2>/dev/null || true
|
||||||
|
|
||||||
# ─── Logging ─────────────────────────────────────────────────────────────────
|
# ─── Logging ─────────────────────────────────────────────────────────────────
|
||||||
log() {
|
log() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||||
|
echo "$msg"
|
||||||
|
echo "$msg" >> "$LOG_FILE" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── Notification ────────────────────────────────────────────────────────────
|
||||||
notify() {
|
notify() {
|
||||||
local msg="$1"
|
local msg="$1"
|
||||||
case "$NOTIFY_METHOD" in
|
case "$NOTIFY_METHOD" in
|
||||||
telegram)
|
telegram)
|
||||||
[[ -n "$TELEGRAM_CHAT_ID" ]] && curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
if [[ -n "$TELEGRAM_BOT_TOKEN" ]] && [[ -n "$TELEGRAM_CHAT_ID" ]]; then
|
||||||
-d "chat_id=$TELEGRAM_CHAT_ID" -d "text=$msg" > /dev/null 2>&1 || true
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||||
|
-d "chat_id=$TELEGRAM_CHAT_ID" -d "text=$msg" > /dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
ntfy)
|
ntfy)
|
||||||
[[ -n "$NTPY_TOPIC" ]] && curl -s -H "Title: Gitea Update" -d "$msg" "https://ntfy.sh/$NTPY_TOPIC" > /dev/null 2>&1 || true
|
if [[ -n "$NTPY_TOPIC" ]]; then
|
||||||
|
curl -s -H "Title: Gitea Update" -d "$msg" "https://ntfy.sh/$NTPY_TOPIC" > /dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── Lock file ───────────────────────────────────────────────────────────────
|
||||||
|
acquire_lock() {
|
||||||
|
if [[ -f "$LOCK_FILE" ]]; then
|
||||||
|
local pid
|
||||||
|
pid=$(cat "$LOCK_FILE" 2>/dev/null || echo "")
|
||||||
|
# Vérifier si le processus existe encore
|
||||||
|
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
||||||
|
log "⚠️ Une autre instance (PID $pid) est déjà en cours d'exécution — arrêt"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# Stale lock, on le nettoie
|
||||||
|
log "⚠️ Lock stale détecté (PID $pid n'existe plus), on continue"
|
||||||
|
rm -f "$LOCK_FILE"
|
||||||
|
fi
|
||||||
|
echo $$ > "$LOCK_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
release_lock() {
|
||||||
|
rm -f "$LOCK_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Nettoyage en cas d'erreur ───────────────────────────────────────────────
|
||||||
|
cleanup() {
|
||||||
|
release_lock
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# ─── Récupération du digest (compatible POSIX grep) ──────────────────────────
|
||||||
|
get_image_digest() {
|
||||||
|
local image="$1"
|
||||||
|
local digest
|
||||||
|
# Essayer d'abord avec -E (ERE, disponible presque partout)
|
||||||
|
digest=$(docker image inspect "$image" --format '{{.RepoDigests}}' 2>/dev/null | grep -oE 'sha256:[a-f0-9]{64}' | head -1 || echo "")
|
||||||
|
if [[ -z "$digest" ]]; then
|
||||||
|
# Fallback : utiliser awk
|
||||||
|
digest=$(docker image inspect "$image" --format '{{.RepoDigests}}' 2>/dev/null | grep -o 'sha256:[a-f0-9]\{64\}' | head -1 || echo "")
|
||||||
|
fi
|
||||||
|
echo "$digest"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# MAIN
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
acquire_lock
|
||||||
|
|
||||||
log "════════════════════════════════════════════════════════"
|
log "════════════════════════════════════════════════════════"
|
||||||
log "🔄 Début vérification mise à jour Gitea"
|
log "🔄 Début vérification mise à jour Gitea"
|
||||||
|
|
||||||
@@ -53,23 +137,32 @@ if ! command -v docker &>/dev/null; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ─── Récupérer l'image digest actuelle ──────────────────────────────────────
|
# Vérifier si docker compose (plugin) ou docker-compose (standalone) est dispo
|
||||||
OLD_DIGEST=$(docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" --format '{{.RepoDigests}}' 2>/dev/null | grep -oP 'sha256:[a-f0-9]+' || echo "")
|
if docker compose version &>/dev/null; then
|
||||||
|
DOCKER_COMPOSE="docker compose"
|
||||||
|
elif command -v docker-compose &>/dev/null; then
|
||||||
|
DOCKER_COMPOSE="docker-compose"
|
||||||
|
else
|
||||||
|
log "❌ Ni 'docker compose' ni 'docker-compose' n'est disponible"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
log "📦 Image actuelle : ${IMAGE_NAME}:${IMAGE_TAG}"
|
# ─── Récupérer l'image digest actuelle ──────────────────────────────────────
|
||||||
|
FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
|
||||||
|
OLD_DIGEST=$(get_image_digest "$FULL_IMAGE")
|
||||||
|
|
||||||
|
log "📦 Image actuelle : $FULL_IMAGE"
|
||||||
log "🔍 Digest actuel : ${OLD_DIGEST:-non disponible}"
|
log "🔍 Digest actuel : ${OLD_DIGEST:-non disponible}"
|
||||||
|
|
||||||
# ─── Pull de la nouvelle image ──────────────────────────────────────────────
|
# ─── Pull de la nouvelle image ──────────────────────────────────────────────
|
||||||
log "📥 Pull de la nouvelle image..."
|
log "📥 Pull de la nouvelle image..."
|
||||||
PULL_OUTPUT=$(docker compose -f "$COMPOSE_FILE" pull "$CONTAINER_NAME" 2>&1)
|
PULL_OUTPUT=$($DOCKER_COMPOSE -f "$COMPOSE_FILE" pull "$APP_SERVICE" 2>&1) || {
|
||||||
PULL_EXIT=$?
|
PULL_EXIT=$?
|
||||||
|
log "❌ Échec du pull (code $PULL_EXIT) : $PULL_OUTPUT"
|
||||||
if [[ $PULL_EXIT -ne 0 ]]; then
|
|
||||||
log "❌ Échec du pull : $PULL_OUTPUT"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
|
|
||||||
NEW_DIGEST=$(docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" --format '{{.RepoDigests}}' 2>/dev/null | grep -oP 'sha256:[a-f0-9]+' || echo "")
|
NEW_DIGEST=$(get_image_digest "$FULL_IMAGE")
|
||||||
|
|
||||||
log "🔍 Nouveau digest : ${NEW_DIGEST:-non disponible}"
|
log "🔍 Nouveau digest : ${NEW_DIGEST:-non disponible}"
|
||||||
|
|
||||||
@@ -83,34 +176,68 @@ fi
|
|||||||
# ─── MISE À JOUR NÉCESSAIRE ─────────────────────────────────────────────────
|
# ─── MISE À JOUR NÉCESSAIRE ─────────────────────────────────────────────────
|
||||||
log "🔔 Nouvelle version détectée ! Mise à jour en cours..."
|
log "🔔 Nouvelle version détectée ! Mise à jour en cours..."
|
||||||
|
|
||||||
# Backup rapide avant mise à jour (optionnel, le script de backup complet gère le quotidien)
|
# Sauvegarder l'image actuelle pour rollback éventuel
|
||||||
|
log "💾 Sauvegarde de l'image actuelle pour rollback..."
|
||||||
|
OLD_IMAGE_ID=$(docker image inspect "$FULL_IMAGE" --format '{{.ID}}' 2>/dev/null || echo "")
|
||||||
|
ROLLBACK_TAG="${IMAGE_NAME}:rollback-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
if [[ -n "$OLD_IMAGE_ID" ]]; then
|
||||||
|
docker tag "$OLD_IMAGE_ID" "$ROLLBACK_TAG" 2>/dev/null || {
|
||||||
|
log "⚠️ Impossible de tagger l'image pour rollback (on continue)"
|
||||||
|
ROLLBACK_TAG=""
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ROLLBACK_TAG=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup rapide DB avant mise à jour
|
||||||
BACKUP_PRE_DIR="${BACKUP_PRE_DIR:-/tmp/gitea-pre-update-$(date +%Y%m%d-%H%M%S)}"
|
BACKUP_PRE_DIR="${BACKUP_PRE_DIR:-/tmp/gitea-pre-update-$(date +%Y%m%d-%H%M%S)}"
|
||||||
mkdir -p "$BACKUP_PRE_DIR"
|
mkdir -p "$BACKUP_PRE_DIR"
|
||||||
|
|
||||||
log "💾 Backup pré-update vers $BACKUP_PRE_DIR"
|
log "💾 Backup DB pré-update vers $BACKUP_PRE_DIR"
|
||||||
docker exec gitea_db mysqldump -u root -pgitea gitea > "$BACKUP_PRE_DIR/gitea_db.sql" 2>/dev/null || {
|
if docker ps --format '{{.Names}}' | grep -q "^${DB_CONTAINER}$"; then
|
||||||
log "⚠️ Backup pré-update DB échoué, on continue quand même"
|
docker exec "$DB_CONTAINER" mysqldump \
|
||||||
}
|
-u root -p"$DB_ROOT_PASSWORD" \
|
||||||
|
--single-transaction \
|
||||||
|
"$DB_NAME" > "$BACKUP_PRE_DIR/gitea_db.sql" 2>/dev/null || {
|
||||||
|
log "⚠️ Backup pré-update DB échoué, on continue quand même"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log "⚠️ Conteneur $DB_CONTAINER non trouvé — pas de backup pré-update"
|
||||||
|
fi
|
||||||
|
|
||||||
# Recréer le conteneur avec la nouvelle image
|
# Recréer le conteneur avec la nouvelle image
|
||||||
log "🚀 Redémarrage du conteneur avec la nouvelle image..."
|
log "🚀 Redémarrage du conteneur avec la nouvelle image..."
|
||||||
UP_OUTPUT=$(docker compose -f "$COMPOSE_FILE" up -d "$CONTAINER_NAME" 2>&1)
|
UP_OUTPUT=$($DOCKER_COMPOSE -f "$COMPOSE_FILE" up -d "$APP_SERVICE" 2>&1) || {
|
||||||
UP_EXIT=$?
|
UP_EXIT=$?
|
||||||
|
log "❌ Échec du redémarrage (code $UP_EXIT) : $UP_OUTPUT"
|
||||||
|
|
||||||
if [[ $UP_EXIT -ne 0 ]]; then
|
# Rollback réel : restaurer l'ancienne image
|
||||||
log "❌ Échec du redémarrage : $UP_OUTPUT"
|
if [[ -n "$ROLLBACK_TAG" ]]; then
|
||||||
log "⚠️ Tentative de rollback..."
|
log "🔄 ROLLBACK : restauration de l'image $ROLLBACK_TAG..."
|
||||||
docker compose -f "$COMPOSE_FILE" up -d "$CONTAINER_NAME" 2>&1 || true
|
docker tag "$ROLLBACK_TAG" "$FULL_IMAGE" 2>/dev/null || true
|
||||||
|
$DOCKER_COMPOSE -f "$COMPOSE_FILE" up -d "$APP_SERVICE" 2>&1 || true
|
||||||
|
docker rmi "$ROLLBACK_TAG" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
log "⚠️ Rollback impossible (pas d'image sauvegardée)"
|
||||||
|
$DOCKER_COMPOSE -f "$COMPOSE_FILE" up -d "$APP_SERVICE" 2>&1 || true
|
||||||
|
fi
|
||||||
notify "❌ GITEA UPDATE FAILED sur $(hostname) — rollback effectué"
|
notify "❌ GITEA UPDATE FAILED sur $(hostname) — rollback effectué"
|
||||||
exit 1
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nettoyer le tag de rollback si tout va bien
|
||||||
|
if [[ -n "$ROLLBACK_TAG" ]]; then
|
||||||
|
docker rmi "$ROLLBACK_TAG" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ─── Vérification santé ─────────────────────────────────────────────────────
|
# ─── Vérification santé ─────────────────────────────────────────────────────
|
||||||
log "🏥 Vérification santé (timeout=${HEALTH_CHECK_TIMEOUT}s)..."
|
log "🏥 Vérification santé (timeout=${HEALTH_CHECK_TIMEOUT}s)..."
|
||||||
|
HEALTH_OK=false
|
||||||
for i in $(seq 1 "$HEALTH_CHECK_TIMEOUT"); do
|
for i in $(seq 1 "$HEALTH_CHECK_TIMEOUT"); do
|
||||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL" 2>/dev/null || echo "000")
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL" 2>/dev/null || echo "000")
|
||||||
if [[ "$STATUS" == "200" || "$STATUS" == "302" || "$STATUS" == "303" ]]; then
|
if [[ "$STATUS" == "200" || "$STATUS" == "302" || "$STATUS" == "303" ]]; then
|
||||||
log "✅ Gitea répond (HTTP $STATUS) après ${i}s"
|
log "✅ Gitea répond (HTTP $STATUS) après ${i}s"
|
||||||
|
HEALTH_OK=true
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
if [[ $i -eq "$HEALTH_CHECK_TIMEOUT" ]]; then
|
if [[ $i -eq "$HEALTH_CHECK_TIMEOUT" ]]; then
|
||||||
@@ -122,7 +249,7 @@ for i in $(seq 1 "$HEALTH_CHECK_TIMEOUT"); do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# ─── Rapport final ──────────────────────────────────────────────────────────
|
# ─── Rapport final ──────────────────────────────────────────────────────────
|
||||||
GITEA_VERSION=$(curl -s "$HEALTH_CHECK_URL/api/v1/version" 2>/dev/null | grep -oP '"version":"\K[^"]+' || echo "inconnue")
|
GITEA_VERSION=$(curl -s "$HEALTH_CHECK_URL/api/v1/version" 2>/dev/null | grep -oE '"version":"[^"]+"' | cut -d'"' -f4 || echo "inconnue")
|
||||||
|
|
||||||
log "✅ Mise à jour réussie !"
|
log "✅ Mise à jour réussie !"
|
||||||
log "📋 Nouvelle version : $GITEA_VERSION"
|
log "📋 Nouvelle version : $GITEA_VERSION"
|
||||||
@@ -132,7 +259,20 @@ log "═══ Fin ────────────────────
|
|||||||
|
|
||||||
notify "✅ Gitea mis à jour vers $GITEA_VERSION sur $(hostname)"
|
notify "✅ Gitea mis à jour vers $GITEA_VERSION sur $(hostname)"
|
||||||
|
|
||||||
# Nettoyage vieilles images
|
# ─── Nettoyage ciblé : seulement les vieilles images Gitea ──────────────────
|
||||||
|
log "🧹 Nettoyage des anciennes images Gitea..."
|
||||||
|
# Trouver toutes les images gitea/gitea sauf la dernière utilisée
|
||||||
|
CURRENT_IMAGE_ID=$(docker image inspect "$FULL_IMAGE" --format '{{.ID}}' 2>/dev/null || echo "")
|
||||||
|
if [[ -n "$CURRENT_IMAGE_ID" ]]; then
|
||||||
|
# Lister les images Gitea non taggées ou anciennes
|
||||||
|
docker images "$IMAGE_NAME" --format '{{.ID}} {{.Tag}}' 2>/dev/null | while read -r img_id img_tag; do
|
||||||
|
if [[ "$img_id" != "$CURRENT_IMAGE_ID" ]] && [[ "$img_tag" != "$IMAGE_TAG" ]]; then
|
||||||
|
log " 🗑️ Suppression ancienne image : $img_id ($img_tag)"
|
||||||
|
docker rmi "$img_id" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
# Nettoyer aussi les images dangling (sans tag)
|
||||||
docker image prune -f 2>/dev/null || true
|
docker image prune -f 2>/dev/null || true
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
+356
-107
@@ -9,16 +9,23 @@
|
|||||||
# 1. La base de données MySQL (dump SQL complet)
|
# 1. La base de données MySQL (dump SQL complet)
|
||||||
# 2. Les volumes Docker (data, mysql)
|
# 2. Les volumes Docker (data, mysql)
|
||||||
# 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
|
||||||
#
|
#
|
||||||
# Le tout est horodaté et stocké dans BACKUP_DIR.
|
# Paramètres configurables : voir gitea-scripts.conf
|
||||||
# Un restore peut se faire sur N'IMPORTE QUEL hôte Docker.
|
|
||||||
#
|
|
||||||
# Paramètres configurables (modifier ci-dessous ou passer en variables d'env)
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ─── Configuration (modifiable) ─────────────────────────────────────────────
|
# ─── Charger la configuration commune ─────────────────────────────────────────
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
CONFIG_FILE="${CONFIG_FILE:-$SCRIPT_DIR/gitea-scripts.conf}"
|
||||||
|
|
||||||
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── 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/dev-prod/gitea}"
|
||||||
COMPOSE_DIR="${COMPOSE_DIR:-$HOME/dev/git/outils/docker/gitea}"
|
COMPOSE_DIR="${COMPOSE_DIR:-$HOME/dev/git/outils/docker/gitea}"
|
||||||
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||||
@@ -26,6 +33,8 @@ COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
|||||||
# Conteneurs
|
# Conteneurs
|
||||||
DB_CONTAINER="${DB_CONTAINER:-gitea_db}"
|
DB_CONTAINER="${DB_CONTAINER:-gitea_db}"
|
||||||
APP_CONTAINER="${APP_CONTAINER:-gitea_server}"
|
APP_CONTAINER="${APP_CONTAINER:-gitea_server}"
|
||||||
|
DB_SERVICE="${DB_SERVICE:-gitea_db}"
|
||||||
|
APP_SERVICE="${APP_SERVICE:-gitea_server}"
|
||||||
|
|
||||||
# Base de données
|
# Base de données
|
||||||
DB_NAME="${DB_NAME:-gitea}"
|
DB_NAME="${DB_NAME:-gitea}"
|
||||||
@@ -38,25 +47,176 @@ VOLUME_DATA="${VOLUME_DATA:-/DOCKER_CONFIG/gitea/data}"
|
|||||||
VOLUME_MYSQL="${VOLUME_MYSQL:-/DOCKER_CONFIG/gitea/mysql}"
|
VOLUME_MYSQL="${VOLUME_MYSQL:-/DOCKER_CONFIG/gitea/mysql}"
|
||||||
|
|
||||||
# Rétention
|
# Rétention
|
||||||
RETENTION_DAYS="${RETENTION_DAYS:-30}" # Garder X jours de backups
|
RETENTION_DAYS="${RETENTION_DAYS:-30}"
|
||||||
RETENTION_WEEKLY="${RETENTION_WEEKLY:-4}" # Garder X backups hebdomadaires (dimanche)
|
RETENTION_WEEKLY="${RETENTION_WEEKLY:-4}"
|
||||||
RETENTION_MONTHLY="${RETENTION_MONTHLY:-6}" # Garder X backups mensuels (1er du mois)
|
RETENTION_MONTHLY="${RETENTION_MONTHLY:-6}"
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
COMPRESS="${COMPRESS:-true}" # Compression gzip
|
COMPRESS="${COMPRESS:-true}"
|
||||||
VERIFY_BACKUP="${VERIFY_BACKUP:-true}" # Vérifier l'intégrité après backup
|
VERIFY_BACKUP="${VERIFY_BACKUP:-true}"
|
||||||
STOP_CONTAINERS="${STOP_CONTAINERS:-false}" # Arrêter les conteneurs pendant le backup (plus sûr)
|
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/gitea-backup.log}"
|
||||||
|
LOG_DIR="$(dirname "$LOG_FILE")"
|
||||||
|
|
||||||
|
# Lock file
|
||||||
|
LOCK_DIR="${LOCK_DIR:-/tmp}"
|
||||||
|
LOCK_FILE="${LOCK_DIR}/gitea-backup.lock"
|
||||||
|
|
||||||
|
# Backup distant
|
||||||
|
REMOTE_BACKUP_ENABLED="${REMOTE_BACKUP_ENABLED:-false}"
|
||||||
|
REMOTE_BACKUP_METHOD="${REMOTE_BACKUP_METHOD:-rsync}"
|
||||||
|
REMOTE_BACKUP_HOST="${REMOTE_BACKUP_HOST:-}"
|
||||||
|
REMOTE_BACKUP_USER="${REMOTE_BACKUP_USER:-}"
|
||||||
|
REMOTE_BACKUP_PATH="${REMOTE_BACKUP_PATH:-}"
|
||||||
|
REMOTE_BACKUP_SSH_KEY="${REMOTE_BACKUP_SSH_KEY:-$HOME/.ssh/id_rsa}"
|
||||||
|
|
||||||
|
# Notifications
|
||||||
|
NOTIFY_METHOD="${NOTIFY_METHOD:-log}"
|
||||||
|
TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}"
|
||||||
|
TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID:-}"
|
||||||
|
NTPY_TOPIC="${NTPY_TOPIC:-}"
|
||||||
|
|
||||||
|
# ─── Prérequis : s'assurer que le dossier de log existe ──────────────────────
|
||||||
|
mkdir -p "$LOG_DIR" 2>/dev/null || true
|
||||||
|
|
||||||
# ─── Logging ─────────────────────────────────────────────────────────────────
|
# ─── Logging ─────────────────────────────────────────────────────────────────
|
||||||
log() {
|
log() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||||
|
echo "$msg"
|
||||||
|
echo "$msg" >> "$LOG_FILE" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
# ─── Initialisation ─────────────────────────────────────────────────────────
|
# ─── Notification ────────────────────────────────────────────────────────────
|
||||||
|
notify() {
|
||||||
|
local msg="$1"
|
||||||
|
case "$NOTIFY_METHOD" in
|
||||||
|
telegram)
|
||||||
|
if [[ -n "$TELEGRAM_BOT_TOKEN" ]] && [[ -n "$TELEGRAM_CHAT_ID" ]]; then
|
||||||
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||||
|
-d "chat_id=$TELEGRAM_CHAT_ID" -d "text=$msg" > /dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
ntfy)
|
||||||
|
if [[ -n "$NTPY_TOPIC" ]]; then
|
||||||
|
curl -s -H "Title: Gitea Backup" -d "$msg" "https://ntfy.sh/$NTPY_TOPIC" > /dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Lock file ───────────────────────────────────────────────────────────────
|
||||||
|
acquire_lock() {
|
||||||
|
if [[ -f "$LOCK_FILE" ]]; then
|
||||||
|
local pid
|
||||||
|
pid=$(cat "$LOCK_FILE" 2>/dev/null || echo "")
|
||||||
|
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
||||||
|
log "⚠️ Une autre instance (PID $pid) est déjà en cours d'exécution — arrêt"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
log "⚠️ Lock stale détecté (PID $pid n'existe plus), on continue"
|
||||||
|
rm -f "$LOCK_FILE"
|
||||||
|
fi
|
||||||
|
echo $$ > "$LOCK_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
release_lock() {
|
||||||
|
rm -f "$LOCK_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
release_lock
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# ─── Détection docker compose ────────────────────────────────────────────────
|
||||||
|
detect_compose() {
|
||||||
|
if docker compose version &>/dev/null; then
|
||||||
|
echo "docker compose"
|
||||||
|
elif command -v docker-compose &>/dev/null; then
|
||||||
|
echo "docker-compose"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Vérification espace disque (robuste) ────────────────────────────────────
|
||||||
|
check_disk_space() {
|
||||||
|
local path="$1"
|
||||||
|
local min_kb="${2:-2097152}" # 2 GB par défaut
|
||||||
|
local available_kb
|
||||||
|
|
||||||
|
# Méthode robuste : utiliser df -P (format POSIX, pas de wrapping)
|
||||||
|
available_kb=$(df -P "$path" 2>/dev/null | tail -1 | awk '{print $4}')
|
||||||
|
if [[ -z "$available_kb" || "$available_kb" -le 0 ]]; then
|
||||||
|
# Fallback avec --output (GNU df)
|
||||||
|
available_kb=$(df --output=avail "$path" 2>/dev/null | tail -1 | tr -d ' ' || echo "0")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$available_kb" -lt "$min_kb" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "$available_kb"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Backup distant ──────────────────────────────────────────────────────────
|
||||||
|
sync_remote() {
|
||||||
|
local backup_path="$1"
|
||||||
|
if [[ "$REMOTE_BACKUP_ENABLED" != "true" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -z "$REMOTE_BACKUP_HOST" ]] || [[ -z "$REMOTE_BACKUP_USER" ]] || [[ -z "$REMOTE_BACKUP_PATH" ]]; then
|
||||||
|
log "⚠️ Backup distant : configuration incomplète, ignoré"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "☁️ Synchronisation distante ($REMOTE_BACKUP_METHOD) vers ${REMOTE_BACKUP_USER}@${REMOTE_BACKUP_HOST}:${REMOTE_BACKUP_PATH}..."
|
||||||
|
|
||||||
|
local ssh_opts="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
|
||||||
|
if [[ -f "$REMOTE_BACKUP_SSH_KEY" ]]; then
|
||||||
|
ssh_opts="$ssh_opts -i $REMOTE_BACKUP_SSH_KEY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$REMOTE_BACKUP_METHOD" in
|
||||||
|
rsync)
|
||||||
|
if command -v rsync &>/dev/null; then
|
||||||
|
rsync -avz -e "ssh $ssh_opts" \
|
||||||
|
"$backup_path" \
|
||||||
|
"${REMOTE_BACKUP_USER}@${REMOTE_BACKUP_HOST}:${REMOTE_BACKUP_PATH}/" 2>&1 || {
|
||||||
|
log "⚠️ Échec rsync distant"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
log " ✅ Synchronisation rsync réussie"
|
||||||
|
else
|
||||||
|
log "⚠️ rsync non installé, backup distant ignoré"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
scp)
|
||||||
|
scp $ssh_opts -r "$backup_path" \
|
||||||
|
"${REMOTE_BACKUP_USER}@${REMOTE_BACKUP_HOST}:${REMOTE_BACKUP_PATH}/" 2>&1 || {
|
||||||
|
log "⚠️ Échec SCP distant"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
log " ✅ Synchronisation SCP réussie"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log "⚠️ Méthode de backup distant inconnue : $REMOTE_BACKUP_METHOD"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# MAIN
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
acquire_lock
|
||||||
|
|
||||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||||
DATE_LABEL=$(date +%Y-%m-%d)
|
DATE_LABEL=$(date +%Y-%m-%d)
|
||||||
DAY_OF_WEEK=$(date +%u) # 1=lundi, 7=dimanche
|
DAY_OF_WEEK=$(date +%u) # 1=lundi, 7=dimanche
|
||||||
@@ -66,6 +226,8 @@ BACKUP_NAME="gitea-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)
|
||||||
|
|
||||||
log "════════════════════════════════════════════════════════"
|
log "════════════════════════════════════════════════════════"
|
||||||
log "📦 Début backup Gitea → $BACKUP_PATH"
|
log "📦 Début backup Gitea → $BACKUP_PATH"
|
||||||
|
|
||||||
@@ -73,30 +235,31 @@ log "📦 Début backup Gitea → $BACKUP_PATH"
|
|||||||
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; }
|
||||||
cd "$COMPOSE_DIR" || { log "❌ Répertoire $COMPOSE_DIR introuvable"; exit 1; }
|
cd "$COMPOSE_DIR" || { log "❌ Répertoire $COMPOSE_DIR introuvable"; exit 1; }
|
||||||
|
|
||||||
mkdir -p "$BACKUP_PATH"
|
if [[ -z "$DOCKER_COMPOSE" ]]; then
|
||||||
|
log "❌ Ni 'docker compose' ni 'docker-compose' n'est disponible"
|
||||||
# Vérifier espace disque (minimum 2 GB)
|
|
||||||
AVAILABLE_KB=$(df "$BACKUP_DIR" | tail -1 | awk '{print $4}')
|
|
||||||
if [[ $AVAILABLE_KB -lt 2097152 ]]; then
|
|
||||||
log "❌ Espace disque insuffisant : $(($AVAILABLE_KB / 1024)) MB disponibles (min 2 GB requis)"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$BACKUP_PATH"
|
||||||
|
|
||||||
|
# Vérifier espace disque (minimum 2 GB)
|
||||||
|
AVAILABLE_KB=$(check_disk_space "$BACKUP_DIR" 2097152) || {
|
||||||
|
log "❌ Espace disque insuffisant (moins de 2 GB disponibles sur $BACKUP_DIR)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
log "💾 Espace disque : $((AVAILABLE_KB / 1024)) MB disponibles"
|
log "💾 Espace disque : $((AVAILABLE_KB / 1024)) MB disponibles"
|
||||||
|
|
||||||
# ─── Optionnel : arrêter les conteneurs pour un backup consistant ───────────
|
# ─── Optionnel : arrêter les conteneurs pour un backup consistant ───────────
|
||||||
|
CONTAINERS_WERE_STOPPED=false
|
||||||
if [[ "$STOP_CONTAINERS" == "true" ]]; then
|
if [[ "$STOP_CONTAINERS" == "true" ]]; then
|
||||||
log "⏸️ Arrêt temporaire des conteneurs..."
|
log "⏸️ Arrêt temporaire des conteneurs..."
|
||||||
docker compose -f "$COMPOSE_FILE" stop 2>&1 | tee -a "$LOG_FILE"
|
$DOCKER_COMPOSE -f "$COMPOSE_FILE" stop 2>&1 | tee -a "$LOG_FILE" || true
|
||||||
CONTAINERS_WERE_STOPPED=true
|
CONTAINERS_WERE_STOPPED=true
|
||||||
else
|
|
||||||
CONTAINERS_WERE_STOPPED=false
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ─── 1. Backup base de données MySQL ────────────────────────────────────────
|
# ─── 1. Backup base de données MySQL ────────────────────────────────────────
|
||||||
log "🛢️ Dump base de données MySQL ($DB_NAME)..."
|
log "🛢️ Dump base de données MySQL ($DB_NAME)..."
|
||||||
|
|
||||||
# Vérifier que le conteneur DB tourne
|
|
||||||
if docker ps --format '{{.Names}}' | grep -q "^${DB_CONTAINER}$"; then
|
if docker ps --format '{{.Names}}' | grep -q "^${DB_CONTAINER}$"; then
|
||||||
docker exec "$DB_CONTAINER" mysqldump \
|
docker exec "$DB_CONTAINER" mysqldump \
|
||||||
-u root -p"$DB_ROOT_PASSWORD" \
|
-u root -p"$DB_ROOT_PASSWORD" \
|
||||||
@@ -106,30 +269,30 @@ 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>&1 || {
|
"$DB_NAME" > "${BACKUP_PATH}/gitea-db.sql" 2>"${BACKUP_PATH}/mysqldump.err" || {
|
||||||
log "❌ Échec du dump MySQL"
|
log "❌ Échec du dump MySQL"
|
||||||
[[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && docker compose -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE"
|
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
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
DB_SIZE=$(wc -c < "${BACKUP_PATH}/gitea-db.sql")
|
rm -f "${BACKUP_PATH}/mysqldump.err"
|
||||||
|
DB_SIZE=$(wc -c < "${BACKUP_PATH}/gitea-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)"
|
||||||
[[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && docker compose -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE"
|
[[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && $DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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}/gitea-db.sql"
|
||||||
log " 📦 Compressé : $(ls -lh "${BACKUP_PATH}/gitea-db.sql.gz" | awk '{print $5}')"
|
log " 📦 Compressé : $(ls -lh "${BACKUP_PATH}/gitea-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)..."
|
||||||
|
|
||||||
# On utilise tar depuis l'hôte directement (plus fiable que docker cp pour gros volumes)
|
|
||||||
# Exclusion des fichiers temporaires et logs inutiles
|
|
||||||
tar -czf "${BACKUP_PATH}/gitea-data.tar.gz" \
|
tar -czf "${BACKUP_PATH}/gitea-data.tar.gz" \
|
||||||
-C "$(dirname "$VOLUME_DATA")" \
|
-C "$(dirname "$VOLUME_DATA")" \
|
||||||
--exclude='*.log' \
|
--exclude='*.log' \
|
||||||
@@ -138,10 +301,10 @@ tar -czf "${BACKUP_PATH}/gitea-data.tar.gz" \
|
|||||||
--exclude='cache/*' \
|
--exclude='cache/*' \
|
||||||
"$(basename "$VOLUME_DATA")" 2>&1 || {
|
"$(basename "$VOLUME_DATA")" 2>&1 || {
|
||||||
log "❌ Échec tar du volume data"
|
log "❌ Échec tar du volume data"
|
||||||
[[ "$CONTAINERS_WERE_STOPPED" == "true" ]] && docker compose -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE"
|
[[ "$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" | awk '{print $5}')
|
DATA_SIZE=$(ls -lh "${BACKUP_PATH}/gitea-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)..."
|
||||||
@@ -149,10 +312,10 @@ tar -czf "${BACKUP_PATH}/gitea-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"
|
[[ "$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" | awk '{print $5}')
|
MYSQL_SIZE=$(ls -lh "${BACKUP_PATH}/gitea-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 ──────────────────────────────────
|
||||||
@@ -163,13 +326,15 @@ cp "$COMPOSE_DIR/$COMPOSE_FILE" "${BACKUP_PATH}/docker-compose.yml"
|
|||||||
if [[ -f "${VOLUME_DATA}/gitea/conf/app.ini" ]]; then
|
if [[ -f "${VOLUME_DATA}/gitea/conf/app.ini" ]]; then
|
||||||
cp "${VOLUME_DATA}/gitea/conf/app.ini" "${BACKUP_PATH}/app.ini"
|
cp "${VOLUME_DATA}/gitea/conf/app.ini" "${BACKUP_PATH}/app.ini"
|
||||||
log " ✅ app.ini sauvegardé"
|
log " ✅ app.ini sauvegardé"
|
||||||
|
else
|
||||||
|
log " ⚠️ app.ini non trouvé dans ${VOLUME_DATA}/gitea/conf/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ─── Redémarrer les conteneurs si on les avait arrêtés ──────────────────────
|
# ─── Redémarrer les conteneurs si on les avait arrêtés ──────────────────────
|
||||||
if [[ "$CONTAINERS_WERE_STOPPED" == "true" ]]; then
|
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"
|
$DOCKER_COMPOSE -f "$COMPOSE_FILE" start 2>&1 | tee -a "$LOG_FILE" || true
|
||||||
|
|
||||||
# Attendre que Gitea réponde
|
# Attendre que Gitea 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}" "http://localhost:3000" 2>/dev/null | grep -qE "200|302|303"; then
|
||||||
@@ -183,6 +348,9 @@ 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"
|
||||||
|
[[ "$COMPRESS" == "true" ]] && DB_DUMP_FILE="gitea-db.sql.gz"
|
||||||
|
|
||||||
cat > "$RESTORE_SCRIPT" << RESTORE_EOF
|
cat > "$RESTORE_SCRIPT" << RESTORE_EOF
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -191,12 +359,6 @@ cat > "$RESTORE_SCRIPT" << RESTORE_EOF
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Usage : bash restore.sh [OPTIONS]
|
# Usage : bash restore.sh [OPTIONS]
|
||||||
#
|
#
|
||||||
# Ce script restaure COMPLÈTEMENT Gitea sur n'importe quel hôte Docker :
|
|
||||||
# 1. Crée les volumes Docker
|
|
||||||
# 2. Restaure la base de données MySQL
|
|
||||||
# 3. Restaure les données Gitea
|
|
||||||
# 4. Lance les conteneurs
|
|
||||||
#
|
|
||||||
# Options :
|
# Options :
|
||||||
# --dry-run Affiche ce qui serait fait sans exécuter
|
# --dry-run Affiche ce qui serait fait sans exécuter
|
||||||
# --force Ne pas demander confirmation
|
# --force Ne pas demander confirmation
|
||||||
@@ -221,7 +383,7 @@ 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) head -30 "\$0"; exit 0 ;;
|
--help|-h) head -50 "\$0"; exit 0 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -246,23 +408,19 @@ if [[ "\$DRY_RUN" == "true" ]]; then
|
|||||||
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"
|
||||||
RESTORE_EOF
|
echo " 7. docker exec gitea_db mysql ... < $DB_DUMP_FILE"
|
||||||
|
|
||||||
# Continuer le script restore (le heredoc était trop long pour un seul bloc)
|
|
||||||
cat >> "$RESTORE_SCRIPT" << 'RESTORE_EOF'
|
|
||||||
echo " 7. docker exec gitea_db mysql ... < gitea-db.sql"
|
|
||||||
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 Gitea 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
|
||||||
@@ -270,58 +428,77 @@ 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/gitea-mysql.tar.gz" -C "\$(dirname "\$TARGET_VOLUME_MYSQL")"
|
||||||
|
|
||||||
echo "📦 Étape 3/6 : Restauration volume Gitea..."
|
echo "📦 Étape 3/6 : Restauration volume Gitea..."
|
||||||
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/gitea-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}/gitea/conf"
|
||||||
cp "$BACKUP_DIR/app.ini" "${TARGET_VOLUME_DATA}/gitea/conf/app.ini"
|
cp "\$BACKUP_DIR/app.ini" "\${TARGET_VOLUME_DATA}/gitea/conf/app.ini"
|
||||||
echo " ✅ app.ini restauré"
|
echo " ✅ app.ini restauré"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Détecter docker compose ou docker-compose
|
||||||
|
if docker compose version &>/dev/null; then
|
||||||
|
DC="docker compose"
|
||||||
|
elif command -v docker-compose &>/dev/null; then
|
||||||
|
DC="docker-compose"
|
||||||
|
else
|
||||||
|
echo "❌ Ni 'docker compose' ni 'docker-compose' n'est disponible"
|
||||||
|
exit 1
|
||||||
|
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"
|
||||||
docker compose up -d
|
\$DC up -d
|
||||||
|
|
||||||
echo " ⏳ Attente démarrage MySQL (30s)..."
|
echo " ⏳ Attente démarrage MySQL (30s)..."
|
||||||
sleep 10
|
sleep 10
|
||||||
for i in $(seq 1 20); do
|
MYSQL_READY=false
|
||||||
if docker exec gitea_db mysqladmin ping -u root -p"$DB_ROOT_PASSWORD" --silent 2>/dev/null; then
|
for i in \$(seq 1 20); do
|
||||||
echo " ✅ MySQL prêt après $((10 + i))s"
|
if docker exec gitea_db mysqladmin ping -u root -p"\$DB_ROOT_PASSWORD" --silent 2>/dev/null; then
|
||||||
|
echo " ✅ MySQL prêt après \$((10 + i))s"
|
||||||
|
MYSQL_READY=true
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ "\$MYSQL_READY" != "true" ]]; then
|
||||||
|
echo "⚠️ MySQL n'a pas répondu dans le temps imparti, tentative de restore DB quand même..."
|
||||||
|
fi
|
||||||
|
|
||||||
echo "📦 Étape 6/6 : Restauration base de données..."
|
echo "📦 Étape 6/6 : Restauration base de données..."
|
||||||
DB_DUMP="$BACKUP_DIR/gitea-db.sql"
|
DB_DUMP="\$BACKUP_DIR/$DB_DUMP_FILE"
|
||||||
if [[ -f "${DB_DUMP}.gz" ]]; then
|
if [[ -f "\${DB_DUMP}.gz" ]]; then
|
||||||
gunzip -c "${DB_DUMP}.gz" | docker exec -i gitea_db mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME"
|
gunzip -c "\${DB_DUMP}.gz" | docker exec -i gitea_db mysql -u root -p"\$DB_ROOT_PASSWORD" "\$DB_NAME"
|
||||||
|
elif [[ -f "\$DB_DUMP" ]]; then
|
||||||
|
docker exec -i gitea_db mysql -u root -p"\$DB_ROOT_PASSWORD" "\$DB_NAME" < "\$DB_DUMP"
|
||||||
else
|
else
|
||||||
docker exec -i gitea_db mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" < "$DB_DUMP"
|
echo "❌ Fichier dump introuvable : \$DB_DUMP"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🔄 Redémarrage final des conteneurs..."
|
echo "🔄 Redémarrage final des conteneurs..."
|
||||||
docker compose restart
|
\$DC restart
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════"
|
||||||
echo "✅ RESTAURATION TERMINÉE !"
|
echo "✅ RESTAURATION TERMINÉE !"
|
||||||
echo ""
|
echo ""
|
||||||
echo " Gitea : http://localhost:3000"
|
echo " Gitea : http://localhost:3000"
|
||||||
echo " Vérifiez les logs : docker compose logs -f"
|
echo " Vérifiez les logs : \$DC logs -f"
|
||||||
echo "════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════"
|
||||||
RESTORE_EOF
|
RESTORE_EOF
|
||||||
|
|
||||||
@@ -332,21 +509,24 @@ log " ✅ Script restore créé : restore.sh"
|
|||||||
if [[ "$VERIFY_BACKUP" == "true" ]]; then
|
if [[ "$VERIFY_BACKUP" == "true" ]]; then
|
||||||
log "🔍 Vérification intégrité des archives..."
|
log "🔍 Vérification intégrité des archives..."
|
||||||
ERRORS=0
|
ERRORS=0
|
||||||
|
|
||||||
|
# Vérifier chaque archive .tar.gz (avec gestion du glob vide)
|
||||||
|
shopt -s nullglob
|
||||||
for ARCHIVE in "$BACKUP_PATH"/*.tar.gz; do
|
for ARCHIVE in "$BACKUP_PATH"/*.tar.gz; do
|
||||||
if ! tar -tzf "$ARCHIVE" > /dev/null 2>&1; then
|
if ! tar -tzf "$ARCHIVE" > /dev/null 2>&1; then
|
||||||
log " ❌ Archive corrompue : $ARCHIVE"
|
log " ❌ Archive corrompue : $(basename "$ARCHIVE")"
|
||||||
ERRORS=$((ERRORS + 1))
|
ERRORS=$((ERRORS + 1))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
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}/gitea-db.sql.gz" 2>/dev/null; then
|
||||||
log " ❌ Dump SQL corrompu"
|
log " ❌ Dump SQL corrompu"
|
||||||
ERRORS=$((ERRORS + 1))
|
ERRORS=$((ERRORS + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $ERRORS -gt 0 ]]; then
|
if [[ $ERRORS -gt 0 ]]; then
|
||||||
log "❌ $ERRORS erreur(s) d'intégrité détectée(s)"
|
log "❌ $ERRORS erreur(s) d'intégrité détectée(s)"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -355,55 +535,121 @@ if [[ "$VERIFY_BACKUP" == "true" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ─── 6. Métadonnées du backup ───────────────────────────────────────────────
|
# ─── 6. Métadonnées du backup ───────────────────────────────────────────────
|
||||||
|
GITEA_VERSION="inconnue"
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q "^${APP_CONTAINER}$"; then
|
||||||
|
GITEA_VERSION=$(docker exec "$APP_CONTAINER" gitea --version 2>/dev/null || echo "inconnue")
|
||||||
|
fi
|
||||||
|
|
||||||
|
DB_DISPLAY_SIZE=$(ls -lh "${BACKUP_PATH}"/gitea-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 : $(docker exec "$APP_CONTAINER" gitea --version 2>/dev/null || echo "inconnue")
|
Gitea version : $GITEA_VERSION
|
||||||
DB type : MySQL 8
|
DB type : MySQL 8
|
||||||
DB name : $DB_NAME
|
DB name : $DB_NAME
|
||||||
Compression : $COMPRESS
|
Compression : $COMPRESS
|
||||||
Containers stopped : $STOP_CONTAINERS
|
Containers stopped : $CONTAINERS_WERE_STOPPED
|
||||||
Taille DB : $(ls -lh "${BACKUP_PATH}"/gitea-db.sql* | awk '{print $5}')
|
Taille DB : ${DB_DISPLAY_SIZE:-N/A}
|
||||||
Taille data : $DATA_SIZE
|
Taille data : ${DATA_SIZE:-N/A}
|
||||||
Taille mysql : $MYSQL_SIZE
|
Taille mysql : ${MYSQL_SIZE:-N/A}
|
||||||
INFO
|
INFO
|
||||||
|
|
||||||
# ─── 7. Nettoyage des vieux backups ─────────────────────────────────────────
|
# ─── 7. Nettoyage des vieux backups ─────────────────────────────────────────
|
||||||
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)..."
|
||||||
|
|
||||||
# Supprimer les backups plus vieux que RETENTION_DAYS (sauf dimanche et 1er du mois)
|
# Pour chaque backup, déterminer s'il doit être gardé
|
||||||
find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -mtime +"$RETENTION_DAYS" | while read -r backup; do
|
find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" -printf '%f\n' 2>/dev/null | sort -r | while read -r backup_name; do
|
||||||
BASENAME=$(basename "$backup")
|
backup_path="$BACKUP_DIR/$backup_name"
|
||||||
# Garder les backups du dimanche et du 1er du mois (gérés séparément)
|
|
||||||
log " 🗑️ Suppression : $BASENAME"
|
# Déterminer le jour du mois et le jour de la semaine à partir du nom
|
||||||
rm -rf "$backup"
|
# Format: gitea-backup-YYYYMMDD-HHMMSS
|
||||||
|
local_date=$(echo "$backup_name" | grep -oE '[0-9]{8}' | head -1)
|
||||||
|
if [[ -z "$local_date" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
local_day=${local_date:6:2} # Jour du mois (DD)
|
||||||
|
local_month=${local_date:4:2}
|
||||||
|
local_year=${local_date:0:4}
|
||||||
|
|
||||||
|
# Déterminer le jour de la semaine (1=lundi, 7=dimanche)
|
||||||
|
local_dow=$(date -d "${local_year}-${local_month}-${local_day}" +%u 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
# Âge en jours
|
||||||
|
backup_ts=$(date -d "${local_year}-${local_month}-${local_day}" +%s 2>/dev/null || echo "0")
|
||||||
|
now_ts=$(date +%s)
|
||||||
|
age_days=$(( (now_ts - backup_ts) / 86400 ))
|
||||||
|
|
||||||
|
# Décisions de rétention
|
||||||
|
KEEP=false
|
||||||
|
REASON=""
|
||||||
|
|
||||||
|
# Toujours garder : backups récents
|
||||||
|
if [[ "$age_days" -le "$RETENTION_DAYS" ]]; then
|
||||||
|
KEEP=true
|
||||||
|
REASON="récent (${age_days}j <= ${RETENTION_DAYS}j)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Toujours garder : backups du dimanche (hebdomadaire) dans la limite RETENTION_WEEKLY
|
||||||
|
if [[ "$local_dow" == "7" ]]; then
|
||||||
|
# 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
|
||||||
|
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")
|
||||||
|
if [[ "$wb_dow" == "7" ]]; then
|
||||||
|
echo "$wb"
|
||||||
|
fi
|
||||||
|
done | sort -r | head -n "$RETENTION_WEEKLY" | wc -l)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
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")
|
||||||
|
if [[ "$wb_dow" == "7" ]]; then
|
||||||
|
echo "$wb"
|
||||||
|
fi
|
||||||
|
done | sort -r | head -n "$RETENTION_WEEKLY")
|
||||||
|
|
||||||
|
if echo "$weekly_list" | grep -qF "$backup_name"; then
|
||||||
|
KEEP=true
|
||||||
|
REASON="hebdomadaire (dimanche, dans les ${RETENTION_WEEKLY} derniers)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Toujours garder : backups du 1er du mois (mensuel) dans la limite RETENTION_MONTHLY
|
||||||
|
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
|
||||||
|
mb_date=$(echo "$mb" | grep -oE '[0-9]{8}')
|
||||||
|
mb_day=${mb_date:6:2}
|
||||||
|
if [[ "$mb_day" == "01" ]]; then
|
||||||
|
echo "$mb"
|
||||||
|
fi
|
||||||
|
done | sort -r | head -n "$RETENTION_MONTHLY")
|
||||||
|
|
||||||
|
if echo "$monthly_list" | grep -qF "$backup_name"; then
|
||||||
|
KEEP=true
|
||||||
|
REASON="mensuel (1er du mois, dans les ${RETENTION_MONTHLY} derniers)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$KEEP" == "true" ]]; then
|
||||||
|
log " 💾 Gardé : $backup_name ($REASON)"
|
||||||
|
else
|
||||||
|
log " 🗑️ Suppression : $backup_name"
|
||||||
|
rm -rf "$backup_path"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Nettoyage hebdomadaire : garder les N derniers dimanches
|
# ─── 8. Backup distant ──────────────────────────────────────────────────────
|
||||||
if [[ "$DAY_OF_WEEK" == "7" ]]; then
|
sync_remote "$BACKUP_PATH" || true
|
||||||
find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" | sort -r | tail -n +$((RETENTION_WEEKLY + 1)) | while read -r backup; do
|
|
||||||
# Ne supprimer que si c'est un backup du dimanche
|
|
||||||
# (on ne touche pas aux backups mensuels du 1er)
|
|
||||||
[[ "$(basename "$backup")" =~ 01- ]] && continue
|
|
||||||
log " 🗑️ Suppression hebdo : $(basename "$backup")"
|
|
||||||
rm -rf "$backup"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Nettoyage mensuel : garder les N derniers 1er du mois
|
# ─── 9. Rapport final ───────────────────────────────────────────────────────
|
||||||
if [[ "$DAY_OF_MONTH" == "01" ]]; then
|
TOTAL_SIZE=$(du -sh "$BACKUP_PATH" 2>/dev/null | cut -f1)
|
||||||
find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*01-*" | sort -r | tail -n +$((RETENTION_MONTHLY + 1)) | while read -r backup; do
|
BACKUP_COUNT=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" 2>/dev/null | wc -l)
|
||||||
log " 🗑️ Suppression mensuel : $(basename "$backup")"
|
|
||||||
rm -rf "$backup"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ─── 8. Rapport final ───────────────────────────────────────────────────────
|
|
||||||
TOTAL_SIZE=$(du -sh "$BACKUP_PATH" | cut -f1)
|
|
||||||
BACKUP_COUNT=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "gitea-backup-*" | wc -l)
|
|
||||||
|
|
||||||
log "✅ Backup terminé !"
|
log "✅ Backup terminé !"
|
||||||
log "📊 Total : $TOTAL_SIZE"
|
log "📊 Total : ${TOTAL_SIZE:-N/A}"
|
||||||
log "📋 Backups conservés : $BACKUP_COUNT"
|
log "📋 Backups conservés : $BACKUP_COUNT"
|
||||||
log "📂 Destination : $BACKUP_PATH"
|
log "📂 Destination : $BACKUP_PATH"
|
||||||
log "═══ Fin ───────────────────────────────────────────"
|
log "═══ Fin ───────────────────────────────────────────"
|
||||||
@@ -414,7 +660,10 @@ echo " ├── gitea-db.sql.gz ← Dump MySQL"
|
|||||||
echo " ├── gitea-data.tar.gz ← Volume /data"
|
echo " ├── gitea-data.tar.gz ← Volume /data"
|
||||||
echo " ├── gitea-mysql.tar.gz ← Volume MySQL"
|
echo " ├── gitea-mysql.tar.gz ← Volume MySQL"
|
||||||
echo " ├── docker-compose.yml ← Stack Docker"
|
echo " ├── docker-compose.yml ← Stack Docker"
|
||||||
|
echo " ├── app.ini ← Configuration Gitea"
|
||||||
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"
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# gitea-scripts.conf — Configuration unifiée pour tous les scripts Gitea
|
||||||
|
# =============================================================================
|
||||||
|
# Ce fichier est sourcé par tous les scripts (auto-update, backup, install_cron).
|
||||||
|
# Les valeurs peuvent être surchargées par des variables d'environnement.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# ─── Chemins ──────────────────────────────────────────────────────────────────
|
||||||
|
SCRIPTS_DIR="${SCRIPTS_DIR:-$HOME/scripts}"
|
||||||
|
COMPOSE_DIR="${COMPOSE_DIR:-$HOME/dev/git/outils/docker/gitea}"
|
||||||
|
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||||
|
BACKUP_DIR="${BACKUP_DIR:-/NFS/BACKUP/DOCKER/dev-prod/gitea}"
|
||||||
|
LOCK_DIR="${LOCK_DIR:-/tmp}"
|
||||||
|
|
||||||
|
# ─── Conteneurs ───────────────────────────────────────────────────────────────
|
||||||
|
APP_SERVICE="${APP_SERVICE:-gitea_server}"
|
||||||
|
APP_CONTAINER="${APP_CONTAINER:-gitea_server}"
|
||||||
|
DB_SERVICE="${DB_SERVICE:-gitea_db}"
|
||||||
|
DB_CONTAINER="${DB_CONTAINER:-gitea_db}"
|
||||||
|
|
||||||
|
# ─── Image Docker ─────────────────────────────────────────────────────────────
|
||||||
|
IMAGE_NAME="${IMAGE_NAME:-gitea/gitea}"
|
||||||
|
IMAGE_TAG="${IMAGE_TAG:-latest}"
|
||||||
|
|
||||||
|
# ─── Base de données ─────────────────────────────────────────────────────────
|
||||||
|
DB_NAME="${DB_NAME:-gitea}"
|
||||||
|
DB_USER="${DB_USER:-gitea}"
|
||||||
|
DB_PASSWORD="${DB_PASSWORD:-chab30}"
|
||||||
|
DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-gitea}"
|
||||||
|
|
||||||
|
# ─── Volumes Docker (chemins HÔTE) ────────────────────────────────────────────
|
||||||
|
VOLUME_DATA="${VOLUME_DATA:-/DOCKER_CONFIG/gitea/data}"
|
||||||
|
VOLUME_MYSQL="${VOLUME_MYSQL:-/DOCKER_CONFIG/gitea/mysql}"
|
||||||
|
|
||||||
|
# ─── URLs ─────────────────────────────────────────────────────────────────────
|
||||||
|
HEALTH_CHECK_URL="${HEALTH_CHECK_URL:-http://localhost:3000}"
|
||||||
|
HEALTH_CHECK_TIMEOUT="${HEALTH_CHECK_TIMEOUT:-30}"
|
||||||
|
|
||||||
|
# ─── Logs ─────────────────────────────────────────────────────────────────────
|
||||||
|
LOG_DIR="${LOG_DIR:-/var/log}"
|
||||||
|
UPDATE_LOG="${UPDATE_LOG:-$LOG_DIR/gitea-update.log}"
|
||||||
|
BACKUP_LOG="${BACKUP_LOG:-$LOG_DIR/gitea-backup.log}"
|
||||||
|
BACKUP_CRON_LOG="${BACKUP_CRON_LOG:-$LOG_DIR/gitea-backup-cron.log}"
|
||||||
|
UPDATE_CRON_LOG="${UPDATE_CRON_LOG:-$LOG_DIR/gitea-update-cron.log}"
|
||||||
|
LOG_RETENTION_DAYS="${LOG_RETENTION_DAYS:-90}"
|
||||||
|
|
||||||
|
# ─── Rétention backups ────────────────────────────────────────────────────────
|
||||||
|
RETENTION_DAYS="${RETENTION_DAYS:-30}"
|
||||||
|
RETENTION_WEEKLY="${RETENTION_WEEKLY:-4}"
|
||||||
|
RETENTION_MONTHLY="${RETENTION_MONTHLY:-6}"
|
||||||
|
|
||||||
|
# ─── Options backup ───────────────────────────────────────────────────────────
|
||||||
|
COMPRESS="${COMPRESS:-true}"
|
||||||
|
VERIFY_BACKUP="${VERIFY_BACKUP:-true}"
|
||||||
|
STOP_CONTAINERS="${STOP_CONTAINERS:-false}"
|
||||||
|
ENABLE_NOTIFICATIONS="${ENABLE_NOTIFICATIONS:-false}"
|
||||||
|
|
||||||
|
# ─── Notifications ────────────────────────────────────────────────────────────
|
||||||
|
NOTIFY_METHOD="${NOTIFY_METHOD:-log}"
|
||||||
|
TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}"
|
||||||
|
TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID:-}"
|
||||||
|
NTPY_TOPIC="${NTPY_TOPIC:-}"
|
||||||
|
|
||||||
|
# ─── Backup distant (optionnel) ───────────────────────────────────────────────
|
||||||
|
REMOTE_BACKUP_ENABLED="${REMOTE_BACKUP_ENABLED:-false}"
|
||||||
|
REMOTE_BACKUP_METHOD="${REMOTE_BACKUP_METHOD:-rsync}"
|
||||||
|
REMOTE_BACKUP_HOST="${REMOTE_BACKUP_HOST:-}"
|
||||||
|
REMOTE_BACKUP_USER="${REMOTE_BACKUP_USER:-}"
|
||||||
|
REMOTE_BACKUP_PATH="${REMOTE_BACKUP_PATH:-}"
|
||||||
|
REMOTE_BACKUP_SSH_KEY="${REMOTE_BACKUP_SSH_KEY:-$HOME/.ssh/id_rsa}"
|
||||||
|
|
||||||
|
# ─── Cron ─────────────────────────────────────────────────────────────────────
|
||||||
|
CRON_BACKUP_SCHEDULE="${CRON_BACKUP_SCHEDULE:-0 2 * * *}"
|
||||||
|
CRON_UPDATE_SCHEDULE="${CRON_UPDATE_SCHEDULE:-0 4 * * *}"
|
||||||
|
CRON_MARKER="${CRON_MARKER:-# >>> GITEA SCRIPTS (managed by install_cron.sh) <<<}"
|
||||||
+114
-29
@@ -7,13 +7,23 @@
|
|||||||
# bash install_cron.sh --force → Installation sans confirmation
|
# bash install_cron.sh --force → Installation sans confirmation
|
||||||
# bash install_cron.sh --uninstall → Retire les entrées du cron
|
# bash install_cron.sh --uninstall → Retire les entrées du cron
|
||||||
# bash install_cron.sh --status → Vérifie si les crons sont actifs
|
# bash install_cron.sh --status → Vérifie si les crons sont actifs
|
||||||
|
# bash install_cron.sh --dry-run → Affiche ce qui serait installé
|
||||||
#
|
#
|
||||||
# Paramètres configurables (modifier ci-dessous ou passer en variables d'env)
|
# Paramètres configurables : voir gitea-scripts.conf
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ─── Configuration ──────────────────────────────────────────────────────────
|
# ─── Charger la configuration commune ─────────────────────────────────────────
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
CONFIG_FILE="${CONFIG_FILE:-$SCRIPT_DIR/gitea-scripts.conf}"
|
||||||
|
|
||||||
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Configuration locale ────────────────────────────────────────────────────
|
||||||
SCRIPTS_DIR="${SCRIPTS_DIR:-$HOME/scripts}"
|
SCRIPTS_DIR="${SCRIPTS_DIR:-$HOME/scripts}"
|
||||||
CRON_BACKUP_SCHEDULE="${CRON_BACKUP_SCHEDULE:-0 2 * * *}"
|
CRON_BACKUP_SCHEDULE="${CRON_BACKUP_SCHEDULE:-0 2 * * *}"
|
||||||
CRON_UPDATE_SCHEDULE="${CRON_UPDATE_SCHEDULE:-0 4 * * *}"
|
CRON_UPDATE_SCHEDULE="${CRON_UPDATE_SCHEDULE:-0 4 * * *}"
|
||||||
@@ -42,6 +52,7 @@ Usage: $0 [OPTION]
|
|||||||
|
|
||||||
Options :
|
Options :
|
||||||
--force Installation sans confirmation
|
--force Installation sans confirmation
|
||||||
|
--dry-run Affiche ce qui serait installé sans rien faire
|
||||||
--uninstall Retire les scripts du cron
|
--uninstall Retire les scripts du cron
|
||||||
--status Vérifie l'état des crons Gitea
|
--status Vérifie l'état des crons Gitea
|
||||||
--help Ce message
|
--help Ce message
|
||||||
@@ -73,9 +84,28 @@ check_prerequisites() {
|
|||||||
missing=1
|
missing=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Vérifier aussi la présence du fichier de config (optionnel mais recommandé)
|
||||||
|
if [[ ! -f "$SCRIPTS_DIR/gitea-scripts.conf" ]]; then
|
||||||
|
warn "Fichier de configuration introuvable : $SCRIPTS_DIR/gitea-scripts.conf"
|
||||||
|
info "Les scripts utiliseront leurs valeurs par défaut"
|
||||||
|
fi
|
||||||
|
|
||||||
return $missing
|
return $missing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── Construire le bloc cron ────────────────────────────────────────────────
|
||||||
|
build_cron_block() {
|
||||||
|
cat << CRON_BLOCK
|
||||||
|
|
||||||
|
$CRON_MARKER
|
||||||
|
# Backup quotidien Gitea (DB + volumes)
|
||||||
|
$CRON_BACKUP_SCHEDULE $SCRIPTS_DIR/$BACKUP_SCRIPT >> $BACKUP_LOG 2>&1
|
||||||
|
# Vérification mise à jour Gitea Docker
|
||||||
|
$CRON_UPDATE_SCHEDULE $SCRIPTS_DIR/$UPDATE_SCRIPT >> $UPDATE_LOG 2>&1
|
||||||
|
# >>> END GITEA SCRIPTS <<<
|
||||||
|
CRON_BLOCK
|
||||||
|
}
|
||||||
|
|
||||||
# ─── Status ─────────────────────────────────────────────────────────────────
|
# ─── Status ─────────────────────────────────────────────────────────────────
|
||||||
show_status() {
|
show_status() {
|
||||||
echo ""
|
echo ""
|
||||||
@@ -93,10 +123,15 @@ show_status() {
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo "Entrées actives :"
|
echo "Entrées actives :"
|
||||||
echo "$current_cron" | grep -A1 "$CRON_MARKER" | grep -v "$CRON_MARKER" | grep -v '^$' | while read -r line; do
|
# Extraire toutes les lignes entre les marqueurs
|
||||||
local schedule cmd rest
|
echo "$current_cron" | sed -n "/$CRON_MARKER/,/>>> END GITEA SCRIPTS <<</p" | while read -r line; do
|
||||||
|
# Ignorer les marqueurs et les commentaires
|
||||||
|
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
# Afficher la ligne de cron
|
||||||
|
local schedule cmd
|
||||||
schedule=$(echo "$line" | awk '{print $1" "$2" "$3" "$4" "$5}')
|
schedule=$(echo "$line" | awk '{print $1" "$2" "$3" "$4" "$5}')
|
||||||
cmd=$(echo "$line" | awk '{print $6}')
|
cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/ *$//')
|
||||||
echo " 🕐 $schedule → $cmd"
|
echo " 🕐 $schedule → $cmd"
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
@@ -108,14 +143,25 @@ show_status() {
|
|||||||
# Vérifier l'existence des scripts
|
# Vérifier l'existence des scripts
|
||||||
if [[ -x "$SCRIPTS_DIR/$BACKUP_SCRIPT" ]]; then
|
if [[ -x "$SCRIPTS_DIR/$BACKUP_SCRIPT" ]]; then
|
||||||
ok "Script backup : $SCRIPTS_DIR/$BACKUP_SCRIPT"
|
ok "Script backup : $SCRIPTS_DIR/$BACKUP_SCRIPT"
|
||||||
|
elif [[ -f "$SCRIPTS_DIR/$BACKUP_SCRIPT" ]]; then
|
||||||
|
warn "Script backup : $SCRIPTS_DIR/$BACKUP_SCRIPT (non exécutable)"
|
||||||
else
|
else
|
||||||
err "Script backup : $SCRIPTS_DIR/$BACKUP_SCRIPT (manquant ou non exécutable)"
|
err "Script backup : $SCRIPTS_DIR/$BACKUP_SCRIPT (manquant)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -x "$SCRIPTS_DIR/$UPDATE_SCRIPT" ]]; then
|
if [[ -x "$SCRIPTS_DIR/$UPDATE_SCRIPT" ]]; then
|
||||||
ok "Script update : $SCRIPTS_DIR/$UPDATE_SCRIPT"
|
ok "Script update : $SCRIPTS_DIR/$UPDATE_SCRIPT"
|
||||||
|
elif [[ -f "$SCRIPTS_DIR/$UPDATE_SCRIPT" ]]; then
|
||||||
|
warn "Script update : $SCRIPTS_DIR/$UPDATE_SCRIPT (non exécutable)"
|
||||||
else
|
else
|
||||||
err "Script update : $SCRIPTS_DIR/$UPDATE_SCRIPT (manquant ou non exécutable)"
|
err "Script update : $SCRIPTS_DIR/$UPDATE_SCRIPT (manquant)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Vérifier le fichier de config
|
||||||
|
if [[ -f "$SCRIPTS_DIR/gitea-scripts.conf" ]]; then
|
||||||
|
ok "Config : $SCRIPTS_DIR/gitea-scripts.conf"
|
||||||
|
else
|
||||||
|
warn "Config : $SCRIPTS_DIR/gitea-scripts.conf (absent)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -123,9 +169,9 @@ show_status() {
|
|||||||
# Vérifier les logs
|
# Vérifier les logs
|
||||||
if [[ -f "$BACKUP_LOG" ]]; then
|
if [[ -f "$BACKUP_LOG" ]]; then
|
||||||
local log_size log_lines log_date
|
local log_size log_lines log_date
|
||||||
log_size=$(du -h "$BACKUP_LOG" | cut -f1)
|
log_size=$(du -h "$BACKUP_LOG" 2>/dev/null | cut -f1)
|
||||||
log_lines=$(wc -l < "$BACKUP_LOG")
|
log_lines=$(wc -l < "$BACKUP_LOG" 2>/dev/null || echo "0")
|
||||||
log_date=$(tail -1 "$BACKUP_LOG" 2>/dev/null | cut -c1-19)
|
log_date=$(tail -1 "$BACKUP_LOG" 2>/dev/null | cut -c1-19 || echo "N/A")
|
||||||
ok "Log backup : $BACKUP_LOG ($log_size, $log_lines lignes, dernier: $log_date)"
|
ok "Log backup : $BACKUP_LOG ($log_size, $log_lines lignes, dernier: $log_date)"
|
||||||
else
|
else
|
||||||
warn "Log backup : $BACKUP_LOG (pas encore créé)"
|
warn "Log backup : $BACKUP_LOG (pas encore créé)"
|
||||||
@@ -133,9 +179,9 @@ show_status() {
|
|||||||
|
|
||||||
if [[ -f "$UPDATE_LOG" ]]; then
|
if [[ -f "$UPDATE_LOG" ]]; then
|
||||||
local log_size2 log_lines2 log_date2
|
local log_size2 log_lines2 log_date2
|
||||||
log_size2=$(du -h "$UPDATE_LOG" | cut -f1)
|
log_size2=$(du -h "$UPDATE_LOG" 2>/dev/null | cut -f1)
|
||||||
log_lines2=$(wc -l < "$UPDATE_LOG")
|
log_lines2=$(wc -l < "$UPDATE_LOG" 2>/dev/null || echo "0")
|
||||||
log_date2=$(tail -1 "$UPDATE_LOG" 2>/dev/null | cut -c1-19)
|
log_date2=$(tail -1 "$UPDATE_LOG" 2>/dev/null | cut -c1-19 || echo "N/A")
|
||||||
ok "Log update : $UPDATE_LOG ($log_size2, $log_lines2 lignes, dernier: $log_date2)"
|
ok "Log update : $UPDATE_LOG ($log_size2, $log_lines2 lignes, dernier: $log_date2)"
|
||||||
else
|
else
|
||||||
warn "Log update : $UPDATE_LOG (pas encore créé)"
|
warn "Log update : $UPDATE_LOG (pas encore créé)"
|
||||||
@@ -173,7 +219,13 @@ do_install() {
|
|||||||
ok "Scripts rendus exécutables"
|
ok "Scripts rendus exécutables"
|
||||||
|
|
||||||
# Créer les dossiers de logs si nécessaire
|
# Créer les dossiers de logs si nécessaire
|
||||||
if [[ "$LOG_DIR" != "$HOME"* ]] && [[ ! -w "$LOG_DIR" ]]; then
|
if [[ ! -d "$LOG_DIR" ]]; then
|
||||||
|
mkdir -p "$LOG_DIR" 2>/dev/null || {
|
||||||
|
warn "Impossible de créer $LOG_DIR — les logs iront dans \$HOME"
|
||||||
|
BACKUP_LOG="$HOME/gitea-backup-cron.log"
|
||||||
|
UPDATE_LOG="$HOME/gitea-update-cron.log"
|
||||||
|
}
|
||||||
|
elif [[ ! -w "$LOG_DIR" ]]; then
|
||||||
warn "Pas de droit d'écriture sur $LOG_DIR — les logs iront dans \$HOME"
|
warn "Pas de droit d'écriture sur $LOG_DIR — les logs iront dans \$HOME"
|
||||||
BACKUP_LOG="$HOME/gitea-backup-cron.log"
|
BACKUP_LOG="$HOME/gitea-backup-cron.log"
|
||||||
UPDATE_LOG="$HOME/gitea-update-cron.log"
|
UPDATE_LOG="$HOME/gitea-update-cron.log"
|
||||||
@@ -193,19 +245,14 @@ do_install() {
|
|||||||
|
|
||||||
# Construire le bloc cron
|
# Construire le bloc cron
|
||||||
local cron_block
|
local cron_block
|
||||||
cron_block=$(cat << CRON_BLOCK
|
cron_block=$(build_cron_block)
|
||||||
|
|
||||||
$CRON_MARKER
|
# Appliquer : gérer le cas où la crontab est vide
|
||||||
# Backup quotidien Gitea (DB + volumes)
|
if [[ -z "$current_cron" ]]; then
|
||||||
$CRON_BACKUP_SCHEDULE $SCRIPTS_DIR/$BACKUP_SCRIPT >> $BACKUP_LOG 2>&1
|
echo "$cron_block" | crontab -
|
||||||
# Vérification mise à jour Gitea Docker
|
else
|
||||||
$CRON_UPDATE_SCHEDULE $SCRIPTS_DIR/$UPDATE_SCRIPT >> $UPDATE_LOG 2>&1
|
printf "%s\n%s\n" "$current_cron" "$cron_block" | crontab -
|
||||||
# >>> END GITEA SCRIPTS <<<
|
fi
|
||||||
CRON_BLOCK
|
|
||||||
)
|
|
||||||
|
|
||||||
# Appliquer
|
|
||||||
printf "%s\n" "$current_cron" "$cron_block" | crontab -
|
|
||||||
ok "Crons installés dans la crontab de $CRON_USER"
|
ok "Crons installés dans la crontab de $CRON_USER"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -226,6 +273,32 @@ CRON_BLOCK
|
|||||||
echo "════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── Dry run ────────────────────────────────────────────────────────────────
|
||||||
|
do_dry_run() {
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo "🔍 DRY RUN — APERÇU DE L'INSTALLATION"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
info "Utilisateur : $CRON_USER"
|
||||||
|
info "Dossier scripts : $SCRIPTS_DIR"
|
||||||
|
info "Horaire backup : $CRON_BACKUP_SCHEDULE"
|
||||||
|
info "Horaire update : $CRON_UPDATE_SCHEDULE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
check_prerequisites || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Bloc qui serait ajouté à la crontab :"
|
||||||
|
echo "────────────────────────────────────────"
|
||||||
|
build_cron_block
|
||||||
|
echo "────────────────────────────────────────"
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ Aucune modification n'a été effectuée."
|
||||||
|
echo " Utilisez --force pour installer sans confirmation."
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
# ─── Désinstallation ────────────────────────────────────────────────────────
|
# ─── Désinstallation ────────────────────────────────────────────────────────
|
||||||
do_uninstall() {
|
do_uninstall() {
|
||||||
echo ""
|
echo ""
|
||||||
@@ -246,13 +319,18 @@ do_uninstall() {
|
|||||||
local new_cron
|
local new_cron
|
||||||
new_cron=$(echo "$current_cron" | sed "/$CRON_MARKER/,/>>> END GITEA SCRIPTS <<</d")
|
new_cron=$(echo "$current_cron" | sed "/$CRON_MARKER/,/>>> END GITEA SCRIPTS <<</d")
|
||||||
|
|
||||||
if [[ -z "$new_cron" || "$new_cron" == "$current_cron" ]]; then
|
# Supprimer les lignes vides en trop
|
||||||
echo "$new_cron" | crontab - 2>/dev/null || crontab -r 2>/dev/null || true
|
new_cron=$(echo "$new_cron" | sed '/^$/N;/^\n$/d')
|
||||||
|
|
||||||
|
if [[ -z "${new_cron// }" ]]; then
|
||||||
|
# Crontab complètement vide après suppression
|
||||||
|
crontab -r 2>/dev/null || true
|
||||||
|
ok "Crontab vidée et crons Gitea retirés"
|
||||||
else
|
else
|
||||||
echo "$new_cron" | crontab -
|
echo "$new_cron" | crontab -
|
||||||
|
ok "Crons Gitea retirés de la crontab de $CRON_USER"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ok "Crons Gitea retirés de la crontab de $CRON_USER"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo " Les scripts eux-mêmes n'ont PAS été supprimés."
|
echo " Les scripts eux-mêmes n'ont PAS été supprimés."
|
||||||
echo " Ils restent dans $SCRIPTS_DIR/"
|
echo " Ils restent dans $SCRIPTS_DIR/"
|
||||||
@@ -264,12 +342,14 @@ do_uninstall() {
|
|||||||
FORCE=false
|
FORCE=false
|
||||||
UNINSTALL=false
|
UNINSTALL=false
|
||||||
STATUS=false
|
STATUS=false
|
||||||
|
DRY_RUN=false
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--force) FORCE=true ;;
|
--force) FORCE=true ;;
|
||||||
--uninstall) UNINSTALL=true ;;
|
--uninstall) UNINSTALL=true ;;
|
||||||
--status) STATUS=true ;;
|
--status) STATUS=true ;;
|
||||||
|
--dry-run) DRY_RUN=true ;;
|
||||||
--help|-h) usage ;;
|
--help|-h) usage ;;
|
||||||
*) echo "Option inconnue : $arg"; usage ;;
|
*) echo "Option inconnue : $arg"; usage ;;
|
||||||
esac
|
esac
|
||||||
@@ -285,6 +365,11 @@ if [[ "$UNINSTALL" == "true" ]]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
do_dry_run
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
if [[ "$FORCE" != "true" ]]; then
|
if [[ "$FORCE" != "true" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
+414
@@ -0,0 +1,414 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# =============================================================================
|
||||||
|
# restore.sh — Restauration Gitea depuis un backup
|
||||||
|
# =============================================================================
|
||||||
|
# Usage :
|
||||||
|
# bash restore.sh <chemin_backup> → Restauration interactive
|
||||||
|
# bash restore.sh <chemin_backup> --force → Sans confirmation
|
||||||
|
# bash restore.sh <chemin_backup> --dry-run → Vérification sans exécuter
|
||||||
|
#
|
||||||
|
# Exemples :
|
||||||
|
# bash restore.sh /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-20260626-020000
|
||||||
|
# bash restore.sh /NFS/BACKUP/DOCKER/dev-prod/gitea/gitea-backup-20260626-020000 --dry-run
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ─── Couleurs ───────────────────────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||||||
|
ok() { echo -e " ${GREEN}✅${NC} $1"; }
|
||||||
|
warn() { echo -e " ${YELLOW}⚠️${NC} $1"; }
|
||||||
|
err() { echo -e " ${RED}❌${NC} $1"; }
|
||||||
|
info() { echo -e " ${CYAN}📋${NC} $1"; }
|
||||||
|
|
||||||
|
# ─── Usage ──────────────────────────────────────────────────────────────────
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: $0 <chemin_backup> [OPTIONS]
|
||||||
|
|
||||||
|
Arguments :
|
||||||
|
chemin_backup Dossier du backup à restaurer (ex: gitea-backup-YYYYMMDD-HHMMSS)
|
||||||
|
|
||||||
|
Options :
|
||||||
|
--force Ne pas demander confirmation
|
||||||
|
--dry-run Affiche ce qui serait fait sans exécuter
|
||||||
|
--help Cette aide
|
||||||
|
|
||||||
|
Variables d'environnement :
|
||||||
|
TARGET_COMPOSE_DIR Dossier docker-compose cible (défaut: ~/dev/git/outils/docker/gitea)
|
||||||
|
TARGET_VOLUME_DATA Volume data cible (défaut: /DOCKER_CONFIG/gitea/data)
|
||||||
|
TARGET_VOLUME_MYSQL Volume MySQL cible (défaut: /DOCKER_CONFIG/gitea/mysql)
|
||||||
|
DB_ROOT_PASSWORD Mot de passe root MySQL (défaut: gitea)
|
||||||
|
DB_NAME Nom base de données (défaut: gitea)
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Vérifications ──────────────────────────────────────────────────────────
|
||||||
|
check_prerequisites() {
|
||||||
|
local missing=0
|
||||||
|
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
err "Docker n'est pas installé"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v tar &>/dev/null; then
|
||||||
|
err "tar n'est pas installé"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if docker compose version &>/dev/null; then
|
||||||
|
DOCKER_COMPOSE="docker compose"
|
||||||
|
elif command -v docker-compose &>/dev/null; then
|
||||||
|
DOCKER_COMPOSE="docker-compose"
|
||||||
|
else
|
||||||
|
err "Ni 'docker compose' ni 'docker-compose' n'est disponible"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $missing
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Lister les backups disponibles ─────────────────────────────────────────
|
||||||
|
list_backups() {
|
||||||
|
local dir="${1:-.}"
|
||||||
|
echo ""
|
||||||
|
echo "Backups disponibles :"
|
||||||
|
echo "─────────────────────"
|
||||||
|
local count=0
|
||||||
|
for d in "$dir"/gitea-backup-*; do
|
||||||
|
if [[ -d "$d" ]]; then
|
||||||
|
count=$((count + 1))
|
||||||
|
local info_file="$d/backup-info.txt"
|
||||||
|
local date_info=""
|
||||||
|
if [[ -f "$info_file" ]]; then
|
||||||
|
date_info=$(grep "^Date" "$info_file" 2>/dev/null | sed 's/Date[[:space:]]*:[[:space:]]*//' || echo "inconnue")
|
||||||
|
fi
|
||||||
|
echo " $count. $(basename "$d") ($date_info)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $count -eq 0 ]]; then
|
||||||
|
echo " Aucun backup trouvé dans $dir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Utilisez : $0 <chemin_backup>"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# MAIN
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
FORCE=false
|
||||||
|
DRY_RUN=false
|
||||||
|
BACKUP_PATH=""
|
||||||
|
|
||||||
|
# Détection docker compose (par défaut, sera surchargé après check)
|
||||||
|
DOCKER_COMPOSE="docker compose"
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--force) FORCE=true ;;
|
||||||
|
--dry-run) DRY_RUN=true ;;
|
||||||
|
--help|-h) usage ;;
|
||||||
|
--*) echo "Option inconnue : $arg"; usage ;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$BACKUP_PATH" ]]; then
|
||||||
|
BACKUP_PATH="$arg"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Si aucun backup spécifié, lister les backups et suggérer
|
||||||
|
if [[ -z "$BACKUP_PATH" ]]; then
|
||||||
|
# Chercher dans les emplacements courants
|
||||||
|
FOUND=false
|
||||||
|
for search_dir in "/NFS/BACKUP/DOCKER/dev-prod/gitea" "$HOME/backups/gitea" "."; do
|
||||||
|
if [[ -d "$search_dir" ]]; then
|
||||||
|
list_backups "$search_dir" && FOUND=true && break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$FOUND" != "true" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Usage: $0 <chemin_backup> [OPTIONS]"
|
||||||
|
echo " $0 --help pour plus d'informations"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Résoudre le chemin absolu
|
||||||
|
BACKUP_PATH="$(cd "$(dirname "$BACKUP_PATH")" 2>/dev/null && pwd)/$(basename "$BACKUP_PATH")" || BACKUP_PATH="$(realpath "$BACKUP_PATH" 2>/dev/null || echo "$BACKUP_PATH")"
|
||||||
|
|
||||||
|
# Vérifier que le dossier de backup existe et est valide
|
||||||
|
if [[ ! -d "$BACKUP_PATH" ]]; then
|
||||||
|
err "Dossier de backup introuvable : $BACKUP_PATH"
|
||||||
|
echo ""
|
||||||
|
echo "Recherche de backups dans les emplacements standards..."
|
||||||
|
for search_dir in "/NFS/BACKUP/DOCKER/dev-prod/gitea" "$HOME/backups/gitea" "."; do
|
||||||
|
if [[ -d "$search_dir" ]]; then
|
||||||
|
list_backups "$search_dir"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Vérifier les fichiers requis
|
||||||
|
MISSING_FILES=()
|
||||||
|
for required_file in "gitea-mysql.tar.gz" "gitea-data.tar.gz" "docker-compose.yml"; do
|
||||||
|
if [[ ! -f "$BACKUP_PATH/$required_file" ]]; then
|
||||||
|
MISSING_FILES+=("$required_file")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Vérifier le dump SQL (compressé ou non)
|
||||||
|
if [[ ! -f "$BACKUP_PATH/gitea-db.sql.gz" ]] && [[ ! -f "$BACKUP_PATH/gitea-db.sql" ]]; then
|
||||||
|
MISSING_FILES+=("gitea-db.sql ou gitea-db.sql.gz")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#MISSING_FILES[@]} -gt 0 ]]; then
|
||||||
|
err "Fichiers requis manquants dans le backup :"
|
||||||
|
for f in "${MISSING_FILES[@]}"; do
|
||||||
|
err " - $f"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
warn "Ce dossier ne semble pas être un backup Gitea valide."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo "🔄 RESTAURATION GITEA"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ─── Configuration cible ────────────────────────────────────────────────────
|
||||||
|
TARGET_COMPOSE_DIR="${TARGET_COMPOSE_DIR:-$HOME/dev/git/outils/docker/gitea}"
|
||||||
|
TARGET_VOLUME_DATA="${TARGET_VOLUME_DATA:-/DOCKER_CONFIG/gitea/data}"
|
||||||
|
TARGET_VOLUME_MYSQL="${TARGET_VOLUME_MYSQL:-/DOCKER_CONFIG/gitea/mysql}"
|
||||||
|
DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-gitea}"
|
||||||
|
DB_NAME="${DB_NAME:-gitea}"
|
||||||
|
DB_USER="${DB_USER:-gitea}"
|
||||||
|
|
||||||
|
# ─── Afficher les infos du backup ────────────────────────────────────────────
|
||||||
|
BACKUP_NAME=$(basename "$BACKUP_PATH")
|
||||||
|
BACKUP_DATE=${BACKUP_NAME#gitea-backup-}
|
||||||
|
|
||||||
|
if [[ -f "$BACKUP_PATH/backup-info.txt" ]]; then
|
||||||
|
info "Informations du backup :"
|
||||||
|
while IFS= read -r line; do
|
||||||
|
echo " $line"
|
||||||
|
done < "$BACKUP_PATH/backup-info.txt"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
info "Backup : $BACKUP_NAME"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Configuration cible :"
|
||||||
|
echo " Compose dir : $TARGET_COMPOSE_DIR"
|
||||||
|
echo " Volume data : $TARGET_VOLUME_DATA"
|
||||||
|
echo " Volume MySQL: $TARGET_VOLUME_MYSQL"
|
||||||
|
echo " DB name : $DB_NAME"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ─── Vérifier les prérequis ─────────────────────────────────────────────────
|
||||||
|
check_prerequisites || {
|
||||||
|
echo ""
|
||||||
|
err "Prérequis manquants — impossible de continuer"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Dry-run ────────────────────────────────────────────────────────────────
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo "🔍 DRY RUN — aucune action ne sera effectuée"
|
||||||
|
echo ""
|
||||||
|
echo "Actions qui seraient exécutées :"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Arrêter les conteneurs Gitea existants"
|
||||||
|
echo " cd $TARGET_COMPOSE_DIR && $DOCKER_COMPOSE down"
|
||||||
|
echo ""
|
||||||
|
echo " 2. Créer les répertoires cibles"
|
||||||
|
echo " mkdir -p $TARGET_VOLUME_DATA $TARGET_VOLUME_MYSQL"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Restaurer le volume MySQL"
|
||||||
|
echo " rm -rf ${TARGET_VOLUME_MYSQL}/*"
|
||||||
|
echo " tar -xzf $BACKUP_PATH/gitea-mysql.tar.gz -C $(dirname "$TARGET_VOLUME_MYSQL")"
|
||||||
|
echo ""
|
||||||
|
echo " 4. Restaurer le volume Gitea"
|
||||||
|
echo " rm -rf ${TARGET_VOLUME_DATA}/*"
|
||||||
|
echo " tar -xzf $BACKUP_PATH/gitea-data.tar.gz -C $(dirname "$TARGET_VOLUME_DATA")"
|
||||||
|
echo ""
|
||||||
|
echo " 5. Copier docker-compose.yml"
|
||||||
|
echo " cp $BACKUP_PATH/docker-compose.yml $TARGET_COMPOSE_DIR/"
|
||||||
|
echo ""
|
||||||
|
if [[ -f "$BACKUP_PATH/app.ini" ]]; then
|
||||||
|
echo " 6. Restaurer app.ini"
|
||||||
|
echo " mkdir -p ${TARGET_VOLUME_DATA}/gitea/conf"
|
||||||
|
echo " cp $BACKUP_PATH/app.ini ${TARGET_VOLUME_DATA}/gitea/conf/"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
echo " 7. Démarrer les conteneurs"
|
||||||
|
echo " cd $TARGET_COMPOSE_DIR && $DOCKER_COMPOSE up -d"
|
||||||
|
echo ""
|
||||||
|
echo " 8. Restaurer la base de données"
|
||||||
|
echo " docker exec -i gitea_db mysql -u root -p$DB_ROOT_PASSWORD $DB_NAME < dump"
|
||||||
|
echo ""
|
||||||
|
echo " 9. Redémarrer"
|
||||||
|
echo " $DOCKER_COMPOSE restart"
|
||||||
|
echo ""
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Confirmation ───────────────────────────────────────────────────────────
|
||||||
|
if [[ "$FORCE" != "true" ]]; then
|
||||||
|
echo "⚠️ ATTENTION : Cette opération va ÉCRASER l'installation Gitea existante !"
|
||||||
|
echo " Toute donnée non sauvegardée sera PERDUE."
|
||||||
|
echo ""
|
||||||
|
echo " Les conteneurs seront arrêtés et les volumes remplacés."
|
||||||
|
echo ""
|
||||||
|
read -p "Taper 'RESTORE' (en majuscules) pour confirmer : " CONFIRM
|
||||||
|
if [[ "$CONFIRM" != "RESTORE" ]]; then
|
||||||
|
echo "❌ Restauration annulée."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# RESTAURATION
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
TIMING_START=$(date +%s)
|
||||||
|
|
||||||
|
# ─── Étape 1 : Arrêter les conteneurs existants ─────────────────────────────
|
||||||
|
echo "📦 Étape 1/7 : Arrêt des conteneurs existants..."
|
||||||
|
if [[ -f "$TARGET_COMPOSE_DIR/docker-compose.yml" ]] || [[ -f "$TARGET_COMPOSE_DIR/$COMPOSE_FILE" ]]; then
|
||||||
|
cd "$TARGET_COMPOSE_DIR" 2>/dev/null && $DOCKER_COMPOSE down 2>&1 || true
|
||||||
|
fi
|
||||||
|
ok "Conteneurs arrêtés"
|
||||||
|
|
||||||
|
# ─── Étape 2 : Créer les répertoires ────────────────────────────────────────
|
||||||
|
echo "📦 Étape 2/7 : Création des répertoires cibles..."
|
||||||
|
mkdir -p "$TARGET_VOLUME_DATA" "$TARGET_VOLUME_MYSQL" "$TARGET_COMPOSE_DIR"
|
||||||
|
ok "Répertoires créés"
|
||||||
|
|
||||||
|
# ─── Étape 3 : Restaurer volume MySQL ───────────────────────────────────────
|
||||||
|
echo "📦 Étape 3/7 : Restauration volume MySQL..."
|
||||||
|
rm -rf "${TARGET_VOLUME_MYSQL:?}"/*
|
||||||
|
if ! tar -xzf "$BACKUP_PATH/gitea-mysql.tar.gz" -C "$(dirname "$TARGET_VOLUME_MYSQL")"; then
|
||||||
|
err "Échec de l'extraction du volume MySQL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ok "Volume MySQL restauré"
|
||||||
|
|
||||||
|
# ─── Étape 4 : Restaurer volume Gitea ───────────────────────────────────────
|
||||||
|
echo "📦 Étape 4/7 : Restauration volume Gitea..."
|
||||||
|
rm -rf "${TARGET_VOLUME_DATA:?}"/*
|
||||||
|
if ! tar -xzf "$BACKUP_PATH/gitea-data.tar.gz" -C "$(dirname "$TARGET_VOLUME_DATA")"; then
|
||||||
|
err "Échec de l'extraction du volume Gitea"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ok "Volume Gitea restauré"
|
||||||
|
|
||||||
|
# ─── Étape 5 : Copier docker-compose ────────────────────────────────────────
|
||||||
|
echo "📦 Étape 5/7 : Copie docker-compose et configuration..."
|
||||||
|
cp "$BACKUP_PATH/docker-compose.yml" "$TARGET_COMPOSE_DIR/"
|
||||||
|
ok "docker-compose.yml copié"
|
||||||
|
|
||||||
|
if [[ -f "$BACKUP_PATH/app.ini" ]]; then
|
||||||
|
mkdir -p "${TARGET_VOLUME_DATA}/gitea/conf"
|
||||||
|
cp "$BACKUP_PATH/app.ini" "${TARGET_VOLUME_DATA}/gitea/conf/app.ini"
|
||||||
|
ok "app.ini restauré"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Étape 6 : Démarrer les conteneurs ──────────────────────────────────────
|
||||||
|
echo "📦 Étape 6/7 : Démarrage des conteneurs..."
|
||||||
|
cd "$TARGET_COMPOSE_DIR"
|
||||||
|
if ! $DOCKER_COMPOSE up -d; then
|
||||||
|
err "Échec du démarrage des conteneurs"
|
||||||
|
warn "Vérifiez les logs : $DOCKER_COMPOSE logs"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ok "Conteneurs démarrés"
|
||||||
|
|
||||||
|
# Attendre que MySQL soit prêt
|
||||||
|
echo " ⏳ Attente démarrage MySQL..."
|
||||||
|
MYSQL_READY=false
|
||||||
|
sleep 10
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if docker exec gitea_db mysqladmin ping -u root -p"$DB_ROOT_PASSWORD" --silent 2>/dev/null; then
|
||||||
|
echo " ✅ MySQL prêt après $((10 + i))s"
|
||||||
|
MYSQL_READY=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$MYSQL_READY" != "true" ]]; then
|
||||||
|
warn "MySQL n'a pas répondu — on tente la restauration DB quand même..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Étape 7 : Restaurer la base de données ─────────────────────────────────
|
||||||
|
echo "📦 Étape 7/7 : Restauration base de données..."
|
||||||
|
|
||||||
|
# Créer la base de données si elle n'existe pas
|
||||||
|
docker exec gitea_db 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
|
||||||
|
|
||||||
|
# Restaurer le dump
|
||||||
|
if [[ -f "$BACKUP_PATH/gitea-db.sql.gz" ]]; then
|
||||||
|
gunzip -c "$BACKUP_PATH/gitea-db.sql.gz" | docker exec -i gitea_db mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" 2>&1 || {
|
||||||
|
err "Échec de la restauration du dump SQL (compressé)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
elif [[ -f "$BACKUP_PATH/gitea-db.sql" ]]; then
|
||||||
|
docker exec -i gitea_db mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" < "$BACKUP_PATH/gitea-db.sql" 2>&1 || {
|
||||||
|
err "Échec de la restauration du dump SQL"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
ok "Base de données restaurée"
|
||||||
|
|
||||||
|
# ─── Redémarrage final ──────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "🔄 Redémarrage final des conteneurs..."
|
||||||
|
$DOCKER_COMPOSE restart 2>&1 || true
|
||||||
|
|
||||||
|
# ─── Vérification santé ─────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "🏥 Vérification que Gitea répond..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:3000" 2>/dev/null || echo "000")
|
||||||
|
if [[ "$STATUS" == "200" || "$STATUS" == "302" || "$STATUS" == "303" ]]; then
|
||||||
|
ok "Gitea répond (HTTP $STATUS) après ${i}s"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [[ $i -eq 30 ]]; then
|
||||||
|
warn "Gitea ne répond pas encore — vérifiez les logs"
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# ─── Résumé ─────────────────────────────────────────────────────────────────
|
||||||
|
TIMING_END=$(date +%s)
|
||||||
|
DURATION=$((TIMING_END - TIMING_START))
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo "✅ RESTAURATION TERMINÉE !"
|
||||||
|
echo ""
|
||||||
|
echo " Durée : ${DURATION}s"
|
||||||
|
echo " Backup : $BACKUP_NAME"
|
||||||
|
echo " Cible : $TARGET_COMPOSE_DIR"
|
||||||
|
echo " Gitea : http://localhost:3000"
|
||||||
|
echo ""
|
||||||
|
echo " Logs : $DOCKER_COMPOSE logs -f"
|
||||||
|
echo " Status : $DOCKER_COMPOSE ps"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# =============================================================================
|
||||||
|
# setup.sh — Installation complète des scripts Gitea
|
||||||
|
# =============================================================================
|
||||||
|
# Usage :
|
||||||
|
# bash setup.sh → Installation interactive complète
|
||||||
|
# bash setup.sh --force → Installation sans confirmation
|
||||||
|
# bash setup.sh --update → Mise à jour des scripts (git pull)
|
||||||
|
# bash setup.sh --dry-run → Affiche ce qui serait fait
|
||||||
|
#
|
||||||
|
# Ce script :
|
||||||
|
# 1. Clone ou met à jour les scripts depuis Git
|
||||||
|
# 2. Vérifie que tous les prérequis sont installés
|
||||||
|
# 3. Rend les scripts exécutables
|
||||||
|
# 4. Crée les dossiers de logs
|
||||||
|
# 5. Installe les entrées cron
|
||||||
|
# 6. Configure la rotation des logs (logrotate)
|
||||||
|
# 7. Propose un premier backup
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ─── Couleurs ───────────────────────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
|
ok() { echo -e " ${GREEN}✅${NC} $1"; }
|
||||||
|
warn() { echo -e " ${YELLOW}⚠️${NC} $1"; }
|
||||||
|
err() { echo -e " ${RED}❌${NC} $1"; }
|
||||||
|
info() { echo -e " ${CYAN}📋${NC} $1"; }
|
||||||
|
step() { echo -e "\n${BLUE}════════════════════════════════════════════════════════${NC}"; echo -e "${BLUE}🔧 $1${NC}"; echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"; }
|
||||||
|
|
||||||
|
# ─── Configuration ──────────────────────────────────────────────────────────
|
||||||
|
GIT_REPO="${GIT_REPO:-https://git.dracodev.net/Outils/gitea-scripts.git}"
|
||||||
|
SCRIPTS_DIR="${SCRIPTS_DIR:-$HOME/scripts}"
|
||||||
|
BRANCH="${BRANCH:-main}"
|
||||||
|
|
||||||
|
# ─── Usage ──────────────────────────────────────────────────────────────────
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: $0 [OPTIONS]
|
||||||
|
|
||||||
|
Options :
|
||||||
|
--force Installation sans confirmation
|
||||||
|
--update Mettre à jour les scripts existants (git pull)
|
||||||
|
--dry-run Affiche ce qui serait fait sans rien installer
|
||||||
|
--help Cette aide
|
||||||
|
|
||||||
|
Variables d'environnement :
|
||||||
|
GIT_REPO URL du dépôt Git (défaut: $GIT_REPO)
|
||||||
|
SCRIPTS_DIR Dossier d'installation (défaut: \$HOME/scripts)
|
||||||
|
BRANCH Branche Git à utiliser (défaut: main)
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Vérification des prérequis système ─────────────────────────────────────
|
||||||
|
check_system_prerequisites() {
|
||||||
|
step "Étape 1/6 : Vérification des prérequis système"
|
||||||
|
|
||||||
|
local missing=0
|
||||||
|
|
||||||
|
info "Vérification des commandes requises..."
|
||||||
|
|
||||||
|
for cmd in git docker curl tar gzip crontab; do
|
||||||
|
if command -v "$cmd" &>/dev/null; then
|
||||||
|
local version=""
|
||||||
|
case "$cmd" in
|
||||||
|
git) version=$(git --version 2>/dev/null | head -1) ;;
|
||||||
|
docker) version=$(docker --version 2>/dev/null | head -1) ;;
|
||||||
|
curl) version=$(curl --version 2>/dev/null | head -1) ;;
|
||||||
|
esac
|
||||||
|
ok "$cmd ${version:+($version)}"
|
||||||
|
else
|
||||||
|
err "$cmd : NON INSTALLÉ"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Vérifier docker compose
|
||||||
|
if docker compose version &>/dev/null; then
|
||||||
|
ok "docker compose (plugin)"
|
||||||
|
elif command -v docker-compose &>/dev/null; then
|
||||||
|
ok "docker-compose (standalone)"
|
||||||
|
else
|
||||||
|
err "docker compose : NON INSTALLÉ"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Vérifier le daemon Docker
|
||||||
|
if docker info &>/dev/null; then
|
||||||
|
ok "Docker daemon : actif"
|
||||||
|
else
|
||||||
|
warn "Docker daemon : injoignable (vérifier que Docker tourne)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $missing -gt 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
err "$missing prérequis manquants. Installez-les avant de continuer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Cloner ou mettre à jour les scripts ────────────────────────────────────
|
||||||
|
install_scripts() {
|
||||||
|
step "Étape 2/6 : Installation des scripts"
|
||||||
|
|
||||||
|
if [[ -d "$SCRIPTS_DIR/.git" ]]; then
|
||||||
|
info "Dépôt existant trouvé dans $SCRIPTS_DIR"
|
||||||
|
cd "$SCRIPTS_DIR"
|
||||||
|
local current_remote
|
||||||
|
current_remote=$(git remote get-url origin 2>/dev/null || echo "")
|
||||||
|
if [[ "$current_remote" == "$GIT_REPO" ]]; then
|
||||||
|
info "Mise à jour depuis $GIT_REPO (branche: $BRANCH)..."
|
||||||
|
git fetch origin "$BRANCH" 2>&1 || {
|
||||||
|
warn "Impossible de fetch — on continue avec la version actuelle"
|
||||||
|
}
|
||||||
|
if git diff --quiet "origin/$BRANCH" 2>/dev/null; then
|
||||||
|
ok "Scripts déjà à jour"
|
||||||
|
else
|
||||||
|
git pull origin "$BRANCH" 2>&1 || {
|
||||||
|
warn "Impossible de pull — on continue avec la version actuelle"
|
||||||
|
}
|
||||||
|
ok "Scripts mis à jour"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Remote différent ($current_remote != $GIT_REPO) — pas de mise à jour automatique"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "Clonage de $GIT_REPO vers $SCRIPTS_DIR..."
|
||||||
|
if [[ -d "$SCRIPTS_DIR" ]]; then
|
||||||
|
warn "$SCRIPTS_DIR existe déjà mais n'est pas un dépôt Git"
|
||||||
|
info "Sauvegarde dans ${SCRIPTS_DIR}.bak..."
|
||||||
|
mv "$SCRIPTS_DIR" "${SCRIPTS_DIR}.bak.$(date +%s)" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
git clone --branch "$BRANCH" "$GIT_REPO" "$SCRIPTS_DIR" 2>&1 || {
|
||||||
|
err "Échec du clone de $GIT_REPO"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
ok "Scripts clonés dans $SCRIPTS_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Vérifier que les scripts principaux existent
|
||||||
|
local scripts_ok=true
|
||||||
|
for script in "gitea-backup.sh" "gitea-auto-update.sh" "install_cron.sh" "restore.sh" "gitea-scripts.conf"; do
|
||||||
|
if [[ -f "$SCRIPTS_DIR/$script" ]]; then
|
||||||
|
ok "$script présent"
|
||||||
|
else
|
||||||
|
warn "$script : absent"
|
||||||
|
scripts_ok=false
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$scripts_ok" != "true" ]]; then
|
||||||
|
warn "Certains scripts sont absents — le dépôt peut être incomplet"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Rendre les scripts exécutables ─────────────────────────────────────────
|
||||||
|
make_executable() {
|
||||||
|
step "Étape 3/6 : Permissions des scripts"
|
||||||
|
|
||||||
|
for script in "gitea-backup.sh" "gitea-auto-update.sh" "install_cron.sh" "restore.sh" "setup.sh"; do
|
||||||
|
if [[ -f "$SCRIPTS_DIR/$script" ]]; then
|
||||||
|
chmod +x "$SCRIPTS_DIR/$script"
|
||||||
|
ok "$script → exécutable"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Créer les dossiers de logs ─────────────────────────────────────────────
|
||||||
|
setup_logs() {
|
||||||
|
step "Étape 4/6 : Configuration des logs"
|
||||||
|
|
||||||
|
# Charger la config pour connaître LOG_DIR
|
||||||
|
if [[ -f "$SCRIPTS_DIR/gitea-scripts.conf" ]]; then
|
||||||
|
source "$SCRIPTS_DIR/gitea-scripts.conf"
|
||||||
|
fi
|
||||||
|
LOG_DIR="${LOG_DIR:-/var/log}"
|
||||||
|
|
||||||
|
if [[ -d "$LOG_DIR" ]] && [[ -w "$LOG_DIR" ]]; then
|
||||||
|
ok "Dossier de logs $LOG_DIR accessible"
|
||||||
|
elif [[ -d "$LOG_DIR" ]]; then
|
||||||
|
warn "Pas de droit d'écriture sur $LOG_DIR"
|
||||||
|
info "Les logs seront écrits dans $HOME/"
|
||||||
|
else
|
||||||
|
if mkdir -p "$LOG_DIR" 2>/dev/null; then
|
||||||
|
ok "Dossier de logs $LOG_DIR créé"
|
||||||
|
else
|
||||||
|
warn "Impossible de créer $LOG_DIR — logs dans $HOME/"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Créer les fichiers de log s'ils n'existent pas
|
||||||
|
touch "$HOME/gitea-backup-cron.log" "$HOME/gitea-update-cron.log" 2>/dev/null || true
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Configurer logrotate ───────────────────────────────────────────────────
|
||||||
|
setup_logrotate() {
|
||||||
|
step "Étape 5/6 : Rotation des logs"
|
||||||
|
|
||||||
|
local logrotate_conf="/etc/logrotate.d/gitea-scripts"
|
||||||
|
local need_sudo=false
|
||||||
|
|
||||||
|
if [[ -f "$logrotate_conf" ]]; then
|
||||||
|
ok "logrotate déjà configuré : $logrotate_conf"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -w "/etc/logrotate.d" ]]; then
|
||||||
|
need_sudo=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
local log_files="$HOME/gitea-backup-cron.log $HOME/gitea-update-cron.log /var/log/gitea-backup.log /var/log/gitea-update.log"
|
||||||
|
|
||||||
|
cat << LOGROTATE
|
||||||
|
# Rotation des logs Gitea scripts
|
||||||
|
$log_files {
|
||||||
|
weekly
|
||||||
|
rotate 12
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
copytruncate
|
||||||
|
create 0644 $USER $USER
|
||||||
|
}
|
||||||
|
LOGROTATE
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [[ "$need_sudo" == "true" ]]; then
|
||||||
|
warn "Droits sudo requis pour créer $logrotate_conf"
|
||||||
|
info "Voici la commande à exécuter :"
|
||||||
|
echo ""
|
||||||
|
echo " sudo tee $logrotate_conf << 'EOF'"
|
||||||
|
cat << LOGROTATE
|
||||||
|
# Rotation des logs Gitea scripts
|
||||||
|
$log_files {
|
||||||
|
weekly
|
||||||
|
rotate 12
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
copytruncate
|
||||||
|
create 0644 $USER $USER
|
||||||
|
}
|
||||||
|
LOGROTATE
|
||||||
|
echo "EOF"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "Exécuter cette commande maintenant ? [O/n] " -r REPLY
|
||||||
|
if [[ "$REPLY" =~ ^[OoYy]?$ ]]; then
|
||||||
|
sudo tee "$logrotate_conf" > /dev/null << LOGROTATE_EOF
|
||||||
|
# Rotation des logs Gitea scripts
|
||||||
|
$log_files {
|
||||||
|
weekly
|
||||||
|
rotate 12
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
copytruncate
|
||||||
|
create 0644 $USER $USER
|
||||||
|
}
|
||||||
|
LOGROTATE_EOF
|
||||||
|
ok "logrotate configuré"
|
||||||
|
else
|
||||||
|
info "Vous pourrez configurer logrotate plus tard avec la commande ci-dessus"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cat > "$logrotate_conf" << LOGROTATE_EOF
|
||||||
|
# Rotation des logs Gitea scripts
|
||||||
|
$log_files {
|
||||||
|
weekly
|
||||||
|
rotate 12
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
copytruncate
|
||||||
|
create 0644 $USER $USER
|
||||||
|
}
|
||||||
|
LOGROTATE_EOF
|
||||||
|
ok "logrotate configuré : $logrotate_conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Installer les crons ────────────────────────────────────────────────────
|
||||||
|
setup_cron() {
|
||||||
|
step "Étape 6/6 : Installation des crons"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPTS_DIR/install_cron.sh" ]]; then
|
||||||
|
if [[ "$FORCE" == "true" ]]; then
|
||||||
|
bash "$SCRIPTS_DIR/install_cron.sh" --force
|
||||||
|
else
|
||||||
|
bash "$SCRIPTS_DIR/install_cron.sh"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
err "install_cron.sh introuvable"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Mise à jour (git pull) ─────────────────────────────────────────────────
|
||||||
|
do_update() {
|
||||||
|
step "Mise à jour des scripts Gitea"
|
||||||
|
|
||||||
|
if [[ ! -d "$SCRIPTS_DIR/.git" ]]; then
|
||||||
|
err "$SCRIPTS_DIR n'est pas un dépôt Git — impossible de mettre à jour"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$SCRIPTS_DIR"
|
||||||
|
info "Fetch des dernières modifications..."
|
||||||
|
git fetch origin "$BRANCH" 2>&1
|
||||||
|
|
||||||
|
if git diff --quiet "origin/$BRANCH" 2>/dev/null; then
|
||||||
|
ok "Scripts déjà à jour"
|
||||||
|
else
|
||||||
|
info "Mise à jour disponible — application..."
|
||||||
|
git pull origin "$BRANCH" 2>&1
|
||||||
|
ok "Scripts mis à jour"
|
||||||
|
|
||||||
|
# Rendre exécutables à nouveau (au cas où)
|
||||||
|
for script in *.sh; do
|
||||||
|
[[ -f "$script" ]] && chmod +x "$script"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "Scripts mis à jour : $(git log --oneline -3)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# MAIN
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
FORCE=false
|
||||||
|
DRY_RUN=false
|
||||||
|
UPDATE=false
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--force) FORCE=true ;;
|
||||||
|
--dry-run) DRY_RUN=true ;;
|
||||||
|
--update) UPDATE=true ;;
|
||||||
|
--help|-h) usage ;;
|
||||||
|
*) echo "Option inconnue : $arg"; usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔══════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ 🛠️ GITEA SCRIPTS — INSTALLATEUR ║"
|
||||||
|
echo "╚══════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
info "Dossier scripts : $SCRIPTS_DIR"
|
||||||
|
info "Dépôt Git : $GIT_REPO"
|
||||||
|
info "Branche : $BRANCH"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ─── Mode mise à jour ──────────────────────────────────────────────────────
|
||||||
|
if [[ "$UPDATE" == "true" ]]; then
|
||||||
|
do_update
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Mode dry-run ───────────────────────────────────────────────────────────
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo "🔍 DRY RUN — aucune action ne sera effectuée"
|
||||||
|
echo ""
|
||||||
|
echo "Actions qui seraient exécutées :"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Vérifier les prérequis (git, docker, curl, tar, gzip, crontab)"
|
||||||
|
echo " 2. Cloner/MàJ $GIT_REPO → $SCRIPTS_DIR"
|
||||||
|
echo " 3. Rendre les scripts exécutables"
|
||||||
|
echo " 4. Créer les dossiers de logs"
|
||||||
|
echo " 5. Configurer logrotate"
|
||||||
|
echo " 6. Installer les crons (backup + auto-update)"
|
||||||
|
echo ""
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Confirmation interactive ───────────────────────────────────────────────
|
||||||
|
if [[ "$FORCE" != "true" ]]; then
|
||||||
|
echo "Ce script va installer complètement les scripts de maintenance Gitea :"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Vérifier les prérequis système"
|
||||||
|
echo " 2. Cloner/MàJ les scripts depuis Git"
|
||||||
|
echo " 3. Rendre les scripts exécutables"
|
||||||
|
echo " 4. Créer les dossiers de logs"
|
||||||
|
echo " 5. Configurer la rotation des logs"
|
||||||
|
echo " 6. Installer les tâches cron (backup + auto-update)"
|
||||||
|
echo ""
|
||||||
|
read -p "Continuer l'installation ? [O/n] " -r REPLY
|
||||||
|
if [[ ! "$REPLY" =~ ^[OoYy]?$ ]]; then
|
||||||
|
echo "Installation annulée."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Exécution des étapes ───────────────────────────────────────────────────
|
||||||
|
check_system_prerequisites
|
||||||
|
install_scripts
|
||||||
|
make_executable
|
||||||
|
setup_logs
|
||||||
|
setup_logrotate
|
||||||
|
setup_cron
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# RÉSUMÉ FINAL
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔══════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ ✅ INSTALLATION TERMINÉE ! ║"
|
||||||
|
echo "╚══════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo "📂 Scripts installés dans : $SCRIPTS_DIR"
|
||||||
|
echo ""
|
||||||
|
echo "📋 Commandes utiles :"
|
||||||
|
echo ""
|
||||||
|
echo " 🔍 Vérifier l'état : bash $SCRIPTS_DIR/install_cron.sh --status"
|
||||||
|
echo " 💾 Backup manuel : bash $SCRIPTS_DIR/gitea-backup.sh"
|
||||||
|
echo " 🔄 Restaurer : bash $SCRIPTS_DIR/restore.sh <dossier_backup>"
|
||||||
|
echo " ⬆️ Mise à jour : bash $SCRIPTS_DIR/setup.sh --update"
|
||||||
|
echo " 🗑️ Désinstaller : bash $SCRIPTS_DIR/install_cron.sh --uninstall"
|
||||||
|
echo ""
|
||||||
|
echo "⚙️ Configuration : $SCRIPTS_DIR/gitea-scripts.conf"
|
||||||
|
echo ""
|
||||||
|
echo "📊 Logs :"
|
||||||
|
echo " Backup : ${LOG_DIR:-/var/log}/gitea-backup.log"
|
||||||
|
echo " Update : ${LOG_DIR:-/var/log}/gitea-update.log"
|
||||||
|
echo " Cron backup : $HOME/gitea-backup-cron.log"
|
||||||
|
echo " Cron update : $HOME/gitea-update-cron.log"
|
||||||
|
echo ""
|
||||||
|
echo "🕐 Tâches cron :"
|
||||||
|
echo " Backup : $(grep "$SCRIPTS_DIR/gitea-backup.sh" <(crontab -l 2>/dev/null) | awk '{print $1" "$2" "$3" "$4" "$5}')"
|
||||||
|
echo " Update : $(grep "$SCRIPTS_DIR/gitea-auto-update.sh" <(crontab -l 2>/dev/null) | awk '{print $1" "$2" "$3" "$4" "$5}')"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Proposer un premier backup
|
||||||
|
if [[ "$FORCE" != "true" ]]; then
|
||||||
|
read -p "💾 Lancer un premier backup maintenant ? [O/n] " -r REPLY
|
||||||
|
if [[ "$REPLY" =~ ^[OoYy]?$ ]]; then
|
||||||
|
echo ""
|
||||||
|
bash "$SCRIPTS_DIR/gitea-backup.sh"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🎉 Prêt ! Les scripts tourneront automatiquement selon le planning cron."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user