feat(deploy): ajoute un environnement dev déployé à la demande
Infra / Formatage et validation Terraform (push) Successful in 50s

Troisième projet Compose sur la VM ENI, /srv/enervision/dev, alimenté par
workflow_dispatch de n'importe quelle branche autre que dev et main
(https://dev.enervision.local:9443). La recette suit toujours dev, la
production main.

- deploy.yml : routage main -> prod, dev -> rec, autre -> dev ; groupe de
  concurrence par environnement et non plus par branche.
- provision-host.sh : prépare le dossier dev (ports 9443, 5435, 8027, 8084) ;
  passe safe.directory à git, faute de quoi un second passage en root, celui
  de terraform apply, échoue sur les clones déjà remis au runner.
- ADR 0017, 10-infra.md, 50-cicd.md, infra/README.md à jour.
This commit is contained in:
Johan LEROY
2026-09-23 10:43:48 +02:00
parent c7744483b4
commit 10cc408b03
8 changed files with 109 additions and 39 deletions
@@ -0,0 +1,56 @@
# 0017 - Un troisième environnement, `dev`, déployé à la demande depuis n'importe quelle branche
- Statut : accepté
- Date : 2026-09-23
## Contexte
L'[ADR 0009](0009-deux-environnements-compose-sur-la-vm-eni.md) a posé deux environnements sur
la VM ENI : la recette suit `dev`, la production suit `main`. Les environnements GitHub en
comptent trois, `dev`, `rec` et `prod`, et le troisième ne déployait rien.
Il manque un endroit où montrer une branche de travail avant son merge : la recette ne doit
porter que ce qui est intégré à `dev`, sinon elle cesse d'être une recette. Un
`workflow_dispatch` sur une branche de travail envoyait d'ailleurs cette branche dans la
recette, puisque tout ce qui n'était pas `main` y partait.
La VM est passée à 32 Go : une troisième TimescaleDB, réglée à 2 Go comme les deux autres,
tient sans peine.
## Décision
**Un troisième projet Compose, `enervision-dev`, dans `/srv/enervision/dev`**, bâti exactement
comme les deux autres : son clone, son `.env`, son certificat, préparés par
`scripts/provision-host.sh`.
**Déployé à la demande, jamais sur un push.** `deploy.yml` envoie `main` en prod, `dev` en
recette, et toute autre branche lancée depuis l'onglet Actions dans `dev`. Seul un membre ayant
le droit d'écriture sur le dépôt peut lancer un workflow.
**Ports décalés d'un cran de plus** : HTTPS `9443`, et sur `127.0.0.1` la redirection HTTP
`8083`, PostgreSQL `5435`, Mailpit `8027`, Airflow `8084`. Nom d'hôte `dev.enervision.local`,
pour la même raison de cookie que la recette.
**Le groupe de concurrence suit l'environnement**, et non plus la branche : deux branches lancées
coup sur coup écriraient sinon dans le même dossier en même temps.
## Alternatives écartées
- **`dev` suit la branche `dev` à chaque push, la recette devient manuelle** : la recette
offrirait une version figée au jury, mais la doc CI/CD, l'ADR 0009 et l'habitude de l'équipe
basculeraient à deux jours du rendu.
- **Un environnement par branche de travail** : un projet Compose et une TimescaleDB par
branche, sans mécanisme de nettoyage. La machine ne le porterait pas longtemps.
- **Garder `dev` sur les postes seulement** : rien à montrer d'une branche non mergée sans
passer par la recette.
## Conséquences
- Une branche de travail créée avant ce changement porte l'ancien `deploy.yml` : lancée à la
main, elle part encore dans la recette. Limiter l'environnement GitHub `rec` à la branche
`dev` ferme ce chemin, réglage que seul un administrateur du dépôt peut poser.
- `dev` ne garde aucune donnée d'une branche à l'autre au-delà de ce que ses migrations
acceptent : une branche dont les migrations divergent de `dev` peut laisser la base dans un
état que la suivante refuse. Recréer le volume, `docker compose down -v`, est alors le remède.
- Trois environnements construisent leurs images séparément : l'écart de l'ADR 0009, un même
commit construit deux fois, reste ouvert jusqu'au passage à GHCR.
+18 -17
View File
@@ -227,26 +227,27 @@ Deux conséquences se propagent jusqu'à l'application, et elles ne se devinent
- `APP_TRUST_PROXY_HEADERS` passe à vrai en même temps, sinon la limitation de débit par IP
compte sur l'IP du proxy et devient globale.
### Deux environnements sur la même machine
### Trois environnements sur la même machine
Statut : `En cours`, la machine n'étant pas encore provisionnée. Décision et motifs dans
l'[ADR 0009](../adr/0009-deux-environnements-compose-sur-la-vm-eni.md).
La VM `eadl-2025-nantes-g3` portera la recette et la production, chacune dans son clone du dépôt,
son `.env` et son projet Compose. Le nom de projet préfixe volumes, réseau et conteneurs : rien
n'est partagé. `scripts/provision-host.sh` prépare les deux dossiers, génère les secrets et les
certificats, et ne démarre rien.
Statut : `En cours`. Décision et motifs dans
l'[ADR 0009](../adr/0009-deux-environnements-compose-sur-la-vm-eni.md), étendue à un troisième
environnement par l'[ADR 0017](../adr/0017-environnement-dev-a-la-demande.md).
La VM `eadl-2025-nantes-g3` porte le développement, la recette et la production, chacun dans son
clone du dépôt, son `.env` et son projet Compose. Le nom de projet préfixe volumes, réseau et
conteneurs : rien n'est partagé. `scripts/provision-host.sh` prépare les trois dossiers, génère
les secrets et les certificats, et ne démarre rien.
| | Recette | Production |
|---|---|---|
| Branche, environnement GitHub | `dev`, `rec` | `main`, `prod` |
| Dossier, projet Compose | `/srv/enervision/rec`, `enervision-rec` | `/srv/enervision/prod`, `enervision-prod` |
| URL | `https://rec.enervision.local:8443` | `https://enervision.local` |
| Proxy HTTP, HTTPS | `127.0.0.1:8081`, `8443` | `80`, `443` |
| PostgreSQL, Mailpit, Airflow, sur `127.0.0.1` | `5434`, `8026`, `8082` | `5433`, `8025`, `8080` |
| | Développement | Recette | Production |
|---|---|---|---|
| Branche, environnement GitHub | toute branche lancée à la main, `dev` | `dev`, `rec` | `main`, `prod` |
| Dossier, projet Compose | `/srv/enervision/dev`, `enervision-dev` | `/srv/enervision/rec`, `enervision-rec` | `/srv/enervision/prod`, `enervision-prod` |
| URL | `https://dev.enervision.local:9443` | `https://rec.enervision.local:8443` | `https://enervision.local` |
| Proxy HTTP, HTTPS | `127.0.0.1:8083`, `9443` | `127.0.0.1:8081`, `8443` | `80`, `443` |
| PostgreSQL, Mailpit, Airflow, sur `127.0.0.1` | `5435`, `8027`, `8084` | `5434`, `8026`, `8082` | `5433`, `8025`, `8080` |
Les deux noms d'hôte visent la même IP, à déclarer dans le `/etc/hosts` des postes. Deux noms
Les trois noms d'hôte visent la même IP, à déclarer dans le `/etc/hosts` des postes. Deux noms
distincts sont nécessaires : le cookie `__Secure-ev_refresh` est posé par hôte, pas par port.
La redirection HTTP de la recette est ramenée sur la boucle locale parce que la configuration
La redirection HTTP de la recette et du développement est ramenée sur la boucle locale parce que la configuration
Nginx renvoie vers `https://$host` sans port, c'est-à-dire vers la production.
Le déploiement est décrit dans [50-cicd.md](50-cicd.md) : un runner GitHub Actions installé sur
@@ -267,7 +268,7 @@ sequenceDiagram
TF->>VM: SSH, get.docker.com puis docker compose version
TF->>VM: copie et exécute scripts/provision-host.sh
VM->>VM: deux clones, deux .env, deux certificats
VM->>VM: trois clones, trois .env, trois certificats
TF->>VM: installe actions-runner, config.sh, svc.sh
VM->>GH: le runner s'enregistre avec le label eni-g3
```
+3 -2
View File
@@ -131,13 +131,14 @@ entrant n'est ouvert.
|---|---|---|---|
| `push` sur `dev` | `rec` | `/srv/enervision/rec` | aucune : la recette suit `dev` |
| `push` sur `main` | `prod` | `/srv/enervision/prod` | approbation d'un relecteur dans l'environnement `prod`, branche `main` seule autorisée |
| `workflow_dispatch` sur toute autre branche | `dev` | `/srv/enervision/dev` | droit d'écriture sur le dépôt, seul à pouvoir lancer un workflow ([ADR 0017](../adr/0017-environnement-dev-a-la-demande.md)) |
Le job aligne le clone sur la branche (`fetch`, `checkout`, `reset --hard`), lance
`make stack-up`, qui reconstruit les images, redémarre les conteneurs puis applique les
migrations Alembic dans le conteneur backend, et attend jusqu'à trois minutes que
`/api/v1/health/ready` réponde derrière le proxy. Cette sonde ne vérifie que la connexion à la
base et la présence de TimescaleDB : sans la migration, le déploiement serait vert sur une base
sans schéma, et c'est pourquoi `make stack-up` la porte. Un groupe de concurrence par branche,
sans schéma, et c'est pourquoi `make stack-up` la porte. Un groupe de concurrence par environnement,
sans annulation, empêche deux déploiements simultanés du même environnement.
Le job ne fait pas de `actions/checkout` dans son espace de travail, et c'est voulu : le dossier
@@ -157,7 +158,7 @@ passé à `scripts/provision-host.sh` fixe ce propriétaire.
La machine se prépare avec `scripts/provision-host.sh`, qui vérifie Docker et Compose 2.24.4 ou
plus, clone les deux branches, génère les secrets de chaque `.env` et les certificats
auto-signés, et ne démarre rien. Le détail des deux environnements, ports et noms d'hôte, est
auto-signés, et ne démarre rien. Le détail des trois environnements, ports et noms d'hôte, est
dans [10-infra.md](10-infra.md).
## Ce qui bloque un merge