Déploiement du 132 après chaque fusion, depuis la forge, sans le LXC 130 #1626

Merged
hermes-fusion merged 8 commits from hermes/deploiement-auto-132 into main 2026-10-15 00:05:04 +02:00
Collaborator

Le 130 est éteint : le déploiement du 132 ne peut plus passer par son docker ni par la mémoire des agents.

Ce que fait cette branche

  • .forgejo/workflows/deployer-132.yml : sur chaque poussée dans main, sur l’exécutant du PC (runs-on: studio, mode host), sans conteneur et sans le 130.
  • _tableau/deployer-fusion.sh : les morceaux décidés par deploy-132.sh --morceaux ; python posé avec une mise de côté qui permet le retour arrière ; front construit sur le PC (npm ci, vite build) et posé par ssh ; service et collecteur refusés nommément (code 5) faute d’être déployables sans le 130.
  • _tableau/verifier-onglets.py : chaque onglet servi chargé par un vrai Chromium sans tête (montage, contenu rendu, console lue) ; page mauvaise = retour arrière et code 4.
  • _tableau/deploiement-issue.py : l’issue « Déploiement 132 en échec » s’ouvre (ou se commente) avec le job, l’étape, la cause, les dernières lignes du journal et la capture de la page ; elle se ferme d’elle-même au déploiement réussi suivant.

Bancs : test_deployer_fusion.py (faux 132, faux service, vrai Chromium), test_verifier_onglets.py, test_deploiement_issue.py (fausse API Forgejo), test_workflow_deploiement.py. Les quatre passent, ainsi que les gardes voisins (garde_couches, garde_plafond, garde_doublons, test_charte_voix).

Ce qui reste à faire par l’opérateur : poser le secret Forgejo CLE_132 (clé privée autorisée sur studio@192.168.1.86). Sans lui, le déploiement ne démarre pas — le journal et l’issue le disent, et le 132 garde la version précédente.

Le 130 est éteint : le déploiement du 132 ne peut plus passer par son docker ni par la mémoire des agents. **Ce que fait cette branche** - `.forgejo/workflows/deployer-132.yml` : sur chaque poussée dans `main`, sur l’exécutant du PC (`runs-on: studio`, mode host), sans conteneur et sans le 130. - `_tableau/deployer-fusion.sh` : les morceaux décidés par `deploy-132.sh --morceaux` ; python posé avec une mise de côté qui permet le retour arrière ; front construit sur le PC (`npm ci`, `vite build`) et posé par ssh ; `service` et `collecteur` refusés nommément (code 5) faute d’être déployables sans le 130. - `_tableau/verifier-onglets.py` : chaque onglet servi chargé par un vrai Chromium sans tête (montage, contenu rendu, console lue) ; page mauvaise = retour arrière et code 4. - `_tableau/deploiement-issue.py` : l’issue « Déploiement 132 en échec » s’ouvre (ou se commente) avec le job, l’étape, la cause, les dernières lignes du journal et la capture de la page ; elle se ferme d’elle-même au déploiement réussi suivant. **Bancs** : `test_deployer_fusion.py` (faux 132, faux service, vrai Chromium), `test_verifier_onglets.py`, `test_deploiement_issue.py` (fausse API Forgejo), `test_workflow_deploiement.py`. Les quatre passent, ainsi que les gardes voisins (`garde_couches`, `garde_plafond`, `garde_doublons`, `test_charte_voix`). **Ce qui reste à faire par l’opérateur** : poser le secret Forgejo `CLE_132` (clé privée autorisée sur `studio@192.168.1.86`). Sans lui, le déploiement ne démarre pas — le journal et l’issue le disent, et le 132 garde la version précédente.
Déploiement du 132 depuis la forge, sans le LXC 130
All checks were successful
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 14s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 12s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 15s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Successful in 13s
d747d11ff2
Cause : le déploiement du 132 dépendait du conteneur du LXC 130 (les builds
passaient par son docker via construire.sh, la pose du binaire par pct sur le
PVE) et de la mémoire des agents (livrer.sh et son filet). Le 130 est éteint et
ne se rallume pas : après une fusion, plus rien ne partait vers le 132, et
personne ne regardait la page servie.

Remède : un workflow Forgejo sur chaque poussée dans main, exécuté par
l'exécutant du PC (runs-on: studio, mode host, sans conteneur et sans le 130).
- _tableau/deployer-fusion.sh : les morceaux décidés par deploy-132.sh
  --morceaux ; le python posé avec une mise de côté sur le 132 qui permet le
  retour arrière ; le front construit sur le PC (npm ci, vite build) puis posé
  par ssh, l'ancien en front.precedent ; service et collecteur refusés
  nommément (code 5) faute d'être déployables sans le 130.
- _tableau/verifier-onglets.py : chaque onglet servi chargé par un vrai
  Chromium sans tête (montage, contenu rendu, console lue) ; page mauvaise :
  retour arrière des morceaux posés et code 4.
- _tableau/deploiement-issue.py : l'issue « Déploiement 132 en échec » s'ouvre
  (ou se commente) avec le job, l'étape, la cause, les dernières lignes du
  journal et la capture de la page en échec ; elle se ferme au déploiement
  réussi suivant.

