# Runbook — Procédure de Rollback en Production (LÉZIDÉJOU)

Ce document opérationnel détaille les procédures normatives de retour arrière en production pour le portail LÉZIDÉJOU hébergé sur OVHcloud.

---

## 1. Principes fondamentaux et invariants de production

1. **Releases immuables** : Le rollback ne modifie aucun fichier de la release cible et les releases sont traitées comme immuables par les scripts de déploiement/rollback. Un rollback n'extrait aucune archive et n'exécute aucune commande d'écriture dans la release.
2. **Découplage Code / Base de données** : La bascule du lien symbolique `current` restaure exclusivement le code applicatif et les assets. **Elle ne restaure en aucun cas la base de données**.
3. **Périmètre des garanties techniques** :
   * La bascule atomique via `mv -Tf current.new current` garantit qu'aucune requête entrante ne s'exécute sur un code partiellement déployé.
   * En cas d'échec de migration (`php artisan migrate --force`), le dossier de staging est supprimé et la release active `current` reste inchangée.
   * **Absence de garantie de "zéro-downtime" absolu** : En cas de migration incompatible avec le code antérieur, une interruption de service est inévitable pour restaurer les données (CAS B). C'est pourquoi toute future migration doit privilégier l'approche *expand / contract*.
4. **Interdiction de `migrate:rollback` automatique** : Ne jamais exécuter automatiquement `php artisan migrate:rollback` en production pour tenter de restaurer un schéma altéré. La restauration des données s'effectue exclusivement depuis un backup fiable MySQL réalisé avant l'opération.
5. **Priorité à l'approche Expand / Contract** : Pour toutes les évolutions futures, concevoir des migrations rétrocompatibles avec la version précédente du code afin que tout rollback applicatif puisse s'exécuter sans nécessiter de restauration de base de données.

---

## 2. Arborescence de référence sur le serveur OVH

```text
/homez.2034/lezidea/lezidejou/
├── releases/
│   ├── v1.0.0/                    # Release précédente (immuable)
│   └── v1.0.1/                    # Release défaillante
├── shared/
│   ├── .env                       # Secrets et variables de production (droits 600)
│   └── storage/                   # Persistance (droits 755)
├── current -> releases/v1.0.0      # Symlink actif
├── deploy-release.sh              # Script de déploiement
└── rollback-release.sh            # Script de rollback applicatif
```

---

## 3. Matrice de décision des incidents

Avant toute action, identifier impérativement la nature de la régression :

