infra : la configuration de l'exécuteur entre au dépôt (#141) #145

Merged
gabriel merged 3 commits from lenaic/141-runner-reproductible into develop 2026-09-04 12:44:07 +00:00
Owner

Ce que ça change

La configuration de l'exécuteur Forgejo entre au dépôt. Elle n'existait qu'en un seul exemplaire, sur le serveur, sous un chemin exclu par .gitignore — c'est pourtant elle qui force le réseau hôte des conteneurs de tâche, sans quoi rien ne démarre sur ce LXC. Un clone sur une machine neuve donnait un exécuteur actif qui ne lançait jamais rien, et rien ne le disait.

Un rôle Ansible ci_runner, calqué sur proxy, la dépose et la maintient alignée. Le fichier versionné est sémantiquement identique à celui en service : appliquer ce rôle sur le serveur actuel ne change rien.

Closes #141

Preuve

Le fichier en service a été récupéré et comparé clé à clé au fichier versionné :

$ ssh g2 'sudo cat /opt/g2-forge/data/runner/config.yml' > serveur.yml
$ python3 -c "import yaml; a=yaml.safe_load(open('serveur.yml')); \
    b=yaml.safe_load(open('infra/compose/forge/runner-config.yml')); \
    print('identique' if a==b else 'DIFFÉRENT')"
identique
Clés : cache, container, host, log, runner

Le banc d'essai échoue bien quand il doit — éprouvé sur cinq régressions volontaires, une à la fois :

--- réseau repassé en bridge ---
ÉCHEC  container.network n'est pas « host » : aucune tâche ne démarrera sur ce LXC.
--- force_pull repassé à true ---
ÉCHEC  container.force_pull n'est pas « false » : une image construite localement serait introuvable.
--- cache coupé ---
ÉCHEC  cache.enabled n'est pas « true » : les étapes actions/cache de ci.yml ne garderaient rien, en silence.
--- capacité ramenée à 1 ---
ÉCHEC  runner.capacity vaut 1 : les quatre tâches de la chaîne se feraient la queue une à une.
--- étiquette ci ajoutée au déploiement continu ---
ÉCHEC  deploy.yml joue l'étiquette « ci » : le déploiement continu redémarrerait l'exécuteur qui l'exécute.

ansible-lint passe au profil production, alors que moderate suffisait :

Passed: 0 failure(s), 0 warning(s) in 49 files processed of 54 encountered.
Profile 'moderate' was required, but 'production' profile passed.

Relecture

  • Un pair a relu et laissé un commentaire, même court
  • Ses remarques sont traitées, ou une réponse explique pourquoi elles ne le sont pas

Ce qui suit le code

  • docs/runbooks/ mis à jour, un geste d'exploitation a changé — ci.md gagne « Reconstruire la chaîne sur une machine vierge », forge.md §6 renvoie vers le rôle au lieu d'un geste manuel
  • Une nouvelle variable est apparue, elle est dans vault.yml.examplevault_forgejo_runner_token, nécessaire uniquement sur une cible neuve

Où regarder en priorité

Trois précautions, chacune pour une panne qu'on préfère ne pas vivre. C'est là que j'ai hésité.

  1. L'étiquette ci est hors de --tags app,proxy. Le déploiement continu s'exécute sur cet exécuteur : lui donner le droit de le redémarrer, c'est le faire se couper au milieu de son propre job, à chaque fusion sur main. Le banc refuse cette régression.
  2. Le rôle refuse de déposer tant qu'un FORGEJO-ACTIONS-TASK-* tourne. L'exécuteur ne relit sa configuration qu'au démarrage ; le redémarrer tue les tâches en vol, donc une demande de fusion rouge pour quelqu'un qui n'a rien demandé. -e ci_runner_forcer=true passe outre.
  3. capacity reste à 2, et le ticket ne la monte plus. Je proposais 3 au départ. Le relevé serveur montre qu'elle valait déjà 2, et surtout que la monter ne retirerait aucune vague : depuis la #133, python domine seule le chemin critique. On achèterait de la contention sur 4 cœurs pour zéro seconde.

