--name reprenait runner_labels : un runner et son label portaient le meme
eni-g3. Deux machines qui reprendraient cette racine s'enregistreraient sous
le meme nom, ce que GitHub refuse. Le nom vaut desormais celui de l'hote,
resolu sur la machine, et runner_nom permet de le forcer.
Point 2 de la revue de la PR #144.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Le seul Terraform du dépôt installait un cluster k3s que rien ne consomme, et
sa racine ne passait même pas `terraform init` : depuis Terraform 1.x, un
provisioner `destroy` impose que tout le bloc `connection` ne lise que `self`,
et celui du module k3s lisait `var.*`. Il est corrigé en batissant la connexion
sur `triggers`, où ne figurent que l'adresse, le port, l'utilisateur et le
chemin de la clé : jamais la clé ni un mot de passe.
`environments/vm-eni` provisionne la machine qui porte la recette et la
production : Docker et le plugin Compose, `scripts/provision-host.sh`, puis
l'enregistrement du runner. Rien n'y construit d'image ni ne lance de
conteneur, et un `apply` n'interrompt pas la stack qui tourne. Le déploiement
continu ne bouge pas : `deploy.yml` reste le seul chemin de livraison.
`environments/dev` devient `k3s-cible`, parce qu'il provisionnait un cluster et
pas un environnement applicatif, et que `rec` et `prod` vivent sur la même
machine. `environments/prod`, dossier vide, disparaît.
`infra.yml` formate et valide chaque racine à chaque push : c'est l'absence
d'un tel job qui a laissé ce Terraform cassé sans que rien ne le dise. La
boucle parcourt `environments/*`, une racine ajoutée est couverte sans y
toucher.
ADR 0010 pour la frontière entre provisionnement et livraison, et la doc
d'architecture alignée dessus.
Rapatrie dans dev les mises à jour de dépendances mergées sur main :
#142 (SonarQube ignoré pour Dependabot), #129 checkout v7, #130 setup-uv v7,
#132 setup-node v7, #128 upload-artifact v7, #131 download-artifact v8,
#127 Node 26 (image et CI frontend), #126 nginx 1.31-alpine, #133 pandas 3.0.6
et ruff 0.16.8, #134 Angular 22.1.7, jsdom 30, prettier 3.9.8 (TypeScript 6 et
vitest 4 conservés, majeures ignorées côté Dependabot).
Conflit README.md : statuts « En place » de main, « Quatre DAGs » de dev (#138).
Le tag de l'image y est cité deux fois (présentation, commande nginx -t) ;
il suit celui de docker-compose.prod.yml. Le runner du Dockerfile frontend
est une image durcie dhi.io, suivie séparément, et n'est pas concerné.
Un projet Compose par environnement sur la même machine : ports du proxy et origine
publique en variables dans docker-compose.prod.yml, réglage mémoire des deux bases
TimescaleDB et du webserver Airflow. Le workflow deploy.yml déploie dev en recette et
main en production depuis un runner installé sur la VM, jamais sur pull_request.
scripts/provision-host.sh prépare les deux dossiers, secrets et certificats compris,
sans rien démarrer.
L'image frontend quitte dhi.io/nginx, registre authentifié dont personne n'a l'accès,
pour nginx:1.28-alpine : elle n'avait jamais été construite.
ADR 0009, vues infra et CI/CD, README et .env.example mis à jour.
Refs #21, #22
Compose interpole tout le fichier avant n'importe quelle sous-commande : la garde
`${PUBLIC_HOST:?}` de l'overlay cassait `stack-down` et `stack-logs` autant que le
démarrage. La valeur retombe sur `enervision.local`, et `stack-up` vérifie à la place
que le certificat présent couvre l'hôte demandé, ce qui est la condition réelle à tenir.
La CSP `script-src 'self'` bloquait le gestionnaire `onload` que l'inlining du CSS
critique d'Angular pose sur la feuille de styles : l'application se serait affichée sans
style derrière le proxy. `inlineCritical` passe à faux, le build de production ne produit
plus aucun script en ligne.
La zone de limitation resserrée ne couvre plus que les routes qui vérifient un secret.
Derrière le NAT de l'école, où une seule adresse porte toute la promotion, `/auth/me` et
`/auth/refresh` y auraient produit des 429 en usage normal.
Enfin `certbot/certbot` est épinglé en v5.8.0 pour que Dependabot puisse le suivre, le
proxy attend une API saine plutôt que démarrée, et la redirection vers `$host` est actée
comme risque accepté : figer un nom canonique couperait l'accès par adresse IP, seule
voie ouverte sur la machine cible.
Rapatrie la #118 (DAGs Airflow). Quatre conflits, tous additifs sauf un :
- `.env.example` et `.gitignore` : les blocs Airflow et proxy cohabitent.
- `Makefile` : `AIRFLOW` rejoint les variables de dossier, les cibles Airflow
et TLS cohabitent dans `.PHONY`.
- `00-vue-ensemble.md` : la ligne ML de `dev` est retenue, la ligne Infra de
cette branche aussi, chacune portant sa propre mise à jour.
L'interface Airflow rejoint la base et Mailpit sur `127.0.0.1` dans l'overlay :
elle n'a pas d'authentification à publier derrière le proxy.
Le SPA appelle /api/v1 en relatif et rien ne routait cet appel vers l'API
une fois en conteneur. Le cookie de rafraîchissement prend le préfixe
__Secure- dès que APP_ENV sort de local, donc sans HTTPS il n'était jamais
posé et l'authentification ne survivait pas à un rechargement de page.
Un service proxy, image officielle nginx dont la configuration est montée en
volume, devient le seul composant publié : 80 redirige vers 443 et sert le
défi ACME, 443 termine le TLS, sert le SPA sur / et l'API sur /api/ sous la
même origine, pose HSTS et CSP que l'application refuse délibérément de
poser, et ajoute une limitation de débit au frontal. Backend et frontend ne
sont plus publiés, la base et l'interface Mailpit sont ramenées sur la
boucle locale.
nginx lit toujours les deux mêmes fichiers de certificat : seule leur
fabrication varie, script openssl pour la démonstration, deploy-hook certbot
le jour où un domaine public existera. Le chemin ACME est livré et
documenté, pas exercé : sur une IP privée le défi HTTP-01 ne peut pas
aboutir.
Pose les dossiers des sept domaines de la stack (backend, frontend, base,
ETL, infra, CI/CD, monitoring) avec un README de cadrage par domaine.
Seul apps/backend est initialise, les autres font l'objet d'un ticket dedie.
Le squelette Angular n'est pas versionne a la main : apps/frontend porte la
commande ng new a lancer.