#!/bin/bash # ============================================================================= # gitea-auto-update.sh — Vérifie et applique les mises à jour Gitea Docker # ============================================================================= # À placer dans /usr/local/bin/ ou ~/scripts/ # Cron recommandé : 0 4 * * * /usr/local/bin/gitea-auto-update.sh # # Paramètres configurables (modifier ci-dessous ou passer en variables d'env) # ============================================================================= set -euo pipefail # ─── Configuration (modifiable) ───────────────────────────────────────────── COMPOSE_DIR="${COMPOSE_DIR:-$HOME/dev/git/outils/docker/gitea}" COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}" CONTAINER_NAME="${CONTAINER_NAME:-gitea_server}" IMAGE_NAME="${IMAGE_NAME:-gitea/gitea}" IMAGE_TAG="${IMAGE_TAG:-latest}" # "latest" ou version spécifique LOG_FILE="${LOG_FILE:-/var/log/gitea-update.log}" ENABLE_NOTIFICATIONS="${ENABLE_NOTIFICATIONS:-false}" # true pour envoyer un résumé NOTIFY_METHOD="${NOTIFY_METHOD:-log}" # log, telegram, ntfy TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID:-}" NTPY_TOPIC="${NTPY_TOPIC:-}" HEALTH_CHECK_URL="${HEALTH_CHECK_URL:-http://localhost:3000}" HEALTH_CHECK_TIMEOUT="${HEALTH_CHECK_TIMEOUT:-30}" DB_CONTAINER="${DB_CONTAINER:-gitea_db}" DB_NAME="${DB_NAME:-gitea}" DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-gitea}" # ─── Logging ───────────────────────────────────────────────────────────────── log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } notify() { local msg="$1" case "$NOTIFY_METHOD" in telegram) [[ -n "$TELEGRAM_CHAT_ID" ]] && 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 ;; ntfy) [[ -n "$NTPY_TOPIC" ]] && curl -s -H "Title: Gitea Update" -d "$msg" "https://ntfy.sh/$NTPY_TOPIC" > /dev/null 2>&1 || true ;; esac } log "════════════════════════════════════════════════════════" log "🔄 Début vérification mise à jour Gitea" # ─── Pré-vérifications ────────────────────────────────────────────────────── cd "$COMPOSE_DIR" || { log "❌ Répertoire $COMPOSE_DIR introuvable"; exit 1; } if ! command -v docker &>/dev/null; then log "❌ Docker n'est pas installé" exit 1 fi # ─── Récupérer l'image digest actuelle ────────────────────────────────────── OLD_DIGEST=$(docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" --format '{{.RepoDigests}}' 2>/dev/null | grep -oP 'sha256:[a-f0-9]+' || echo "") log "📦 Image actuelle : ${IMAGE_NAME}:${IMAGE_TAG}" log "🔍 Digest actuel : ${OLD_DIGEST:-non disponible}" # ─── Pull de la nouvelle image ────────────────────────────────────────────── log "📥 Pull de la nouvelle image..." PULL_OUTPUT=$(docker compose -f "$COMPOSE_FILE" pull "$CONTAINER_NAME" 2>&1) PULL_EXIT=$? if [[ $PULL_EXIT -ne 0 ]]; then log "❌ Échec du pull : $PULL_OUTPUT" exit 1 fi NEW_DIGEST=$(docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" --format '{{.RepoDigests}}' 2>/dev/null | grep -oP 'sha256:[a-f0-9]+' || echo "") log "🔍 Nouveau digest : ${NEW_DIGEST:-non disponible}" # ─── Comparer les digests ─────────────────────────────────────────────────── if [[ -n "$OLD_DIGEST" ]] && [[ -n "$NEW_DIGEST" ]] && [[ "$OLD_DIGEST" == "$NEW_DIGEST" ]]; then log "✅ Aucune mise à jour disponible (digest identique)" log "═══ Fin ───────────────────────────────────────────" exit 0 fi # ─── MISE À JOUR NÉCESSAIRE ───────────────────────────────────────────────── 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) BACKUP_PRE_DIR="${BACKUP_PRE_DIR:-/tmp/gitea-pre-update-$(date +%Y%m%d-%H%M%S)}" mkdir -p "$BACKUP_PRE_DIR" log "💾 Backup pré-update vers $BACKUP_PRE_DIR" docker exec "$DB_CONTAINER" mysqldump -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" > "$BACKUP_PRE_DIR/gitea_db.sql" 2>/dev/null || { log "⚠️ Backup pré-update DB échoué, on continue quand même" } # Recréer le 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_EXIT=$? if [[ $UP_EXIT -ne 0 ]]; then log "❌ Échec du redémarrage : $UP_OUTPUT" log "⚠️ Tentative de rollback..." docker compose -f "$COMPOSE_FILE" up -d "$CONTAINER_NAME" 2>&1 || true notify "❌ GITEA UPDATE FAILED sur $(hostname) — rollback effectué" exit 1 fi # ─── Vérification santé ───────────────────────────────────────────────────── log "🏥 Vérification santé (timeout=${HEALTH_CHECK_TIMEOUT}s)..." 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") if [[ "$STATUS" == "200" || "$STATUS" == "302" || "$STATUS" == "303" ]]; then log "✅ Gitea répond (HTTP $STATUS) après ${i}s" break fi if [[ $i -eq "$HEALTH_CHECK_TIMEOUT" ]]; then log "❌ Gitea ne répond pas après ${HEALTH_CHECK_TIMEOUT}s" notify "⚠️ GITEA UPDATE : service lent à démarrer sur $(hostname) — vérifier" exit 1 fi sleep 1 done # ─── Rapport final ────────────────────────────────────────────────────────── GITEA_VERSION=$(curl -s "$HEALTH_CHECK_URL/api/v1/version" 2>/dev/null | grep -oP '"version":"\K[^"]+' || echo "inconnue") log "✅ Mise à jour réussie !" log "📋 Nouvelle version : $GITEA_VERSION" log "📋 Ancien digest : ${OLD_DIGEST:-N/A}" log "📋 Nouveau digest : ${NEW_DIGEST:-N/A}" log "═══ Fin ───────────────────────────────────────────" notify "✅ Gitea mis à jour vers $GITEA_VERSION sur $(hostname)" # Nettoyage vieilles images docker image prune -f 2>/dev/null || true exit 0