Ce qui ne peut pas être versionné. .runner porte le jeton d'enregistrement : il reste hors dépôt (ENF-12). Les libellés y vivent aussi, ils sont donc déclarés dans ci_runner_labels et posés à l'enregistrement. Les changer exige un réenregistrement, pas un redémarrage — c'est dit dans le manuel.

Effet de bord. Ces libellés répondent à une question restée ouverte dans l'en-tête de ci.yml : le label docker vaut node:22-bookworm. Cette image a node et npm, et n'a pas pip — l'explication des exécutions #14 et #15, en échec silencieux. Le commentaire est corrigé, ce n'est plus une supposition.

Documentation périmée réparée au passage. docs/runbooks/ci.md annonçait encore six tâches et une absence de cache, deux affirmations que la #133 avait périmées. Les tableaux sont refaits sur les quatre tâches actuelles.

Conflits. Aucun fichier commun avec lenaic/136-planifier-silver, git merge-tree ne signale rien. Ordre de fusion indifférent.

## Ce que ça change La configuration de l'exécuteur Forgejo entre au dépôt. Elle n'existait qu'en un seul exemplaire, sur le serveur, sous un chemin exclu par `.gitignore` — c'est pourtant elle qui force le réseau hôte des conteneurs de tâche, sans quoi rien ne démarre sur ce LXC. Un clone sur une machine neuve donnait un exécuteur actif qui ne lançait jamais rien, et rien ne le disait. Un rôle Ansible `ci_runner`, calqué sur `proxy`, la dépose et la maintient alignée. Le fichier versionné est **sémantiquement identique à celui en service** : appliquer ce rôle sur le serveur actuel ne change rien. Closes #141 ## Preuve Le fichier en service a été récupéré et comparé clé à clé au fichier versionné : ``` $ ssh g2 'sudo cat /opt/g2-forge/data/runner/config.yml' > serveur.yml $ python3 -c "import yaml; a=yaml.safe_load(open('serveur.yml')); \ b=yaml.safe_load(open('infra/compose/forge/runner-config.yml')); \ print('identique' if a==b else 'DIFFÉRENT')" identique Clés : cache, container, host, log, runner ``` Le banc d'essai échoue bien quand il doit — éprouvé sur cinq régressions volontaires, une à la fois : ``` --- réseau repassé en bridge --- ÉCHEC container.network n'est pas « host » : aucune tâche ne démarrera sur ce LXC. --- force_pull repassé à true --- ÉCHEC container.force_pull n'est pas « false » : une image construite localement serait introuvable. --- cache coupé --- ÉCHEC cache.enabled n'est pas « true » : les étapes actions/cache de ci.yml ne garderaient rien, en silence. --- capacité ramenée à 1 --- ÉCHEC runner.capacity vaut 1 : les quatre tâches de la chaîne se feraient la queue une à une. --- étiquette ci ajoutée au déploiement continu --- ÉCHEC deploy.yml joue l'étiquette « ci » : le déploiement continu redémarrerait l'exécuteur qui l'exécute. ``` `ansible-lint` passe au profil `production`, alors que `moderate` suffisait : ``` Passed: 0 failure(s), 0 warning(s) in 49 files processed of 54 encountered. Profile 'moderate' was required, but 'production' profile passed. ``` ## Relecture - [ ] Un pair a relu et laissé un commentaire, même court - [ ] Ses remarques sont traitées, ou une réponse explique pourquoi elles ne le sont pas ## Ce qui suit le code - [x] `docs/runbooks/` mis à jour, un geste d'exploitation a changé — `ci.md` gagne « Reconstruire la chaîne sur une machine vierge », `forge.md` §6 renvoie vers le rôle au lieu d'un geste manuel - [x] Une nouvelle variable est apparue, elle est dans `vault.yml.example` — `vault_forgejo_runner_token`, nécessaire uniquement sur une cible neuve ## Où regarder en priorité **Trois précautions, chacune pour une panne qu'on préfère ne pas vivre. C'est là que j'ai hésité.** 1. **L'étiquette `ci` est hors de `--tags app,proxy`.** Le déploiement continu s'exécute *sur* cet exécuteur : lui donner le droit de le redémarrer, c'est le faire se couper au milieu de son propre job, à chaque fusion sur `main`. Le banc refuse cette régression. 2. **Le rôle refuse de déposer tant qu'un `FORGEJO-ACTIONS-TASK-*` tourne.** L'exécuteur ne relit sa configuration qu'au démarrage ; le redémarrer tue les tâches en vol, donc une demande de fusion rouge pour quelqu'un qui n'a rien demandé. `-e ci_runner_forcer=true` passe outre. 3. **`capacity` reste à 2, et le ticket ne la monte plus.** Je proposais 3 au départ. Le relevé serveur montre qu'elle valait déjà 2, et surtout que la monter ne retirerait aucune vague : depuis la #133, `python` domine seule le chemin critique. On achèterait de la contention sur 4 cœurs pour zéro seconde. **Ce qui ne peut pas être versionné.** `.runner` porte le jeton d'enregistrement : il reste hors dépôt (ENF-12). Les libellés y vivent aussi, ils sont donc déclarés dans `ci_runner_labels` et posés à l'enregistrement. Les changer exige un réenregistrement, pas un redémarrage — c'est dit dans le manuel. **Effet de bord.** Ces libellés répondent à une question restée ouverte dans l'en-tête de `ci.yml` : le label `docker` vaut `node:22-bookworm`. Cette image a node et npm, et n'a pas `pip` — l'explication des exécutions #14 et #15, en échec silencieux. Le commentaire est corrigé, ce n'est plus une supposition. **Documentation périmée réparée au passage.** `docs/runbooks/ci.md` annonçait encore six tâches et une absence de cache, deux affirmations que la #133 avait périmées. Les tableaux sont refaits sur les quatre tâches actuelles. **Conflits.** Aucun fichier commun avec `lenaic/136-planifier-silver`, `git merge-tree` ne signale rien. Ordre de fusion indifférent.
infra: la configuration de l'exécuteur entre au dépôt
All checks were successful
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m7s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 5s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 3m58s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m2s
ed1d33c93a
Elle n'existait qu'en un seul exemplaire, sur le serveur, sous
/opt/g2-forge/data/runner/config.yml — un chemin exclu par .gitignore. C'est
pourtant elle qui force le réseau hôte des conteneurs de tâche, sans quoi rien
ne démarre sur ce LXC où aucun bridge Docker ne monte. Un clone du dépôt sur
une machine neuve donnait donc un exécuteur qui s'enregistre, s'affiche actif,
et ne lance jamais rien : les demandes de fusion restaient « en attente », sans
message. Le dernier geste manuel non documenté du socle, et une entorse à
ENF-09.