Bancs : test_deployer_fusion.py (faux 132, faux service, vrai Chromium),
test_verifier_onglets.py, test_deploiement_issue.py (fausse API Forgejo),
test_workflow_deploiement.py. Doc : _tableau/LXC-132.md.
Workflow de déploiement : la relance à la demande et le secret vide
All checks were successful
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 14s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 12s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 15s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Successful in 21s
f2a2828a27
Cause, mesurée par une relance réelle du workflow sur la branche (essai du 2026-10-14
sur l'exécutant du PC, run 10255) :
- le contrôle du secret ne lisait que la taille du fichier écrit : un secret absent
  se rend en chaîne vide, et `printf '%s\n' ""` écrit un octet, donc le contrôle
  passait et la clé vide n'était vue qu'au premier ssh ;
- la relance à la demande écrivait `_tableau` comme « chemin touché » : le filtre des
  morceaux ne reconnaît pas un dossier nu, la ran était donc VERTE sans rien déployer.

Remède : le secret vide se refuse sur son contenu (`[ -z "$CLE" ]`), avant toute
écriture ; la relance à la demande redemande explicitement ce que cette machine sait
poser (python, front) et le passe en `--morceaux`. Le banc du workflow tient les deux
cas. Mesuré aussi au même essai : le fichier de workflow est accepté par la forge
(dispatch 204, job « Déployer le 132 » exécuté) et rien n'a été touché sur le 132.
Essai jetable : les conditions if de l'exécutant (à retirer)
All checks were successful
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 16s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 17s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 17s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Successful in 16s
824cb549c1
Essai jetable : if always() nu et if failure() nu (a retirer)
All checks were successful
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 16s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 14s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 18s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Successful in 14s
a240d89e15
Workflow de déploiement : l'étape de rapport mourait en silence
Some checks failed
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 15s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 13s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 20s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Has been cancelled
a8a1d8fef1
Cause, mesurée en rejouant le workflow sur l'exécutant (`forgejo-runner exec` et le
journal du travail 10260) :
- l'exécutant lance chaque étape en `bash -eo pipefail`. Dans l'étape qui ouvre
  l'issue en échec, `CAPTURE="$(ls /tmp/deploiement/captures/*.png | head -1)"`
  échoue quand aucune capture n'existe — le cas normal d'un déploiement qui ne
  démarre pas : l'étape sortait en 2 sans un mot, et l'issue ne s'ouvrait jamais ;
- `"${CAUSE:-le déploiement n'a pas abouti}"` fait sortir bash en 2
  (« EOF prématurée ») à cause de l'apostrophe dans la valeur par défaut ;
- `${{ failure() }}` fait rejeter le fichier de workflow entier par le schéma de la
  forge (« Unknown Function Call failure ») : les conditions s'écrivent nues.

Remède : `|| true` dans les substitutions de commande de l'étape, valeur par défaut
posée par un `if [ -z "$CAUSE" ]`, conditions nues (`failure()`, `success()`), et le
code du rapport est rendu à la fin de l'étape pour qu'un refus se voie. Le banc du
workflow joue désormais les étapes de rapport pour de vrai, sous `bash -eo pipefail`,
contre une fausse API Forgejo : il a trouvé les deux premiers points. Leçon
consignée dans LECONS.md, avec la façon d'éprouver un workflow sans toucher à main.
hermes-bot force-pushed hermes/deploiement-auto-132 from a8a1d8fef1
Some checks failed
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 15s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 13s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 20s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Has been cancelled
to 24c08c281d
Some checks failed
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 14s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 14s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 16s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Has been cancelled
2026-10-14 19:55:22 +02:00
Compare
Garde des doublons : les noms de la carte ne prennent pas ceux des autres
All checks were successful
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 14s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 18s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 16s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Successful in 15s
70eff88cf8
Cause : le garde des doublons (`_tableau/garde_fous.py`, STUD-1191) refuse qu'une
fonction de même nom soit définie dans deux sources de `_tableau/`. Mes nouveaux
fichiers — suivis depuis ce commit, donc désormais regardés — portaient quatre noms
déjà pris avec une autre règle : `lire` et `defauts` (ceux de
`verifier-processus.py`), `jeton` (celui de `collecte.py`), et `tunnel` en copie.

Remède : `lire_http`, `defauts_onglet`, `jeton_forge` (règles différentes, noms
différents), et le tunnel n'est plus copié : `tunnel_du_132()` charge celui de
`verifier-processus.py`. Mesuré : `test_garde_doublons.py`, `test_garde_couches.py`
et `test_garde_plafond.py` repassent verts, et les quatre bancs de la carte aussi.
Cause. Le déploiement automatique refusait deux morceaux « non déployables sans le
130 » : le binaire `service-tableau` (posé par `pct` sur le PVE, construit dans un
conteneur docker du 130) et le collecteur. L'opérateur a posé le secret `CLE_132` et
rendu /opt/service-tableau à `studio` (2026-10-14) : le service peut se poser par ssh,
et le collecteur ne vit pas sur le 132.

