Versioning, déploiement et mise à jour automatique

Gestion des versions :
- Fichier VERSION (1.0.0) comme source de vérité
- config/update.php : URL Gitea, AUTO_UPDATE (false par défaut), rétention des sauvegardes

Artisan commands :
- app:check-update  : interroge l'API Gitea, cache Redis 1h, déclenche app:update si AUTO_UPDATE=true
- app:update        : télécharge l'archive, sauvegarde pg_dump, rsync, composer install, migrate, reload php-fpm
- app:rollback      : liste les sauvegardes et restaure via psql

UpdateService :
- Téléchargement via Http::sink() (streaming, pas de charge mémoire)
- Sauvegarde pg_dump dans storage/app/backups/ avant chaque mise à jour
- Rechargement php-fpm gracieux (kill -USR2 1) sans downtime
- Purge automatique des anciennes sauvegardes (configurable)

Docker (refactor pour volume-mount) :
- Dockerfile : runtime seulement (PHP + extensions + composer + rsync + pg_client)
  Le code n'est plus copié dans l'image → les mises à jour ne nécessitent pas de rebuild
- entrypoint.sh : composer install + key:generate + caches au démarrage du container
- docker-compose.prod.yml : montage du code comme volume (.:/var/www/html)

Scripts de déploiement :
- bin/build-release.sh : rsync + tar.gz + sha256, exclut vendor/node_modules/tests
- install.sh : guide d'installation Docker complète (première mise en service)

Interface admin :
- Bandeau "mise à jour disponible" dans le dashboard admin (version courante + cible)
- Badge version + icône "à jour" en pied de tableau de bord
- Commande à copier-coller pour appliquer depuis le container

Planification :
- routes/console.php : Schedule::command('app:check-update')->daily()
- .env.example : variables GITEA_*, AUTO_UPDATE, UPDATE_BACKUPS_TO_KEEP

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 17:38:39 +02:00
parent c790691200
commit ba7fe10329
15 changed files with 756 additions and 23 deletions
Executable
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# MesRelevés — Installation initiale (déploiement Docker)
#
# Usage : ./install.sh
#
# Prérequis : Docker + Docker Compose v2
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
COMPOSE="docker compose -f docker-compose.prod.yml"
echo "────────────────────────────────────────────"
echo " MesRelevés — Installation"
echo "────────────────────────────────────────────"
# ── Prérequis ─────────────────────────────────────────────────────────────────
command -v docker >/dev/null 2>&1 || { echo "Erreur : Docker n'est pas installé."; exit 1; }
docker compose version >/dev/null 2>&1 || { echo "Erreur : Docker Compose v2 requis."; exit 1; }
# ── Fichier .env ──────────────────────────────────────────────────────────────
if [ ! -f .env ]; then
cp .env.example .env
echo ""
echo "→ Fichier .env créé depuis .env.example."
echo " Éditez .env avec vos paramètres (DB_PASSWORD, MAIL_*, APP_URL, etc.)"
echo " puis relancez ce script."
echo ""
exit 0
fi
# Vérification DB_PASSWORD obligatoire
if grep -qE "^DB_PASSWORD=$" .env; then
echo "Erreur : DB_PASSWORD n'est pas défini dans .env."
exit 1
fi
# ── Structure storage ─────────────────────────────────────────────────────────
mkdir -p storage/{app/backups,framework/{cache,sessions,views},logs}
mkdir -p bootstrap/cache
# ── Démarrage des services ────────────────────────────────────────────────────
echo "→ Construction de l'image Docker..."
$COMPOSE build
echo "→ Démarrage des services (db, redis, app, nginx)..."
$COMPOSE up -d
echo "→ Attente de la base de données..."
until $COMPOSE exec -T db pg_isready -U "${DB_USERNAME:-mesreleves}" >/dev/null 2>&1; do
sleep 2
done
echo " Base de données prête."
# Attendre que l'entrypoint termine (composer install, caches)
sleep 8
# ── Migrations ────────────────────────────────────────────────────────────────
echo "→ Migration de la base de données..."
$COMPOSE exec app php artisan migrate --force
# ── Lien storage ──────────────────────────────────────────────────────────────
$COMPOSE exec app php artisan storage:link 2>/dev/null || true
# ── Tâche planifiée (cron sur l'hôte) ────────────────────────────────────────
CRON_CMD="* * * * * cd $(pwd) && docker compose -f docker-compose.prod.yml exec -T app php artisan schedule:run >> /dev/null 2>&1"
echo ""
echo "→ Pour activer les tâches planifiées (vérification automatique des mises à jour),"
echo " ajoutez cette ligne à votre crontab (crontab -e) :"
echo ""
echo " ${CRON_CMD}"
echo ""
# ── Résumé ────────────────────────────────────────────────────────────────────
VERSION=$(cat VERSION 2>/dev/null || echo "?")
echo "────────────────────────────────────────────"
echo "✓ MesRelevés v${VERSION} installé avec succès !"
echo ""
echo " URL : $(grep APP_URL .env | cut -d= -f2)"
echo ""
echo "Commandes utiles :"
echo " Voir les logs : docker compose -f docker-compose.prod.yml logs -f app"
echo " Vérifier updates : docker compose -f docker-compose.prod.yml exec app php artisan app:check-update"
echo " Mettre à jour : docker compose -f docker-compose.prod.yml exec app php artisan app:update"
echo "────────────────────────────────────────────"