Le fichier arrive dans infra/compose/forge/runner-config.yml, à côté de la
composition qui le monte, et le rôle ci_runner l'y dépose. Il est sémantiquement
identique à celui en service, vérifié clé à clé : appliquer ce rôle sur le
serveur actuel ne change rien, ce qui est exactement ce qu'on veut d'un premier
passage.

Le rôle suit ce que fait déjà proxy avec le Caddyfile : il ne possède pas la
pile de la forge, il tient un fichier. Il valide la configuration avant de la
déposer, sauvegarde la précédente, redémarre l'exécuteur et vérifie qu'il est
reparti. Sans différence de contenu, il ne fait rien.

Trois précautions, chacune pour une panne qu'on préfère ne pas vivre. L'exécuteur
ne relit sa configuration qu'au démarrage : le redémarrer tue les conteneurs de
tâche en vol, donc le rôle refuse tant qu'un FORGEJO-ACTIONS-TASK-* tourne
(ci_runner_forcer pour passer outre). L'étiquette « ci » reste hors de
« --tags app,proxy » : le déploiement continu s'exécute sur cet exécuteur et se
couperait lui-même au milieu de son propre job, à chaque fusion sur main. Et
capacity reste à 2 — la monter ne retirerait aucune vague, « python » dominant
seule le chemin critique depuis la #133, mais mettrait trois conteneurs de tâche
de plus sur quatre cœurs déjà chargés.