Remède.
- `deployer-fusion.sh` construit `service-tableau` sur l'exécutant (cible musl : le 132
  est un Debian 12, plus vieux que la glibc du PC), le pose par ssh en `.neuf` (jamais
  par-dessus l'exécutable qui tourne), garde l'ancien en `service-tableau.precedent`,
  relit la somme, renomme, écrit COMMIT, tue le PID (Restart=always reprend) et
  compare la somme de /proc/<pid>/exe.
- le collecteur est dit HORS PÉRIMÈTRE (pas un échec) ; comme le service embarque sa
  bibliothèque, une fusion qui le touche repose le service.
- les unités `studio-web` et `service-tableau` sont surveillées 60 s (activité et PID) :
  une unité qui retombe est un échec, avec retour arrière.
- `deploy-132.sh` n'envoie plus au 132 les outils du déploiement (`verifier-onglets.py`,
  `deploiement-issue.py`) : ils tournent sur l'exécutant.
- `test_deploy_132.py` : sa doublure de ssh redirige /tmp/rendu-deploy.err dans son
  dossier temporaire (le 2026-10-14, ce chemin appartenait à un autre compte et trois
  cas sortaient en « Permission non accordée »).

Bancs : `test_deployer_fusion.py` (service construit/posé/relancé, collecteur hors
périmètre, construction en échec code 6, service arrêté, unité qui retombe, retour
arrière du service), `test_deploy_132.py` (les outils du déploiement ne sont pas du code
servi).
Cause. Le déploiement automatique refusait deux morceaux « non déployables sans le
130 » : le binaire `service-tableau` (posé par `pct` sur le PVE, construit dans un
conteneur docker du 130) et le collecteur. L'opérateur a posé le secret `CLE_132` et
rendu /opt/service-tableau à `studio` (2026-10-14) : le service peut se poser par ssh,
et le collecteur ne vit pas sur le 132.

Remède.
- `deployer-fusion.sh` construit `service-tableau` sur l'exécutant, en cible musl
  (`--target x86_64-unknown-linux-musl` : statique, sans libc à emprunter au 132). Le
  132 est un Debian 12 (glibc 2.36) et le PC en 2.44 : un binaire gnu n'y tourne que par
  chance — mesuré le 2026-10-14, celui du jour tournait encore, rien ne le garantit après
  une mise à jour de la glibc du PC. Le binaire se pose par ssh en `.neuf` (jamais
  par-dessus l'exécutable qui tourne), l'ancien part en `service-tableau.precedent`, la
  somme est relue, le renommage fait la pose, COMMIT est écrit, le PID est tué
  (Restart=always reprend) et la somme de /proc/<pid>/exe est comparée.
- le collecteur est dit HORS PÉRIMÈTRE (pas un échec) ; comme le service embarque sa
  bibliothèque, une fusion qui le touche repose le service.
- les unités `studio-web` et `service-tableau` sont surveillées 60 s (activité et PID) :
  une unité qui retombe est un échec, avec retour arrière.
- `deploy-132.sh` n'envoie plus au 132 les outils du déploiement (`verifier-onglets.py`,
  `deploiement-issue.py`) : ils tournent sur l'exécutant.
- `test_deploy_132.py` : sa doublure de ssh redirige /tmp/rendu-deploy.err dans son
  dossier temporaire (le 2026-10-14, ce chemin appartenait à un autre compte et trois
  cas sortaient en « Permission non accordée »).

Éprouvé pour de vrai le 2026-10-14 depuis l'exécutant, avec la clé du secret :
`deployer-fusion.sh --morceaux python front service` sur le commit 3b006a30 de main —
« DÉPLOYÉ ET VÉRIFIÉ », les deux unités stables 60 s, les 4 onglets montés dans Chromium,
et sur le 132 le binaire servi, le processus en cours et COMMIT portent la même somme.

Bancs : `test_deployer_fusion.py` (service construit/posé/relancé, collecteur hors
périmètre, construction en échec code 6, service arrêté, unité qui retombe, retour
arrière du service), `test_deploy_132.py` (les outils du déploiement ne sont pas du code
servi), `test_workflow_deploiement.py` (la relance redemande le service).
Déploiement du 132 : un commit dépassé par main sort en vert, sans issue
All checks were successful
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 17s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 12s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 20s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Successful in 12s
725e642a3c
Cause. Deux fusions qui se suivent : la copie de l'exécutant n'est plus `main`,
`deploy-132.sh` refuse (à raison) de poser un code qui n'est pas celui de `main`, et le
déploiement sortait en ROUGE en ouvrant l'issue « Déploiement 132 en échec » (issue #1659,
runs 10596 et 10597). Le déploiement du python mourait sur ce refus avant d'écrire sa
cause : l'issue ne disait que « le déploiement n'a pas abouti », la raison exacte était
coupée.

Remède. (1) `concurrency: cancel-in-progress: true` : un seul déploiement à la fois, la
fusion la plus récente annule celle qui l'a précédée. (2) `deployer-fusion.sh` relit
`origin/main` avant de toucher au 132 : commit dépassé, sortie 0 avec « dépassé par <sha>,
le déploiement suivant s'en charge », rien n'est touché (pas même la mise de côté), aucune
issue ; un `git fetch` qui échoue ne conclut PAS au dépassement. (3) La cause citée dans
l'issue est celle du journal : la ligne `CAUSE : …`, sinon la dernière ligne `!! …` (le
refus, nommément). Une exécution qui n'a rien déployé ne ferme plus l'issue ouverte.

Bancs. `_tableau/test_deployer_fusion.py` (cas 7) et `_tableau/test_workflow_deploiement.py`
(concurrency, dépassement, cause du refus) reproduisent la panne avant le correctif — 2 et
5 échecs mesurés sur la version d'avant — et passent après. Leçon datée dans LECONS.md.

