Commit Graph
3 Commits
Author SHA1 Message Date
Claude b184109e23 Réparer l'intégration continue, et poser des ports internes non communs
**Le test des heures ne pouvait que tomber.** Il interrogeait le mois
précédent, alors que le seed ne pose que deux semaines : la courante et la
précédente. Hors des premiers jours d'un mois, le mois précédent est donc
vide. Mesuré sur une base semée à neuf : un seul mois porte des créneaux,
le mois courant. Le défaut ne se voyait pas en développement, où la base
garde les créneaux des exécutions antérieures — 39 créneaux de juillet
survivaient chez moi à des semis d'il y a plusieurs semaines.

**Le seed ne remettait pas l'état de publication.** Son `update` était vide,
si bien qu'une semaine déjà semée gardait le statut qu'elle avait alors : la
semaine précédente, publiée par définition, restait en brouillon dès qu'elle
avait été semée du temps où elle était la semaine courante. D'où des tests
qui échouent en local et passent en intégration continue — l'écart le plus
coûteux à diagnostiquer. Le statut est désormais réimposé.

**Ports internes.** L'application écoute sur 9317 et la base sur 5439,
jusque dans l'image. Sur un réseau Docker deux conteneurs peuvent écouter le
même port sans se gêner — ce n'est donc pas une correction de collision —
mais une valeur unique de bout en bout lève l'ambiguïté quand plusieurs
piles cohabitent derrière le même proxy, et la configuration du
reverse-proxy porte partout le même nombre. Publication, sonde de santé,
serveur, chaîne de connexion et scripts d'amorçage sont alignés sur une
seule variable par service.

`pnpm verify` : 450 tests. Playwright : 77/77 après remise à zéro du semis.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
2026-08-10 08:10:35 +00:00
Claude 90e069f66a Nommer le vrai obstacle : le mot de passe gravé dans le volume
Le diagnostic disait « mot de passe d'amorçage erroné » sans dire pourquoi
cela arrive, et on cherchait du côté du rôle applicatif — qui n'y est pour
rien.

`POSTGRES_PASSWORD` n'est lu qu'à l'initialisation du volume de la base. Un
volume créé lors d'un déploiement antérieur, fût-il un déploiement qui avait
échoué pour une autre raison, garde le mot de passe d'alors ; le changer
dans la pile n'y touche pas, le volume ne se réinitialise jamais. Le compte
d'amorçage est alors refusé, l'application ne peut plus réparer le rôle
applicatif, et le seul symptôme visible est un P1000 qui désigne le mauvais
coupable.

Le message nomme désormais ce cas et donne les deux issues : repartir d'un
volume neuf quand la base est vide, ou réaligner le mot de passe par la
socket locale — que l'image PostgreSQL accepte sans l'ancien.

Éprouvé sur la panne réelle, sous authentification par mot de passe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
2026-08-10 06:25:45 +00:00
Claude 49618c57bf Faire poser le rôle applicatif par l'application elle-même
`db-init` suppose un orchestrateur qui honore
`depends_on: service_completed_successfully`. Swarm l'ignore, et une pile
déployée avant l'ajout du service ne le contient même pas. L'application
redémarrait alors en boucle sur un refus d'authentification que le
diagnostic ajouté précédemment décrivait sans que personne puisse le
corriger.

Elle le corrige donc elle-même au démarrage, si on lui confie les
identifiants d'amorçage — et se tait sinon, pour ne pas contrarier un
déploiement qui préfère les garder hors du conteneur applicatif. Le point
d'entrée retire ces variables avant de lancer le serveur : le processus qui
sert les requêtes ne les voit jamais.

Le branchement create/alter se fait côté client et non dans un bloc `DO` :
le corps d'un `DO` est une chaîne littérale, où `$1` n'est pas un paramètre
de requête. L'échappement du mot de passe est confié à `quote_literal`, et
le nom de rôle est refusé s'il n'a pas la forme d'un identifiant.

Éprouvé sous authentification scram réelle : après réalignement, l'ancien
mot de passe est refusé et le nouveau accepté, le rôle reste NOSUPERUSER
NOBYPASSRLS et devient propriétaire de la base.

Au passage, `withTenant` pose un budget de transaction explicite. Tout accès
aux données passe par lui, si bien que le défaut Prisma de 5 s plafonnait en
réalité chaque requête de l'application, et l'échec se présentait en `P2028`
qui ne désigne ni la requête ni la cause.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
2026-08-09 18:32:38 +00:00