.runner ne peut pas suivre : il porte le jeton d'enregistrement. Les libellés,
qui y vivent aussi, sont donc déclarés côté rôle et posés à l'enregistrement.
Ils répondent au passage à une question laissée ouverte dans l'en-tête de
ci.yml : le label « docker » vaut node:22-bookworm. Cette image a bien node et
npm, et n'a pas pip — voilà l'explication des exécutions #14 et #15, en échec
silencieux. Ce n'est plus une supposition, et le commentaire le dit.

Un banc d'essai est ajouté, et son échec éprouvé sur cinq régressions
volontaires : réseau repassé en bridge, force_pull à true, cache coupé, capacité
ramenée à 1, étiquette « ci » ajoutée au déploiement continu. Il tourne dans la
tâche « Contrôles statiques du dépôt », donc sur node:22-bookworm : en bash,
grep et awk, sans interpréteur Python.

Le manuel gagne « Reconstruire la chaîne sur une machine vierge », et se
remet à jour au passage — il annonçait encore six tâches et une absence de
cache, deux affirmations que la #133 avait périmées.

Ticket #141.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011vnzPACD9TRhRTxDy2y14u
lenaic force-pushed lenaic/141-runner-reproductible from ed1d33c93a
All checks were successful
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m7s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 5s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 3m58s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m2s
to 5cd6403e67
All checks were successful
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m6s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 4s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 57s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 4m54s
2026-09-04 08:22:23 +00:00
Compare
lenaic requested reviews from marvin and removed review requests for justine 2026-09-04 09:31:34 +00:00
gabriel left a comment

Relecture. Le fond est bon : vrai problème, bien diagnostiqué, garde-fous sérieux (capacité figée à 2 avec justification mesurée, étiquette ci hors du déploiement continu, refus de déposer pendant une tâche en vol, banc de non-régression). Quatre points à traiter ou à assumer explicitement avant fusion, puis quelques mineurs.

À challenger

1. Périmètre. 745 lignes / 13 fichiers, dont ~180 de ci.md et l'en-tête de ci.yml qui sont du rattrapage documentaire de la #133, sans lien avec le runner. La PR le reconnaît (« réparée au passage »), mais ça alourdit la relecture et rend un revert impossible sans perdre la mise à jour doc. Deux commits séparés auraient suffi — à défaut, tant pis, mais autant en avoir conscience.

2. docker restart en dur dans infra/ansible/roles/ci_runner/handlers/main.yml au lieu de docker compose restart. Sur une cible neuve où la pile forge est clonée mais pas encore up, le handler échoue. Le reste du projet passe par docker compose (voir proxy, les runbooks). Cohérence + robustesse.

3. assert fragile dans tasks/main.yml : config.container.force_pull is false (idem config.cache.enabled is true, config.container.network == "host") plante sur une erreur « undefined » si la clé est absente de runner-config.yml, au lieu de rendre le fail_msg prévu. Ajouter is defined and devant chaque test.

4. Le chemin Ansible de l'assert n'a probablement jamais tourné pour de vrai. Les « cinq régressions volontaires » du corps de PR reprennent mot pour mot les messages de tests/ci/test-runner-reproductible.sh (le banc shell), pas les fail_msg du chemin slurp → from_yaml → assert du rôle. Un ansible-playbook site.yml --tags ci --check réel sur le serveur, joint en preuve, lèverait le doute.