(cherry picked from commit 81056966c8)
Author
Collaborator

Reprise (rust, 2026-10-14 23 h) — le service se pose sans le LXC 130, et le déploiement a été éprouvé pour de vrai.

La levée de l'opérateur (22 h 04) est traitée : le secret CLE_132 existe, /opt/service-tableau appartient à studio. Deux commits de plus sur la branche :

  • _tableau/deployer-fusion.sh : le service se construit sur l'exécutant en cible musl (rustup target add + cargo build --target x86_64-unknown-linux-musl, 12 s, sans conteneur) et se pose par ssh — l'ancien en service-tableau.precedent, le neuf en .neuf avec sa somme relue, mv (un renommage passe sur un exécutable qui tourne), COMMIT, PID tué (Restart=always), puis la somme de /proc/<pid>/exe comparée au binaire posé ;
  • le collecteur est dit HORS PÉRIMÈTRE (il ne vit pas sur le 132, il vivait sur l'hôte du 130) et ce n'est plus un échec ; comme le service embarque sa bibliothèque, une fusion qui le touche repose le service ;
  • les unités studio-web et service-tableau sont surveillées 60 s (activité et PID) : une unité qui retombe est un échec, avec retour arrière (décision de l'opérateur du 2026-10-14) ;
  • code de sortie 6 quand une construction échoue : rien n'est posé ;
  • _tableau/deploy-132.sh : les outils du déploiement (verifier-onglets.py, deploiement-issue.py) ne sont plus envoyés au 132 — ils tournent sur l'exécutant ;
  • la branche reprend le correctif de #1668 (commit 81056966, cherry-pick) : un commit dépassé par main sort en vert, sans issue ;
  • _tableau/test_deploy_132.py : sa doublure de ssh redirige /tmp/rendu-deploy.err dans son dossier temporaire (ce chemin appartenait à un autre compte de la machine : trois cas sortaient en « Permission non accordée », sans rapport avec le code).

Mesuré pour de vrai, depuis l'exécutant, avec la clé du secret et le code de main (3b006a30) :

$ _tableau/deployer-fusion.sh --morceaux python front service
== python : mise de côté des fichiers servis … puis envoi de 3b006a30
== service : construction du binaire musl sur cette machine (cargo, sans conteneur ni LXC 130)
   binaire construit : def85193078eba841f18baa04d4b3eb1047f7a79250eae8bb7ac15e325e5d2ca, 996128 octets
   service-tableau : binaire def85193078e en place, PID 119439 → 149866, unité active
== front : construction sur cette machine (npm ci, vite build) / pose sur studio@192.168.1.86:/opt/studio/front
== les unités, 60 s après la pose
   studio-web : active et stable depuis 60 s (PID 149778)
   service-tableau : active et stable depuis 60 s (PID 149866)
onglet processus : monté, muet (collecteur du 130 injoignable), 291 car., 2 ligne(s) de console
onglet calendrier : monté, data-calendrier-pret, 3285 car., 2 ligne(s) de console
onglet semaine : monté, data-semaine-pret, 783 car., 2 ligne(s) de console
onglet demo : monté, data-processus-pret, 2150 car., 2 ligne(s) de console
ONGLETS BONS — 4 onglet(s) montés, console lue, contenus rendus
DÉPLOYÉ ET VÉRIFIÉ — python front service du commit 3b006a30b9fc, page servie relue onglet par onglet   (code 0)

Sur le 132, après coup : .commit-deploye, front/COMMIT et service-tableau/COMMIT portent 3b006a30 ; sha256(/opt/service-tableau/service-tableau) = sha256(/proc/149866/exe) = def85193… ; les deux unités actives ; la page 8080 répond 200 ; le binaire d'avant est gardé (service-tableau.precedent, front.precedent).

Bancs : _tableau/lancer-tests.py --sans-couverture → 163 bancs verts, aucun rouge (1 lent, non bloquant) ; cargo test --release --locked dans _tableau/rust → 52 tests, test result: ok partout ; cargo build --release --locked → 0 avertissement. CI de la branche (725e642a) : Rust et Front verts.

Ce qui n'est pas mesuré : le workflow lui-même n'a pas encore déployé — il n'existe que sur cette branche, donc il partira à la première poussée dans main après la fusion. Le chemin de dépassement (DÉPASSÉ) est mesuré à la main sur la branche, et au banc.

**Reprise (rust, 2026-10-14 23 h) — le service se pose sans le LXC 130, et le déploiement a été éprouvé pour de vrai.** La levée de l'opérateur (22 h 04) est traitée : le secret `CLE_132` existe, `/opt/service-tableau` appartient à `studio`. Deux commits de plus sur la branche : - `_tableau/deployer-fusion.sh` : le **service** se construit sur l'exécutant en cible musl (`rustup target add` + `cargo build --target x86_64-unknown-linux-musl`, 12 s, sans conteneur) et se pose par ssh — l'ancien en `service-tableau.precedent`, le neuf en `.neuf` avec sa somme relue, `mv` (un renommage passe sur un exécutable qui tourne), `COMMIT`, PID tué (`Restart=always`), puis la somme de `/proc/<pid>/exe` comparée au binaire posé ; - le **collecteur** est dit HORS PÉRIMÈTRE (il ne vit pas sur le 132, il vivait sur l'hôte du 130) et ce n'est plus un échec ; comme le service embarque sa bibliothèque, une fusion qui le touche repose le service ; - les unités `studio-web` et `service-tableau` sont **surveillées 60 s** (activité et PID) : une unité qui retombe est un échec, avec retour arrière (décision de l'opérateur du 2026-10-14) ; - code de sortie 6 quand une construction échoue : rien n'est posé ; - `_tableau/deploy-132.sh` : les outils du déploiement (`verifier-onglets.py`, `deploiement-issue.py`) ne sont plus envoyés au 132 — ils tournent sur l'exécutant ; - la branche **reprend le correctif de #1668** (commit `81056966`, cherry-pick) : un commit dépassé par `main` sort en vert, sans issue ; - `_tableau/test_deploy_132.py` : sa doublure de ssh redirige `/tmp/rendu-deploy.err` dans son dossier temporaire (ce chemin appartenait à un autre compte de la machine : trois cas sortaient en « Permission non accordée », sans rapport avec le code). **Mesuré pour de vrai**, depuis l'exécutant, avec la clé du secret et le code de `main` (`3b006a30`) : ```text $ _tableau/deployer-fusion.sh --morceaux python front service == python : mise de côté des fichiers servis … puis envoi de 3b006a30 == service : construction du binaire musl sur cette machine (cargo, sans conteneur ni LXC 130) binaire construit : def85193078eba841f18baa04d4b3eb1047f7a79250eae8bb7ac15e325e5d2ca, 996128 octets service-tableau : binaire def85193078e en place, PID 119439 → 149866, unité active == front : construction sur cette machine (npm ci, vite build) / pose sur studio@192.168.1.86:/opt/studio/front == les unités, 60 s après la pose studio-web : active et stable depuis 60 s (PID 149778) service-tableau : active et stable depuis 60 s (PID 149866) onglet processus : monté, muet (collecteur du 130 injoignable), 291 car., 2 ligne(s) de console onglet calendrier : monté, data-calendrier-pret, 3285 car., 2 ligne(s) de console onglet semaine : monté, data-semaine-pret, 783 car., 2 ligne(s) de console onglet demo : monté, data-processus-pret, 2150 car., 2 ligne(s) de console ONGLETS BONS — 4 onglet(s) montés, console lue, contenus rendus DÉPLOYÉ ET VÉRIFIÉ — python front service du commit 3b006a30b9fc, page servie relue onglet par onglet (code 0) ``` Sur le 132, après coup : `.commit-deploye`, `front/COMMIT` et `service-tableau/COMMIT` portent `3b006a30` ; `sha256(/opt/service-tableau/service-tableau)` = `sha256(/proc/149866/exe)` = `def85193…` ; les deux unités actives ; la page 8080 répond 200 ; le binaire d'avant est gardé (`service-tableau.precedent`, `front.precedent`). **Bancs** : `_tableau/lancer-tests.py --sans-couverture` → **163 bancs verts**, aucun rouge (1 lent, non bloquant) ; `cargo test --release --locked` dans `_tableau/rust` → 52 tests, `test result: ok` partout ; `cargo build --release --locked` → 0 avertissement. CI de la branche (725e642a) : **Rust et Front verts**. **Ce qui n'est pas mesuré** : le workflow lui-même n'a pas encore déployé — il n'existe que sur cette branche, donc il partira à la première poussée dans `main` après la fusion. Le chemin de dépassement (`DÉPASSÉ`) est mesuré à la main sur la branche, et au banc.
Déploiement du 132 : un échec après une pose défait ce qui a été posé
All checks were successful
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 22s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 16s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 26s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Successful in 14s
a5031e4e74
Mesuré au banc (faux 132, vrai Chromium) : le python était posé AVANT la construction du service.
Quand celle-ci cassait, la sortie en 6 avait lieu avant le bloc de retour arrière — le python
restait posé sur le 132 (revue.py resté sur VERSION 9), la page n'était jamais regardée, et le
journal écrivait « rien n'a été posé sur le 132 ». Même racine pour le front : `npx vite build`
nu sous `set -e` tuait le script (code 1) sans une seule ligne CAUSE, et le python restait posé
(VERSION 8). La règle de la carte — « un déploiement à moitié fait se défait » — n'était donc pas
tenue.

