CI : plus d'e2e en CI, contract attend check (D-064) #43

Merged
nicolas-fryder merged 18 commits from nicolas-fryder/dev into main 2026-10-15 12:51:36 +00:00 AGit

Retire le job e2e du ci.yml : l'étage e2e regarde l'image derrière le proxy, que les jobs sans Docker ne peuvent pas monter (D-064, décision du 2026-10-14). contract attend check. Contient aussi « Socle 0.17.1 », resté sur dev. Première CI du gabarit depuis l'activation d'Actions. 🤖 Generated with Claude Code

Retire le job e2e du ci.yml : l'étage e2e regarde l'image derrière le proxy, que les jobs sans Docker ne peuvent pas monter (D-064, décision du 2026-10-14). contract attend check. Contient aussi « Socle 0.17.1 », resté sur dev. Première CI du gabarit depuis l'activation d'Actions. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Décision du 2026-10-14 (eqs-demo, premier passage réel de la CI) : l'étage e2e
regarde l'image derrière le proxy, que les jobs sans Docker (D-015) ne peuvent
pas monter. Il se joue en local, à la main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
De 0.17.1 à 0.23.0 en une fois. Ce que la montée demande, et rien de plus :

- `.env.example` déclare `TRUSTED_PROXIES` (0.21, #137 du socle) :
  `config.env-example` la réclamait, bloquant.
- La suite de conformité fournit `jetons.cleCompteSuspendu` — une clé
  valide dont le propriétaire est `banned` — au nouveau contrôle
  `auth.cle-compte-suspendu` (0.21, #138), qui restait « annoncé et jamais
  joué ». La variante suit, et importe `registerEqs` de
  `@eqs/platform-next/register` (#142).
- Baselines rejouées (`eqs baseline`), lockfile rafraîchi.
- README : la section « état » est re-datée (0.23.0, 2026-10-15) ; la
  tâche `knip` tourne ; la réserve « ^0.3.0 / 0.4.0 » de la variante n'était
  plus vraie depuis longtemps.

Porte verte, intégration 17/17. Variante posée sur un clone : check vert,
intégration 17/17.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`next@16.3.7` portait GHSA-cjq9-62q9-8jv4 (HIGH, SSRF de l'optimisation
d'images) et cinq avis de moindre gravité, corrigés en 16.3.8. Le
pre-commit le refusait dès qu'un manifeste bougeait — c'est-à-dire dès qu'il
a recommencé à tourner.

16.4.0 et pas 16.4.1 : la seconde a moins d'un jour, et pnpm 11 ne
l'installe qu'en l'ajoutant à `minimumReleaseAgeExclude` — une protection
qu'on ne lève pas pour un correctif déjà présent dans la précédente.
La variante suit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Les trois fichiers de `.githooks` étaient committés en 100644 : git ignore
un hook sans bit `x` (un `hint:` que personne ne lit), et la porte
bloquante — gitleaks au commit, `pnpm check` + gitleaks au push — ne jouait
dans AUCUN clone. `eqs inspect` annonçait pourtant `hooks.preCommit: true`,
parce qu'il regarde si le fichier existe. Découvert en créant le hub
(EQS/hub ea57baa).

- `.githooks/*` passent en 100755.
- `scripts/verifier-hooks.mjs` lit le mode dans l'index (`git ls-files -s`,
  donc juste aussi sous Windows) et, hors Windows, le bit sur le disque. Il
  est déclaré en tâche `hooks` de `pnpm check` (`eqs.check.taches`) et
  s'appelle seul par `pnpm hooks`. Éprouvé dans les deux sens : rouge sur
  les modes d'origine, vert après.

Le premier commit joué par le hook réveillé a aussitôt refusé un avis HIGH
sur `next` (commit précédent) : la porte n'avait rien vu depuis le début.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Dans un worktree, `.git` est un fichier qui pointe vers
`<dépôt principal>/.git/worktrees/<nom>` par un chemin absolu de l'hôte. Le
repli docker de `_gitleaks.sh` ne montait que la copie de travail : git
répondait « not a git repository » dans le conteneur, gitleaks scannait
0 octet et concluait « no leaks found », code 0. Le pre-commit et le
pre-push passaient sans avoir rien regardé.

Le conteneur reçoit maintenant le répertoire git COMMUN, en lecture seule,
et `GIT_DIR`/`GIT_WORK_TREE` désignent le git-dir : une seule forme pour un
clone ordinaire, un worktree ou un sous-module. Un git-dir hors du
répertoire commun est refusé en le disant, plutôt que scanné à vide.

Éprouvé sur un clone et un worktree jetables, une clé AWS indexée : avant,
le worktree rendait « no leaks found » (0 octet) ; après, « leaks found: 1 »
et le commit est refusé, dans les deux. Les commits poussés (`--log-opts`)
sont lus aussi. Ce commit-ci est passé par le hook corrigé, depuis un
worktree.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`globsTests` de la suite de conformité ne listait que les motifs de l'API :
dès qu'`apps/web` a un test, `testing.fichier-orphelin` le déclare jamais
exécuté et l'intégration échoue (EQS/hub e009d97). Il gagne
`src/**/*.test.ts` et `src/**/*.test.tsx`.

Et le contrôle avait raison à moitié : `apps/web` n'avait pas de
configuration Vitest, et `eqs-check` ne crée `unit:web` que pour un projet
qui en a une. Un test du front tournait sous `pnpm --filter web test`, et
JAMAIS dans `pnpm check` ni au pre-push. `apps/web/vitest.config.mts` pose
le même motif, `passWithNoTests` pour le gabarit qui n'a aucun test.

Éprouvé avec un `apps/web/src/essai.test.ts` jetable : avant,
`[ecart] testing.fichier-orphelin` et 16/17 ; après, 17/17, et `unit:web`
le joue (1 test) — puis passe à vide une fois le fichier retiré.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pour une branche qui n'existe pas encore sur le distant, la plage passée à
gitleaks était `<sha> --not --all`. `--all` compte la branche LOCALE qu'on
pousse, qui pointe sur ce même `<sha>` : la plage était toujours vide.
Toute branche neuve partait avec « 0 commits scanned. no leaks found » —
c'est ce qu'a affiché le premier push de cette branche-ci.

La plage exclut maintenant ce que le distant visé a déjà, d'après nos
références de suivi (`--remotes=<distant>`, ou `--remotes` quand on pousse
vers une URL). Un premier push vers un distant vide scanne tout
l'historique, ce qui est juste.

Éprouvé sur un distant nu jetable, une clé AWS dans un commit de branche
neuve : l'ancien hook l'a laissée partir (0 commit scanné), le nouveau la
refuse (1 commit, « leaks found: 1 »), depuis un clone comme depuis un
worktree.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Next 16.4 ajoute au premier `next dev` un bloc « This is NOT the Next.js
you know » à `apps/web/AGENTS.md`, et le recrée si on l'enlève : l'arbre
est sale après chaque `pnpm dev`, et le fichier écrit à la main a un second
auteur qui se régénère seul (EQS/hub).

Éprouvé en 16.4.0 : sans le réglage, `next dev` annonce « Generated
AGENTS.md for AI agents » et modifie le fichier ; avec, rien ne bouge.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Facultative ; le défaut reste deux domaines. Tirée du hub (EQS/hub dc339c4,
proxy/), qui en avait besoin pour servir un serveur d'autorisation à une
seule adresse — et qui gagne au passage une image de front sans aucune
adresse d'API dedans, la même en dev et en prod.

- `variantes/une-adresse/proxy/` : Caddy, `/api/*`, `/.well-known/*`,
  `/health`, `/version`, `/openapi.json`, `/docs*` vers l'API, le reste vers
  le front ; `X-Forwarded-For` du client conservé derrière Traefik.
- `variantes/une-adresse/docker-compose.coolify.yml` : le compose par défaut
  plus le service `proxy` (sonde, restart, estampille), seul à recevoir un
  domaine ; `NEXT_PUBLIC_API_URL` vidée pour le front.
- `apps/web/next.config.mjs` : en développement ET `NEXT_PUBLIC_API_URL`
  vide, les mêmes chemins sont réécrits vers l'API ; inerte sinon. Le port
  de l'API arrive par `PORT_API`, que `scripts/next.mjs` relève avant de
  lancer Next : `next dev` pose `PORT` au port du FRONT, et lire `PORT` dans
  la configuration faisait boucler la réécriture sur elle-même (le hub a ce
  défaut). 127.0.0.1 plutôt que `localhost`, que bien des postes résolvent
  d'abord en `::1`.
- README de la variante : quand la choisir, la poser, les chemins, Coolify,
  et la dispense que `eqs coolify verify` exige à tort (il lit le Dockerfile
  de la racine pour tout service construit).

Joué : API + `next dev` en même origine (`/version`, `/health`, `/api/moi`,
`/openapi.json`, `/docs`, `/api/auth/get-session` servis par le port du
front), défaut inchangé (404 sur ces chemins côté front) ; image du proxy
construite et branchée sur les deux processus, même routage, sonde verte ;
`eqs doctor` et `eqs coolify verify` propres sur un clone où la variante est
posée, et ce dernier refuse toujours un `proxy` sans sonde ni estampille.
Pas joué : un déploiement Coolify depuis le gabarit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- README : une section « Où ce dépôt et le guide divergent » — le guide
  d'`eqs-platform` dit qu'il n'y a pas de lockfile (il y en a un), propose à
  l'étape 6 une vérification qui ne vérifie rien (`env -u DATABASE_URL pnpm
  --filter api start` recharge `.env` et ne rend jamais la main ;
  `DATABASE_URL= …` sort en 1 en la nommant, joué dans les deux sens), et
  ne connaît ni la tâche `hooks` ni `variantes/une-adresse`.
- README : `/eqs-init` vit dans `eqs-platform`, pas dans ce dépôt.
- `apps/web/AGENTS.md` : la CSP est tenue par `src/proxy.ts`, plus par un
  `middleware.ts` que Next 16 ne lit plus.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`cli.baseline-presente` signalait une baseline plus ancienne que le dernier
commit sur `apps/`. Rien d'autre ne bouge que la date, le commit et la
durée de la porte. Porte verte, intégration 17/17.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ce qui change pour un clone, depuis 0.17.1 :

- `TRUSTED_PROXIES=` dans `.env.example` : un seau de limitation par client
  derrière Traefik, pour better-auth et Express (eqs-platform #137).
- La suite de conformité émet une clé dont le propriétaire est suspendu :
  `auth.cle-compte-suspendu` est jouée et passe (#138). `depotClesPrisma`
  vient du socle, rien à changer dans `plateforme.ts`.
- pre-push : `pnpm check --e2e` (D-064). L'e2e ne tourne plus en CI, il
  tourne ici, et seulement si ce qu'il regarde a changé (D-065).
- `scripts/e2e.mjs` : le shell sous Windows est justifié, et `nosemgrep`
  le dit à l'étage sast. `.pnpm-store/` ignoré.
- next ^16.4.0 (GHSA-cjq9-62q9-8jv4).

Variante next-fullstack :

- `registerEqs` depuis `@eqs/platform-next/register`, l'entrée légère :
  better-auth n'est plus chargé avant la télémétrie (#142).
- `demarrerRoutes` monte les routes de conformité (#149) :
  `errors.validation` et `errors.500-sans-pile` se jouent, leurs dispenses
  sont retirées. `next.validation` reste dispensée : la route du socle n'est
  pas `.strict()`, et un champ inconnu y rend 200 (eqs-platform #159).
  Les raisons des autres dispenses disent ce qui reste à fournir.

Baseline refigée : les champs observés viennent de `.eqs/conformance.json`.
Nest : porte verte, intégration 17/17 (52 règles passées). Variante, jouée
sur un clone de labo : porte verte, intégration 17/17 (44 passées).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fusion de dev (PR #42, la montée 0.23.0 de Marc) dans la mienne
Some checks failed
ci / check-1 (pull_request) Successful in 2m11s
ci / check (pull_request) Successful in 0s
ci / secrets (pull_request) Successful in 4s
ci / vulns (pull_request) Failing after 14s
ci / sast (pull_request) Successful in 37s
ci / mutation (pull_request) Successful in 0s
ci / workflows (pull_request) Successful in 2s
ci / security (pull_request) Failing after 0s
ci / mutation-1 (pull_request) Has been skipped
ci / integration-1 (pull_request) Failing after 1m13s
ci / integration (pull_request) Failing after 0s
ci / contrat-a-jour (pull_request) Successful in 1m8s
ci / audit-1 (pull_request) Successful in 53s
ci / audit (pull_request) Successful in 0s
ci / fuzzing (pull_request) Failing after 2m17s
ci / contract (pull_request) Failing after 0s
3ec1f5652e
Les deux montées ont été faites en parallèle le même matin. Les doublons
(TRUSTED_PROXIES, clé suspendue, registerEqs léger, versions) prennent la
version de #42, déjà sur dev. S'ajoute par-dessus :

- pre-push : `pnpm check --e2e` (D-064), et le commentaire de ci.yml qui le
  dit ;
- `scripts/e2e.mjs` : le shell Windows justifié, `nosemgrep` ; `.pnpm-store/`
  ignoré ;
- variante next-fullstack, sur la baseline refaite par #42 : dispenses
  `errors.validation` et `errors.500-sans-pile` retirées (elles passent depuis
  #149), `next.validation` renvoie à eqs-platform #159, les raisons de
  `front.error-boundary` et de la télémétrie disent ce qui reste à fournir.

Porte verte, intégration 17/17 (nest), variante 17/17 sur un clone de labo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI : l'intégration construit l'API d'abord, le fuzzing sait la démarrer
Some checks failed
ci / check-1 (pull_request) Successful in 1m29s
ci / check (pull_request) Successful in 0s
ci / secrets (pull_request) Successful in 4s
ci / vulns (pull_request) Failing after 14s
ci / sast (pull_request) Successful in 37s
ci / workflows (pull_request) Successful in 3s
ci / mutation-1 (pull_request) Has been skipped
ci / security (pull_request) Failing after 0s
ci / mutation (pull_request) Successful in 0s
ci / integration-1 (pull_request) Failing after 1m18s
ci / integration (pull_request) Failing after 0s
ci / contrat-a-jour (pull_request) Successful in 1m2s
ci / audit-1 (pull_request) Successful in 48s
ci / audit (pull_request) Successful in 0s
ci / fuzzing (pull_request) Successful in 1m37s
ci / contract (pull_request) Successful in 0s
8d34df66ef
Première CI du gabarit (run #1, 2026-10-15), deux rouges qui ne tenaient
qu'au gabarit :

- integration : « Cannot find module apps/api/dist/main.js ». La suite lance
  le binaire construit sans le construire ; en local un dist/ ancien la
  faisait passer, au risque d'un faux vert (chasse 5). pnpm build d'abord —
  17/17 depuis un dist/ supprimé.
- fuzzing : « Missing script start ». start:api à la racine, et le contrat,
  le port et la santé dans ci.yml. Port 4000, celui par défaut du socle : la
  CI n'a pas de .env. 30/30 en local contre l'API démarrée comme en CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Intégration : la durée de la porte se lit aussi dans la baseline
Some checks failed
ci / check-1 (pull_request) Successful in 1m24s
ci / check (pull_request) Successful in 0s
ci / secrets (pull_request) Successful in 4s
ci / vulns (pull_request) Failing after 16s
ci / sast (pull_request) Successful in 36s
ci / workflows (pull_request) Successful in 2s
ci / security (pull_request) Failing after 0s
ci / mutation-1 (pull_request) Has been skipped
ci / mutation (pull_request) Successful in 0s
ci / integration-1 (pull_request) Successful in 1m20s
ci / integration (pull_request) Successful in 0s
ci / contrat-a-jour (pull_request) Successful in 1m2s
ci / audit-1 (pull_request) Successful in 48s
ci / audit (pull_request) Successful in 0s
ci / fuzzing (pull_request) Successful in 1m35s
ci / contract (pull_request) Successful in 0s
28ed18bc11
Deuxième CI du gabarit (2026-10-15) : 16/17, et « testing.check-budget
annoncée et jamais jouée ». La règle lisait .eqs/check.json, ignoré par git :
en CI, l'intégration part d'un checkout neuf où eqs-check n'a pas tourné. En
local le fichier traînait — vert ici, rouge là, pour la même cause.

Repli sur audit.tests.checkSeconds de eqs-baseline.json, commitée. Ni l'un ni
l'autre : non exécutable, jamais un 0 qui passerait à vide. 17/17 sans
.eqs/check.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci: le smoke ne tourne pas dans le gabarit lui-même
Some checks failed
ci / check-1 (pull_request) Successful in 1m30s
ci / check (pull_request) Successful in 0s
ci / secrets (pull_request) Successful in 4s
ci / vulns (pull_request) Failing after 14s
ci / sast (pull_request) Successful in 38s
ci / workflows (pull_request) Successful in 3s
ci / mutation-1 (pull_request) Has been skipped
ci / security (pull_request) Failing after 0s
ci / mutation (pull_request) Successful in 0s
ci / integration-1 (pull_request) Successful in 1m16s
ci / integration (pull_request) Successful in 0s
ci / contrat-a-jour (pull_request) Successful in 1m31s
ci / audit-1 (pull_request) Successful in 1m2s
ci / audit (pull_request) Successful in 0s
ci / fuzzing (pull_request) Successful in 1m36s
ci / contract (pull_request) Successful in 0s
140d0e7089
Il visait https://<service>.equilibre-structures.fr deux fois par heure et
occupait la file du runner partagé pour un échec certain. Les dépôts créés
depuis le gabarit gardent leur smoke.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Infrastructure/eqs-satellite-template!43
No description provided.