Mineurs

  • test-runner-reproductible.sh : le check grep -qE -- '--tags[[:space:]]+[a-z,]*\bci\b' sur deploy.yml ne couvre que la forme longue --tags ; un futur passage à -t passerait à travers sans que le banc ne bronche.
  • test-runner-reproductible.sh suppose git présent sur node:22-bookworm (ok sur l'image full, pas slim) — c'est implicite, un mot dans l'en-tête du script ne coûterait rien.
  • tasks/main.yml lit le fichier deux fois (slurp pour le contenu, stat pour le checksum) : cosmétique.
  • meta/main.yml : min_ansible_version: "2.17" — vérifier que ça colle avec les autres rôles.

Rien de bloquant côté CI (verte). Les points 2 et 3 méritent une correction ; les points 1 et 4 peuvent être assumés par une réponse.

Relecture. Le fond est bon : vrai problème, bien diagnostiqué, garde-fous sérieux (capacité figée à 2 avec justification mesurée, étiquette `ci` hors du déploiement continu, refus de déposer pendant une tâche en vol, banc de non-régression). Quatre points à traiter ou à assumer explicitement avant fusion, puis quelques mineurs. ## À challenger **1. Périmètre.** 745 lignes / 13 fichiers, dont ~180 de `ci.md` et l'en-tête de `ci.yml` qui sont du rattrapage documentaire de la #133, sans lien avec le runner. La PR le reconnaît (« réparée au passage »), mais ça alourdit la relecture et rend un `revert` impossible sans perdre la mise à jour doc. Deux commits séparés auraient suffi — à défaut, tant pis, mais autant en avoir conscience. **2. `docker restart` en dur** dans `infra/ansible/roles/ci_runner/handlers/main.yml` au lieu de `docker compose restart`. Sur une cible neuve où la pile forge est clonée mais pas encore `up`, le handler échoue. Le reste du projet passe par `docker compose` (voir `proxy`, les runbooks). Cohérence + robustesse. **3. `assert` fragile** dans `tasks/main.yml` : `config.container.force_pull is false` (idem `config.cache.enabled is true`, `config.container.network == "host"`) plante sur une erreur « undefined » si la clé est absente de `runner-config.yml`, au lieu de rendre le `fail_msg` prévu. Ajouter `is defined and` devant chaque test. **4. Le chemin Ansible de l'assert n'a probablement jamais tourné pour de vrai.** Les « cinq régressions volontaires » du corps de PR reprennent mot pour mot les messages de `tests/ci/test-runner-reproductible.sh` (le banc shell), pas les `fail_msg` du chemin `slurp → from_yaml → assert` du rôle. Un `ansible-playbook site.yml --tags ci --check` réel sur le serveur, joint en preuve, lèverait le doute. ## Mineurs - `test-runner-reproductible.sh` : le check `grep -qE -- '--tags[[:space:]]+[a-z,]*\bci\b'` sur `deploy.yml` ne couvre que la forme longue `--tags` ; un futur passage à `-t` passerait à travers sans que le banc ne bronche. - `test-runner-reproductible.sh` suppose `git` présent sur `node:22-bookworm` (ok sur l'image *full*, pas *slim*) — c'est implicite, un mot dans l'en-tête du script ne coûterait rien. - `tasks/main.yml` lit le fichier deux fois (slurp pour le contenu, stat pour le checksum) : cosmétique. - `meta/main.yml` : `min_ansible_version: "2.17"` — vérifier que ça colle avec les autres rôles. Rien de bloquant côté CI (verte). Les points 2 et 3 méritent une correction ; les points 1 et 4 peuvent être assumés par une réponse.
infra: corriger la relecture de la #145
Some checks failed
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 4s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m22s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m10s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Failing after 5m32s
527afd4377
Deux défauts, deux mineurs, et un périmètre resserré.

Le handler redémarrait par `docker restart`, qui échoue sur une cible neuve où
la pile forge est clonée mais pas encore démarrée : le conteneur n'existe pas
encore, et la situation est pourtant normale. Compose part de la déclaration,
pas de l'existant, et c'est la voie qu'emprunte le reste du projet.

Les assertions de conformité lisaient des clés sans `is defined`. Une clé
absente de runner-config.yml levait une erreur « undefined » au lieu de rendre
le fail_msg, si bien que l'exploitant lisait une trace Jinja plutôt que la
marche à suivre, au moment où il en a le plus besoin.

Le banc surveille maintenant les deux, et rien ne les surveillait. Éprouvé dans
les deux sens : rouge sur `docker restart` remis, rouge sur un `is defined`
retiré, vert sur le rôle corrigé. Le contrôle sur les conditions a d'ailleurs
trouvé un cas que j'avais laissé, `capacity | int >= 2` sur sa propre ligne sans
garde.

Le périmètre de `docs/runbooks/ci.md` tombe de 150 lignes à 85, et ne porte plus
que la section « Reconstruire la chaîne sur une machine vierge ». Le reste était
le rattrapage documentaire de la #133, sans lien avec l'exécuteur : il est
traité par la #149, et c'est ce qui causait le conflit entre les deux demandes.

Mineurs : le contrôle sur l'étiquette `ci` couvre aussi la forme courte `-t`, et
l'en-tête du banc dit sa dépendance à git, absent de l'image node *slim*.

`min_ansible_version: "2.17"` est aligné sur les huit rôles, vérifié.

Sur le quatrième point, le chemin Ansible des assertions : je n'ai pas pu
l'éprouver en l'exécutant. Un banc qui lirait ces conditions dans le rôle pour
les faire évaluer se heurte au modèle de confiance d'ansible-core depuis 2.19,
qui refuse d'évaluer comme gabarit une chaîne lue dans un fichier, et le filtre
prévu pour lever ce marquage n'est pas exposé en 2.21.3. La garde est donc
statique, et le banc le dit plutôt que de le laisser croire.
Author
Owner

Les quatre points sont traités, et deux d'entre eux se répondaient.

Points 2 et 3 : de vrais défauts

docker restart échoue sur une cible neuve où la pile forge est clonée mais pas
encore démarrée, le conteneur n'existant pas encore alors que la situation est
normale. Passé par Compose, qui part de la déclaration et non de l'existant.

Les assertions lisaient des clés sans is defined : une clé absente levait une
trace Jinja au lieu du fail_msg qui dit quoi faire, au moment où on en a le
plus besoin.

Corrigés, et surtout le banc les surveille maintenant, alors que rien ne les
surveillait. Éprouvés dans les deux sens : rouge sur docker restart remis,
rouge sur un is defined retiré, vert sur le rôle corrigé.

Ton contrôle a d'ailleurs trouvé un cas que j'avais laissé en corrigeant :
config.runner.capacity | int >= 2 sur sa propre ligne, sans garde.

Point 1 : périmètre resserré

docs/runbooks/ci.md passe de 150 lignes à 85 et ne garde que la section
« Reconstruire la chaîne sur une machine vierge ». Le reste était le rattrapage
documentaire de la #133 que tu as identifié, et il est traité par la #149.

Effet de bord utile : ça supprime le conflit entre les deux demandes, qu'Olivier
avait relevé de son côté. Vérifié par fusion à blanc, les deux passent
maintenant dans n'importe quel ordre.

L'en-tête de ci.yml reste, en revanche : il dit que le libellé docker vaut
node:22-bookworm, ce qui n'est plus une supposition parce que les libellés
de l'exécuteur sont désormais lisibles dans le dépôt. C'est cette demande qui le
rend vrai.

Point 4 : tu avais raison, et je ne peux pas le satisfaire

Ce chemin n'avait jamais tourné, et il portait le défaut. Ton doute était fondé.

J'ai écrit le banc que tu demandais, qui lit les conditions dans le rôle et les
fait évaluer par Ansible plutôt que de les recopier. Il se heurte au modèle de
confiance d'ansible-core depuis 2.19 : une chaîne lue dans un fichier est
marquée non fiable et n'est pas évaluée comme gabarit. Le filtre prévu pour lever
ce marquage n'est pas exposé en 2.21.3, la version qu'on épingle. Et je n'ai pas
le mot de passe du coffre pour jouer site.yml --tags ci sur la cible.

La garde est donc statique : chaque condition qui lit une clé doit porter
is defined. Le banc écrit cette limite noir sur blanc plutôt que de laisser
croire qu'il éprouve un comportement.

Si tu peux jouer --tags ci --check sur le serveur avec le coffre, la sortie
jointe ici lèverait le dernier doute.

Mineurs

La forme courte -t est couverte. La dépendance à git, absent de l'image node
slim, est dite dans l'en-tête du banc. min_ansible_version est bien à
"2.17" sur les huit rôles, vérifié.

La double lecture slurp plus stat, je l'ai laissée : elle est cosmétique et la
toucher demanderait de repasser sur la logique d'idempotence, ce qui n'est pas le
bon rapport dans cette demande.

Les quatre points sont traités, et deux d'entre eux se répondaient. ### Points 2 et 3 : de vrais défauts `docker restart` échoue sur une cible neuve où la pile forge est clonée mais pas encore démarrée, le conteneur n'existant pas encore alors que la situation est normale. Passé par Compose, qui part de la déclaration et non de l'existant. Les assertions lisaient des clés sans `is defined` : une clé absente levait une trace Jinja au lieu du `fail_msg` qui dit quoi faire, au moment où on en a le plus besoin. Corrigés, et surtout **le banc les surveille maintenant**, alors que rien ne les surveillait. Éprouvés dans les deux sens : rouge sur `docker restart` remis, rouge sur un `is defined` retiré, vert sur le rôle corrigé. Ton contrôle a d'ailleurs trouvé un cas que j'avais laissé en corrigeant : `config.runner.capacity | int >= 2` sur sa propre ligne, sans garde. ### Point 1 : périmètre resserré `docs/runbooks/ci.md` passe de 150 lignes à 85 et ne garde que la section « Reconstruire la chaîne sur une machine vierge ». Le reste était le rattrapage documentaire de la #133 que tu as identifié, et il est traité par la #149. Effet de bord utile : ça supprime le conflit entre les deux demandes, qu'Olivier avait relevé de son côté. Vérifié par fusion à blanc, les deux passent maintenant dans n'importe quel ordre. L'en-tête de `ci.yml` reste, en revanche : il dit que le libellé `docker` vaut `node:22-bookworm`, ce qui n'est plus une supposition **parce que** les libellés de l'exécuteur sont désormais lisibles dans le dépôt. C'est cette demande qui le rend vrai. ### Point 4 : tu avais raison, et je ne peux pas le satisfaire Ce chemin n'avait jamais tourné, et il portait le défaut. Ton doute était fondé. J'ai écrit le banc que tu demandais, qui lit les conditions dans le rôle et les fait évaluer par Ansible plutôt que de les recopier. Il se heurte au modèle de confiance d'ansible-core depuis 2.19 : une chaîne lue dans un fichier est marquée non fiable et n'est pas évaluée comme gabarit. Le filtre prévu pour lever ce marquage n'est pas exposé en 2.21.3, la version qu'on épingle. Et je n'ai pas le mot de passe du coffre pour jouer `site.yml --tags ci` sur la cible. La garde est donc **statique** : chaque condition qui lit une clé doit porter `is defined`. Le banc écrit cette limite noir sur blanc plutôt que de laisser croire qu'il éprouve un comportement. Si tu peux jouer `--tags ci --check` sur le serveur avec le coffre, la sortie jointe ici lèverait le dernier doute. ### Mineurs La forme courte `-t` est couverte. La dépendance à `git`, absent de l'image node *slim*, est dite dans l'en-tête du banc. `min_ansible_version` est bien à `"2.17"` sur les huit rôles, vérifié. La double lecture slurp plus stat, je l'ai laissée : elle est cosmétique et la toucher demanderait de repasser sur la logique d'idempotence, ce qui n'est pas le bon rapport dans cette demande.
gabriel requested reviews from gabriel and removed review requests for marvin 2026-09-04 12:37:43 +00:00
Merge branch 'develop' into lenaic/141-runner-reproductible
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 5s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 19s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m10s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 2m53s
e7140fa05b
gabriel approved these changes 2026-09-04 12:43:32 +00:00
Dismissed
gabriel left a comment

Relecture des corrections. Les quatre points sont traités, et chaque correctif est désormais gardé par le banc — c'est le bon réflexe.

Point 2 — docker restart : le handler passe par docker compose -f {{ ci_runner_forge_dir }}/docker-compose.yml restart runner, même convention de chemin que le rôle proxy. Le banc rougit si on revient à docker restart.

Point 3 — assert fragile : chaque condition lisant une clé porte is defined and, gardes sur les clés parentes comprises, et le cas capacity | int >= 2 que j'avais laissé passer est inclus. Le banc compte config. contre is defined.

Point 1 — périmètre : ci.md resserré à une seule section sur le sujet, le rattrapage doc de la #133 renvoyé à la #149 — ce qui supprime au passage le conflit avec lenaic/136. L'en-tête de ci.yml reste, et la justification causale tient : c'est cette demande qui rend l'affirmation vraie.

Point 4 — chemin Ansible des assertions : la limite est assumée et écrite noir sur blanc dans le banc, plutôt que maquillée. La garde statique est un compromis correct. Si je peux jouer site.yml --tags ci --check avec le coffre je joindrai la sortie, mais ça ne bloque pas.

Mineurs tous traités : forme courte -t, dépendance à git hors image slim, min_ansible_version vérifié identique sur les huit rôles.

CI verte sur la tête, banc rejoué localement (14/14). Bon pour fusion.

Relecture des corrections. Les quatre points sont traités, et chaque correctif est désormais gardé par le banc — c'est le bon réflexe. **Point 2 — `docker restart`** : le handler passe par `docker compose -f {{ ci_runner_forge_dir }}/docker-compose.yml restart runner`, même convention de chemin que le rôle `proxy`. Le banc rougit si on revient à `docker restart`. **Point 3 — `assert` fragile** : chaque condition lisant une clé porte `is defined and`, gardes sur les clés parentes comprises, et le cas `capacity | int >= 2` que j'avais laissé passer est inclus. Le banc compte `config.` contre `is defined`. **Point 1 — périmètre** : `ci.md` resserré à une seule section sur le sujet, le rattrapage doc de la #133 renvoyé à la #149 — ce qui supprime au passage le conflit avec `lenaic/136`. L'en-tête de `ci.yml` reste, et la justification causale tient : c'est cette demande qui rend l'affirmation vraie. **Point 4 — chemin Ansible des assertions** : la limite est assumée et écrite noir sur blanc dans le banc, plutôt que maquillée. La garde statique est un compromis correct. Si je peux jouer `site.yml --tags ci --check` avec le coffre je joindrai la sortie, mais ça ne bloque pas. Mineurs tous traités : forme courte `-t`, dépendance à `git` hors image *slim*, `min_ansible_version` vérifié identique sur les huit rôles. CI verte sur la tête, banc rejoué localement (14/14). Bon pour fusion.
gabriel approved these changes 2026-09-04 12:44:02 +00:00
gabriel merged commit 8f5c4275a4 into develop 2026-09-04 12:44:07 +00:00
gabriel deleted branch lenaic/141-runner-reproductible 2026-09-04 12:44:07 +00:00
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
g2/enervision!145
No description provided.