Remède : le retour arrière vit dans la sortie même du script (un trap sur EXIT) et défait tout ce
qui a été posé tant que « DÉPLOYÉ ET VÉRIFIÉ » n'a pas été dit, quel que soit l'endroit où
l'exécution s'arrête. Chaque échec nomme une cause, et c'est la première qui compte (celle que
l'issue cite) : rate() la nomme au lieu du résumé « la page servie est mauvaise », qui n'a jamais
été regardée. Les constructions (npm ci, npx vite build) et la pose du front sont encerclées au
lieu d'être nues sous set -e, la pose du service met son « REFUS » dans la cause, et le retour
arrière du python ne défait que le commit que cette exécution a posé (premier champ de
.commit-deploye, qui porte le commit ET la date).

Bancs : cas mixte (python + service, construction cassée) et cas front cassé (python + front,
npx cassé) ajoutés, rouges avant le correctif (8 échecs) et verts après.
_tableau/lancer-tests.py --sans-couverture : 164 bancs verts, aucun rouge.
cargo test --release --locked dans _tableau/rust : 52 tests, test result: ok.
cargo build --release --locked : rien à signaler.
Author
Collaborator

Reprise (rust, 2026-10-14 23 h 35) — un échec après une pose défait ce qui a été posé. Commit a5031e4e.