| Type d'incident | Impact Base de Données | Procédure à appliquer |
|---|---|---|
| **CAS A — Bug applicatif pur** (Erreur 500, régression CSS/JS, mauvaise configuration logicielle) | Schéma et données strictement compatibles avec la version précédente | **Procédure CAS A** (Rollback applicatif seul via `rollback-release.sh`) |
| **CAS B — Incompatibilité / Altération DB** (Migration destructive, colonne manquante pour l'ancienne release, données altérées) | Schéma ou données incompatibles avec l'ancienne version | **Procédure CAS B** (Mode maintenance + Restauration dump OVH + Rollback applicatif) |

---

## 4. Procédure CAS A : Rollback applicatif pur (DB compatible)

Cette procédure s'exécute rapidement par simple bascule de lien symbolique.

### Étape 1 : Connexion SSH au serveur de production
```bash
ssh -i ~/.ssh/lezidejou_deploy_ed25519 lezidea@ssh.cluster131.hosting.ovh.net
```

### Étape 2 : Exécution du script de rollback
Lancer le script en indiquant le tag de la release cible précédente vers laquelle revenir :
```bash
bash ~/lezidejou/rollback-release.sh v1.0.0 ~/lezidejou
```

Le script effectue automatiquement les opérations suivantes :
1. Validation stricte du format SemVer du tag (`^v[0-9]+\.[0-9]+\.[0-9]+$`) ;
2. Contrôle d'intégrité de la release cible (`vendor/autoload.php`, `public/index.php`) ;
3. Vérification que `.env`, `storage` et `public/storage` sont de vrais symlinks (`-L`) pointant vers leurs cibles canoniques sous `shared/` ;
4. Création du lien temporaire `current.new` ;
5. Bascule atomique instantanée `mv -Tf current.new current` ;
6. Confirmation de la release active (sans aucune écriture dans les fichiers de la release cible).

### Étape 3 : Contrôles de validation (Smoke tests)
Vérifier en navigation publique et via les logs de rotation quotidienne :
```bash
tail -n 50 $(ls -t ~/lezidejou/shared/storage/logs/laravel*.log 2>/dev/null | head -n 1)
curl -s -o /dev/null -w "%{http_code}\n" https://lezidejou.fr/
```

---

## 5. Procédure CAS B : Incident critique avec rupture DB (DB incompatible)

Lorsque la base de données a subi une migration incompatible avec la version précédente, l'ordre exact d'exécution est impératif pour éviter toute corruption de données ou affichage d'erreurs aux utilisateurs.

### Séquence chronologique obligatoire :

```text
[1. Mode Maintenance] ➔ [2. Restauration MySQL OVH] ➔ [3. Rollback Applicatif] ➔ [4. Smoke Tests] ➔ [5. Sortie Maintenance]
```

### Étape 1 : Activer immédiatement le mode maintenance avec un secret éphémère
Depuis la session SSH :
```bash
ssh -i ~/.ssh/lezidejou_deploy_ed25519 lezidea@ssh.cluster131.hosting.ovh.net

# Générer un secret aléatoire fort pour l'intervention (non versionné)
MAINTENANCE_SECRET=$(php -r "echo bin2hex(random_bytes(16));")
echo "Secret de bypass maintenance : ${MAINTENANCE_SECRET}"

# Activer le mode maintenance avec ce secret éphémère
php ~/lezidejou/current/artisan down --secret="${MAINTENANCE_SECRET}"
```
*(Le secret éphémère permet uniquement à l'administrateur d'accéder au site via `https://lezidejou.fr/${MAINTENANCE_SECRET}` pendant l'intervention pour exécuter les vérifications sans exposer les erreurs au public)*.

### Étape 2 : Restaurer la sauvegarde MySQL depuis l'Espace Client OVHcloud
1. Se connecter à l'espace client [OVHcloud Web Cloud](https://www.ovh.com/manager/) ;
2. Naviguer vers : **Web Cloud > Hébergements > lezidea > Bases de données** ;
3. Repérer la base `lezideaapp` ;
4. Cliquer sur les trois points `...` à droite de la base et sélectionner **Restaurer une sauvegarde** ;
5. Choisir la sauvegarde immédiate réalisée juste avant la mise en production ;
6. Confirmer et attendre la notification de fin de restauration d'OVHcloud.

### Étape 3 : Exécuter le rollback applicatif
Une fois la base de données restaurée et alignée avec l'ancien schéma :
```bash
bash ~/lezidejou/rollback-release.sh v1.0.0 ~/lezidejou
```

### Étape 4 : Effectuer les contrôles et smoke tests
- Tester les pages principales en accédant au portail avec le secret de bypass éphémère généré à l'étape 1 : `https://lezidejou.fr/<MAINTENANCE_SECRET>` ;
- Vérifier la connexion à la base et le journal des erreurs :
  ```bash
  tail -n 50 $(ls -t ~/lezidejou/shared/storage/logs/laravel*.log 2>/dev/null | head -n 1)
  ```

### Étape 5 : Désactiver le mode maintenance
Une fois la conformité du portail validée :
```bash
php ~/lezidejou/current/artisan up
```
L'application est à nouveau en ligne sur la release stable précédente.

---

## 6. Politique relative au stockage persistant (`storage/`)

* **Données éphémères (exclues de sauvegarde)** :
  * `shared/storage/framework/cache/`
  * `shared/storage/framework/sessions/`
  * `shared/storage/framework/views/`
  * `shared/storage/logs/`
* **Données métier persistantes** :
  * `shared/storage/app/` et `shared/storage/app/public/`
* **État en version v1** : Le portail LÉZIDÉJOU v1 est une vitrine éditoriale et un catalogue applicatif ne proposant pas de formulaires d'upload utilisateur. Tous les médias du catalogue sont versionnés dans `public/images/`. Aucune sauvegarde spécifique de `storage/app` n'est requise pour la v1.
* **Règle pour les versions futures** : Dès qu'un module applicatif génèrera ou collectera des fichiers persistants non reconstructibles dans `storage/app`, un script de synchronisation / sauvegarde chiffrée périodique de ce dossier devra être adjoint à la stratégie de sauvegarde globale.
