# Déploiement sur staging — procédure

**Depuis le 20/08, staging se déploie TOUT SEUL.** Ce document décrit d'abord
l'automatisme, puis la procédure manuelle — qui reste la voie de secours et
l'explication de ce que l'automatisme fait.

Écrite le 19/08/2026 après l'avoir reconstituée : elle vivait dans la tête du
pilote et une compaction de contexte l'a fait perdre. **Ce qui n'est pas écrit
ici n'existe pas.**

## L'automatisme (déploiement continu)

```
PR → CI (tests + lint) → merge dans development
   → la suite est REJOUÉE sur development
   → si VERTE : deploy-staging.yml déploie
   → contrôle de santé → retour arrière automatique si mauvais
```

Le déclencheur est la **réussite du workflow `tests` sur `development`**, jamais
le merge lui-même : **un merge rouge ne déploie rien.** L'automatisme s'ajoute à
la protection de branche ; il garantit qu'un rouge ne part pas en ligne même si
un contrôle non requis a été ignoré au merge.

> ⛔ **Erratum du 24/08** — la première version de ce paragraphe affirmait que la
> protection de branche était « indisponible sur le plan GitHub actuel » et que
> « rien n'empêche techniquement de merger une PR rouge ». **Mesuré faux le
> 24/08**, par lecture de la configuration (deux sessions indépendantes) : la
> protection est **active et identique sur les quatre branches** (BACK et FRONT,
> `main` et `development`) — contrôle requis `Suite — vert ou rouge (MySQL 8)`
> (BACK) / `tests` (FRONT), `enforce_admins: true`, force-push et suppression
> interdits. **Une PR dont le contrôle REQUIS est rouge ne peut pas fusionner.**
> Le « merge au contrôle rouge » du 23/08 (#621) portait un `Pint` rouge **non
> requis** ; le contrôle requis était vert. ⚠️ **Le trou réel est là : aucun
> linter n'est requis, sur aucune branche** — un `Pint` ou `ESLint` rouge
> fusionne (l'arbitrage rejoint #467). Réf. : charte §6 (« un réglage se LIT »),
> consignation du 23/08 §4.2 (correction `e8677bc`).

Fichiers : `.github/workflows/deploy-staging.yml` dans **chacun** des deux
dépôts. Secrets : `DEPLOY_SSH_KEY` (clé ed25519 DÉDIÉE, créée le 20/08 pour cet
usage — pas une clé personnelle réutilisée), `DEPLOY_HOST`, `DEPLOY_USER`.

**Déclenchement à la main** : onglet Actions → `deploy-staging` → *Run workflow*
(entrée `workflow_dispatch`), utile pour redéployer sans nouveau commit.

**Ce que l'automatisme ne fait PAS :**
- **aucune migration de base n'est jouée** — une migration change des données,
  elle reste une décision humaine, lancée à la main après lecture ;
- **il ne touche jamais la production** — la prod se déploie sur décision, avec
  approbation explicite ;
- **il ne prévient pas les testeurs** — voir la fin de ce document.

**En cas d'échec** : le contrôle de santé restaure l'état précédent tout seul
(front depuis la sauvegarde horodatée, back par `git reset --hard` sur le commit
d'avant) et l'exécution finit en rouge. Il faut alors lire le journal du
workflow avant de retenter — un retour arrière réussi n'est pas un problème
réglé.

Serveur : `ubuntu@57.129.1.122` (OVH). Deux applications distinctes, deux
procédures séparées — un correctif front ne se déploie PAS en déployant le back.

## Back (API Laravel)

Docroot : `/var/www/api-staging.efektiv-academie-dev.com/e-learning-api`
Le code est un clone git qui suit `development` : on **tire**, on ne pousse pas
de fichiers.

```
ssh ubuntu@57.129.1.122 "cd /var/www/api-staging.efektiv-academie-dev.com/e-learning-api \
  && git pull -q origin development \
  && sudo -u www-data php artisan optimize:clear >/dev/null \
  && git log --oneline -1 && echo 'deploy back OK'"
```

`optimize:clear` n'est pas optionnel : sans lui, les routes et la configuration
restent en cache et le correctif ne prend pas. Le message `PHP Warning: Module
"intl" is already loaded` est inoffensif et attendu.

## Front (React/Vite)

Docroot : `/var/www/staging.efektiv-academie-dev.com/` — **des fichiers
construits**, pas un clone git. Le build se fait en LOCAL, puis on envoie.

**Node 24 exactement** (le 25 et le 26 cassent des tests et le build) :
`export PATH="/opt/homebrew/opt/node@24/bin:$PATH"`.

```
cd ~/Projects/numedia/dev-agency/EFEKTIVACADEMIE-FRONT
git checkout development && git pull origin development
export PATH="/opt/homebrew/opt/node@24/bin:$PATH" && node -v   # doit dire v24.x
rm -rf dist && npm run build

rsync -az --delete --exclude=.htaccess dist/ ubuntu@57.129.1.122:/tmp/front-deploy-<ref>/

ssh ubuntu@57.129.1.122 "D=/var/www/staging.efektiv-academie-dev.com; \
  sudo cp -a \$D \${D}-backup-\$(date +%Y%m%d-%H%M) \
  && sudo rsync -a --delete --exclude=.htaccess /tmp/front-deploy-<ref>/ \$D/ \
  && sudo chown -R www-data:www-data \$D \
  && ls \$D/assets/ | grep -E '^index-' && echo 'deploy front OK'"
```

Trois points qui ne se devinent pas :
- **`--exclude=.htaccess` DEUX fois.** Le `.htaccess` du docroot appartient au
  serveur (réécriture d'URL + protection) et n'est PAS dans le dépôt : un
  `--delete` sans exclusion le supprime et casse le site entier.
- **Sauvegarde horodatée avant écrasement**, systématique (`…-backup-AAAAMMJJ-HHMM`).
  C'est le retour arrière ; les sauvegardes s'accumulent, à purger de temps en temps.
- **`chown www-data`** après copie, sinon Apache ne sert pas les fichiers.

## Vérification — obligatoire, le déploiement n'est pas fini sans elle

```
curl -s https://staging.efektiv-academie-dev.com/ | grep -oE 'index-[A-Za-z0-9_-]+\.(js|css)'
```

Le hash doit être celui du `dist/` qu'on vient de construire. Puis **prouver le
correctif lui-même** : chercher dans le bundle servi une chaîne que seul le
correctif introduit, ou rejouer l'appel d'API concerné avec un compte de recette.
Un « deploy OK » n'est pas une preuve — le hash servi et le comportement le sont.

## Ce que le déploiement ne fait PAS

Il ne prévient pas les testeurs. Un correctif déployé qui touche un écran testé
doit être inscrit au **journal des correctifs** de la feuille de suivi (mention
« À REJOUER »), sinon les testeurs continuent sur un cache périmé.
Voir `TRAITEMENT_DES_BUGS.md`.