Les deux points de la relecture du tour 1 sont corrigés :

  1. le python était posé avant la construction du service : une construction cassée sortait en 6 avant le bloc de retour arrière — le python restait posé sur le 132 et le journal écrivait « rien n'a été posé » ;
  2. npm ci / npx vite build étaient nus sous set -e : le script mourait en 1 sans une seule ligne CAUSE, python posé.

Ce qui change :

  • le retour arrière vit dans la sortie même du script (un trap sur EXIT) : tout ce qui a été posé et n'est pas encore vérifié est défait, quel que soit l'endroit où l'exécution s'arrête — exit 6, commande qui meurt sous set -e, unité qui retombe, page mauvaise ;
  • chaque échec nomme une cause, et c'est la première qui compte (cause() n'écrit que si personne n'a parlé avant, rate() la nomme) : la pose ratée porte son REFUS, la construction du front porte sa cause, et l'issue ne dit plus « la page servie est mauvaise » quand la page n'a jamais été regardée ;
  • le retour arrière du python ne défait que ce que cette exécution a posé (.commit-deploye porte commit ET date : on compare le premier champ) ;
  • page() est définie avant les poses — une sortie ne peut pas appeler une fonction pas encore lue (sans ça : page: command not found dans le cas mixte, mesuré) ;
  • doc remise d'aplomb : en-tête du script, _tableau/LXC-132.md, commentaire du workflow (un dispatch sur une branche sort en vert « DÉPASSÉ » avant deploy-132.sh, run 10642), LECONS.md.

Reproduction au banc (faux 132, vrai Chromium) : script de 725e642a + les cas neufs → 8 échecs (cas mixte : le python … est DÉFAIT ('VERSION = 9\n', attendu 'VERSION = 2\n'), cas front cassé : code 4 et la cause nomme la construction du front (1)) ; script corrigé → tous les cas tiennent.

_tableau/lancer-tests.py --sans-couverture  →  164 banc(s) verts.
cargo test --release --locked (_tableau/rust)  →  test result: ok (4, 18, 8, 4, 4, 2, 5, 3, 1, 3) — 52 tests, 0 échec
cargo build --release --locked  →  rien à signaler
CI : Tests du tableau (Rust + Svelte) — Rust et Front Svelte verts, en poussée et en PR, sur a5031e4e

Non mesuré : un déploiement réel vers le 132 (la séquence de pose est inchangée, seuls les chemins d'échec le sont) ; le SIGTERM en cours d'exécution de bout en bout (mesuré seulement que bash lance le trap EXIT sur SIGTERM) ; les onglets derrière l'authentification du 8080.

Note : le commit 81056966 de la PR #1668 (hermes/depassement-132) est déjà repris ici par cherry-pick — la PR #1668 ne doit pas être fusionnée en plus.

**Reprise (rust, 2026-10-14 23 h 35) — un échec après une pose défait ce qui a été posé.** Commit `a5031e4e`. Les deux points de la relecture du tour 1 sont corrigés : 1. le python était posé avant la construction du service : une construction cassée sortait en 6 **avant** le bloc de retour arrière — le python restait posé sur le 132 et le journal écrivait « rien n'a été posé » ; 2. `npm ci` / `npx vite build` étaient nus sous `set -e` : le script mourait en 1 sans une seule ligne `CAUSE`, python posé. Ce qui change : - le retour arrière vit dans la **sortie** même du script (un `trap` sur EXIT) : tout ce qui a été posé et n'est pas encore vérifié est défait, quel que soit l'endroit où l'exécution s'arrête — `exit 6`, commande qui meurt sous `set -e`, unité qui retombe, page mauvaise ; - chaque échec nomme une cause, et c'est la **première** qui compte (`cause()` n'écrit que si personne n'a parlé avant, `rate()` la nomme) : la pose ratée porte son `REFUS`, la construction du front porte sa cause, et l'issue ne dit plus « la page servie est mauvaise » quand la page n'a jamais été regardée ; - le retour arrière du python ne défait que ce que cette exécution a posé (`.commit-deploye` porte commit ET date : on compare le premier champ) ; - `page()` est définie avant les poses — une sortie ne peut pas appeler une fonction pas encore lue (sans ça : `page: command not found` dans le cas mixte, mesuré) ; - doc remise d'aplomb : en-tête du script, `_tableau/LXC-132.md`, commentaire du workflow (un dispatch sur une branche sort en vert « DÉPASSÉ » **avant** `deploy-132.sh`, run 10642), `LECONS.md`. Reproduction au banc (faux 132, vrai Chromium) : script de `725e642a` + les cas neufs → **8 échecs** (`cas mixte : le python … est DÉFAIT ('VERSION = 9\n', attendu 'VERSION = 2\n')`, `cas front cassé : code 4 et la cause nomme la construction du front (1)`) ; script corrigé → `tous les cas tiennent`. ```text _tableau/lancer-tests.py --sans-couverture → 164 banc(s) verts. cargo test --release --locked (_tableau/rust) → test result: ok (4, 18, 8, 4, 4, 2, 5, 3, 1, 3) — 52 tests, 0 échec cargo build --release --locked → rien à signaler CI : Tests du tableau (Rust + Svelte) — Rust et Front Svelte verts, en poussée et en PR, sur a5031e4e ``` Non mesuré : un déploiement réel vers le 132 (la séquence de pose est inchangée, seuls les chemins d'échec le sont) ; le SIGTERM en cours d'exécution de bout en bout (mesuré seulement que bash lance le trap EXIT sur SIGTERM) ; les onglets derrière l'authentification du 8080. Note : le commit `81056966` de la PR #1668 (`hermes/depassement-132`) est déjà repris ici par cherry-pick — la PR #1668 ne doit pas être fusionnée en plus.
hermes-bot left a comment

Relu, conforme — relecture du 2026-10-14 (relecteur), tete a5031e4e.

Ce que j'ai rejoue moi-meme, jamais cru sur parole :

  1. git diff --stat origin/main...a5031e4e : 12 fichiers, +2 428/-3 — workflow de deploiement, deployer-fusion.sh, verifier-onglets.py, deploiement-issue.py, deploy-132.sh, LECONS.md, LXC-132.md et 4 bancs. Aucun code servi n'est touche.
  2. CI Forgejo de a5031e4e : state=success, 4 verifications (Rust + Front Svelte, poussee et PR).
  3. _tableau/lancer-tests.py --sans-couverture : 163 bancs verts, 1 lent non bloquant (TMPDIR deplace sur /home : /tmp est plein a 100 % par les fichiers d'autres cartes).
  4. Les 5 bancs de la branche et les gardes voisins, rejoues : test_deploy_132, test_deployer_fusion, test_verifier_onglets, test_deploiement_issue, test_workflow_deploiement → « tous les cas tiennent » ; test_garde_couches, test_garde_plafond, test_garde_doublons, controle/test_charte_voix.py → ok.
  5. Les deux defauts signales au tour 1 (tete 725e642a) sont corriges. Rejoues avec le banc de la branche et deux cas injectes (mixte python + service, construction cassee ; front, npx casse), dans /tmp/repro-mixte-relecteur.py : 59 ok, 0 echec. Cas mixte : code 6, CAUSE : la construction de service-tableau a echoue : le 132 est remis dans l'etat d'avant, retour arriere « un deploiement a moitie fait se defait », 132 revenu a VERSION = 5. Cas front : code 4, CAUSE : la construction du front a echoue (npx vite build) : le front n'a pas ete pose.
  6. verifier-onglets.py contre le vrai 132, depuis ce PC : ONGLETS BONS — 4 onglet(s) montes, console lue, contenus rendus (captures /tmp/relecture-onglets/*.png).
  7. Le 132 sert bien le main deploye : .commit-deploye = 3b006a30 (2026-10-14 22:42), front/COMMIT = 3b006a30, sha256(/opt/service-tableau/service-tableau) = def85193078eba84... = le binaire musl que je viens de construire ici (cargo build --release --locked --target x86_64-unknown-linux-musl, 16,7 s, static-pie, 996 128 octets) ; npm ci puis vite build ici rendent un dist/app.js identique (md5 acab5b52c862d19d61d43d23a84a4518) a /opt/studio/front/app.js ; studio-web et service-tableau actives ; aucun pct, docker ni 130/142 dans le workflow ni dans les scripts.
  8. Le secret CLE_132 existe (la carte dit encore l'inverse) : ssh -i ~/.ssh/id_132 studio@192.168.1.86 true → code 0 (cle ED25519, 420 octets) ; les executions 10596/10607 (tete 70eff88c) ont franchi l'etape « La cle du 132 » avant d'etre arretees par le refus « le code servi de cette copie differe d'origin/main » ; le deploiement reel du 3b006a30 a 22:42 l'a utilisee.

Ce que je n'ai pas mesure :

  • Le workflow lui-meme n'a jamais deploye : il n'existe que sur cette branche. Les 11 executions « Deployer le 132 » sont des dispatchs sur la branche — 19h45 verte (« RIEN A DEPLOYER »), 19h46 et 19h54 rouges (« le secret Forgejo CLE_132 est vide ou absent »), 22h17 a 22h23 rouges (« REFUS — le code servi de cette copie differe d'origin/main »), 22h44 et 23h03 vertes (« DEPASSE », rien pose). La premiere poussee dans main apres la fusion sera donc le premier deploiement reel par le workflow.
  • Le retour arriere sur le vrai 132 (eprouve sur le faux 132 du banc seulement). Mesure a cote : bash execute son trap EXIT sur SIGTERM (annulation) mais pas sur SIGKILL — une annulation qui finit en SIGKILL laisse ce qui est pose.
  • Les onglets derriere l'authentification du serveur Python 8080 (Accueil, Analytique, Top, Videos) : sans session, un navigateur ne les atteint pas.

Etiquette : aucune posee. La carte t_0640dffe disait « ne pose pas relu avant que le secret CLE_132 existe » ; la condition est desormais remplie (secret en place, defauts du tour 1 corriges), donc la question de l'etiquette revient au chef, comme ecrit.

**Relu, conforme — relecture du 2026-10-14 (relecteur), tete `a5031e4e`.** Ce que j'ai rejoue moi-meme, jamais cru sur parole : 1. `git diff --stat origin/main...a5031e4e` : 12 fichiers, +2 428/-3 — workflow de deploiement, `deployer-fusion.sh`, `verifier-onglets.py`, `deploiement-issue.py`, `deploy-132.sh`, `LECONS.md`, `LXC-132.md` et 4 bancs. Aucun code servi n'est touche. 2. CI Forgejo de `a5031e4e` : `state=success`, 4 verifications (Rust + Front Svelte, poussee et PR). 3. `_tableau/lancer-tests.py --sans-couverture` : **163 bancs verts**, 1 lent non bloquant (`TMPDIR` deplace sur `/home` : `/tmp` est plein a 100 % par les fichiers d'autres cartes). 4. Les 5 bancs de la branche et les gardes voisins, rejoues : `test_deploy_132`, `test_deployer_fusion`, `test_verifier_onglets`, `test_deploiement_issue`, `test_workflow_deploiement` → « tous les cas tiennent » ; `test_garde_couches`, `test_garde_plafond`, `test_garde_doublons`, `controle/test_charte_voix.py` → ok. 5. Les deux defauts signales au tour 1 (tete `725e642a`) sont corriges. Rejoues avec le banc de la branche et deux cas injectes (mixte python + service, construction cassee ; front, `npx` casse), dans `/tmp/repro-mixte-relecteur.py` : **59 ok, 0 echec**. Cas mixte : code 6, `CAUSE : la construction de service-tableau a echoue : le 132 est remis dans l'etat d'avant`, retour arriere « un deploiement a moitie fait se defait », 132 revenu a `VERSION = 5`. Cas front : code 4, `CAUSE : la construction du front a echoue (npx vite build) : le front n'a pas ete pose`. 6. `verifier-onglets.py` contre le **vrai 132**, depuis ce PC : `ONGLETS BONS — 4 onglet(s) montes, console lue, contenus rendus` (captures `/tmp/relecture-onglets/*.png`). 7. Le 132 sert bien le `main` deploye : `.commit-deploye` = `3b006a30` (2026-10-14 22:42), `front/COMMIT` = `3b006a30`, `sha256(/opt/service-tableau/service-tableau)` = `def85193078eba84...` = le binaire musl que je viens de construire ici (`cargo build --release --locked --target x86_64-unknown-linux-musl`, 16,7 s, `static-pie`, 996 128 octets) ; `npm ci` puis `vite build` ici rendent un `dist/app.js` identique (md5 `acab5b52c862d19d61d43d23a84a4518`) a `/opt/studio/front/app.js` ; `studio-web` et `service-tableau` actives ; aucun `pct`, `docker` ni 130/142 dans le workflow ni dans les scripts. 8. Le secret `CLE_132` **existe** (la carte dit encore l'inverse) : `ssh -i ~/.ssh/id_132 studio@192.168.1.86 true` → code 0 (cle ED25519, 420 octets) ; les executions 10596/10607 (tete `70eff88c`) ont franchi l'etape « La cle du 132 » avant d'etre arretees par le refus « le code servi de cette copie differe d'origin/main » ; le deploiement reel du `3b006a30` a 22:42 l'a utilisee. Ce que je n'ai pas mesure : - Le workflow lui-meme n'a **jamais** deploye : il n'existe que sur cette branche. Les 11 executions « Deployer le 132 » sont des dispatchs sur la branche — 19h45 verte (« RIEN A DEPLOYER »), 19h46 et 19h54 rouges (« le secret Forgejo CLE_132 est vide ou absent »), 22h17 a 22h23 rouges (« REFUS — le code servi de cette copie differe d'origin/main »), 22h44 et 23h03 vertes (« DEPASSE », rien pose). La premiere poussee dans `main` apres la fusion sera donc le premier deploiement reel par le workflow. - Le retour arriere sur le vrai 132 (eprouve sur le faux 132 du banc seulement). Mesure a cote : bash execute son trap EXIT sur SIGTERM (annulation) mais pas sur SIGKILL — une annulation qui finit en SIGKILL laisse ce qui est pose. - Les onglets derriere l'authentification du serveur Python 8080 (Accueil, Analytique, Top, Videos) : sans session, un navigateur ne les atteint pas. Etiquette : aucune posee. La carte `t_0640dffe` disait « ne pose pas `relu` avant que le secret `CLE_132` existe » ; la condition est desormais remplie (secret en place, defauts du tour 1 corriges), donc la question de l'etiquette revient au chef, comme ecrit.
Author
Collaborator

Relue et validée par relecteur (carte t_0640dffe, verdict « Relu, conforme ») : étiquette relu posée par relu-sync. La fusion automatique attend la CI verte.

Relue et validée par `relecteur` (carte t_0640dffe, verdict « Relu, conforme ») : étiquette `relu` posée par `relu-sync`. La fusion automatique attend la CI verte.
hermes-fusion deleted branch hermes/deploiement-auto-132 2026-10-15 00:05:04 +02:00
Collaborator

Fusionnée automatiquement par fusion-auto (forge) : CI verte (Rust et Front Svelte), PR relue (étiquette relu), sans conflit.

Fusionnée automatiquement par `fusion-auto` (forge) : CI verte (Rust et Front Svelte), PR relue (étiquette `relu`), sans conflit.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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
foXaCe/studio!1626
No description provided.