⚠ CORRIGE LE PREMIER JET DE CETTE BRANCHE, qui épinglait `gitea/act_runner:0.3.1`.
`gitea/act_runner` est GELÉE à 0.6.1. Le successeur officiel est `gitea/runner`
(binaire renommé `act_runner` → `gitea-runner`), aujourd'hui en **2.3.0**.
Épingler l'ancien nom nous aurait enfermés dans une image morte — trouvé grâce
aux notes de version de Gitea 1.27 signalées par le fondateur.
VÉRIFIÉ AVANT DE BASCULER — c'est un remplacement DIRECT pour ce compose :
• entrypoint identique : /sbin/tini -- run.sh
• mêmes variables lues : CONFIG_FILE, GITEA_INSTANCE_URL,
GITEA_RUNNER_{REGISTRATION_TOKEN,NAME,LABELS}
• config.yaml compatible : capacity, labels, cache.*, container.force_pull,
options, valid_volumes, host.workdir_parent — AUCUNE clé utilisée ici n'a
disparu (comparé au `gitea-runner generate-config` de la 2.3.0)
La 2.3.0 apporte en prime des réglages qui parlent à nos pannes connues :
`health_check.min_free_disk_space_mb` (les images de runner supprimées quand le
disque se remplit, ADR 20260407) et `state_report_interval` (les tâches tuées en
zombie faute de rapport, factory#50).
GITEA 1.25.5 → 1.27.1 : deux versions mineures, migrations de base
IRRÉVERSIBLES. Sauvegardes du jour VÉRIFIÉES avant, pas supposées :
/mnt/backups/postgres/backup_20260730.sql.gz 13 Mo, gzip -t OK,
contient « CREATE DATABASE gitea » (pg_dumpall)
/mnt/backups/gitea/backup_20260730.gitea.gz 1,7 Go, gzip -t OK
⚠ Le backup Gitea utilise `gitea dump --skip-db` : il ne contient PAS la base.
C'est le dump postgres qui la porte — les deux sont nécessaires.
Changements cassants de 1.27 et leur portée ici, vérifiée :
• workflows réutilisables externes retirés → AUCUN dans front, kadans-api,
factory (contrôlé programmatiquement, `uses:` au niveau job)
• nonce CSP pour scripts inline → concerne les templates personnalisés, nous
n'en avons pas
• X-Content-Type-Options: nosniff par défaut
Refs arcodange-org/factory#50
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Réponse à « qu'est-ce qui nous empêche d'upgrade des deux côtés ? » : rien.
Le playbook déployait `gitea/act_runner:latest` avec `pull: missing`, c'est-à-dire
la pire combinaison possible — un tag FLOTTANT qui n'est JAMAIS rafraîchi. Chaque
hôte garde donc ce que « latest » voulait dire le jour de son premier pull :
pi1 : sha256:7bdc8d31… → v0.3.1
pi3 : sha256:0f65fa10… → v0.2.13
Deux machines censées être équivalentes, deux versions à trois mineures d'écart.
Effets mesurés : le MÊME job, sur la MÊME image de CI, met 511 s sur pi1 et
397 s sur pi3 (114 s d'écart imputables à la machine) ; et pi3 a mal lu la
définition d'un job dont il dépendait (« 'runs-on' key not defined », puis
« No steps found »).
⚠ POURQUOI PAS `latest` + `pull: always`. `latest` vaut aujourd'hui **0.6.1**
(Docker Hub, 30/04/2026), soit 3 à 4 versions mineures devant tout ce qui est
éprouvé ici. Le runner exécute TOUTE la CI de la forge : une montée subie, non
datée et non choisie s'y paie cher. On épingle donc, et on monte délibérément.
⚠ POURQUOI 0.3.1 ET PAS 0.6.1. 0.3.1 est la version que pi1 exécute DÉJÀ avec
succès sur cette forge. Ce changement aligne donc pi3 VERS LE HAUT, sur du
prouvé, sans saut de quatre versions. Passer ensuite à 0.6.1 devient une
modification d'UNE ligne, datée et reculable — c'est tout l'intérêt de la
variable.
⚠ Et `pull: missing` redevient CORRECT avec un tag épinglé : changer la version
change le tag, donc l'image est absente, donc elle est tirée. Aucun besoin de
`pull: always`, qui interrogerait le registre à chaque passage pour rien.
⚠ NE PAS jouer ce playbook pendant qu'une CI tourne : il recrée les conteneurs
de runner et TUE les jobs en vol (journaux perdus). Vérifier `list_runs` avant —
et se rappeler qu'un merge est un déclencheur.
Refs arcodange-org/factory#50
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Les jobs CI du dépôt kadans réinstallent, à CHAQUE exécution, des choses qui
changent tous les trimestres. Mesuré le 2026-07-29 sur le run 628 (runner
ARM64, 2 vCPU / 3 Gio) :
npm install -g bun → 8,6 s
playwright install --with-deps chromium → 104,0 s (apt-get, à chaque run)
────────
112,6 s jetées par run, sur le
job qui EST le chemin critique
Le cache `actions/cache` ne peut rien contre ces 104 s : il couvre le NAVIGATEUR
(299 Mo déjà mis en cache), pas ses dépendances SYSTÈME — `--with-deps` relance
apt quoi qu'il arrive.
POURQUOI CONSTRUIRE ICI PLUTÔT QUE POUSSER UNE IMAGE. La tentative de publier
l'image au registre a échoué (kadans#224 puis #225) : 3,81 Go dont une couche
unique de 1,36 Go, `docker push` casse en « connection reset by peer » — 7
couches passent, 3 sont retentées 50 fois puis abandonnées. À titre de
comparaison, runner-images:ubuntu-latest-ca (534,7 Mo, plus grosse couche
261 Mo) passe sans problème : la limite est entre 261 Mo et ~500 Mo par couche.
Construire localement supprime le problème — aucune couche ne traverse le
réseau.
Et c'est bien sur CHAQUE machine : avec `capacity: 1`, le parallélisme vient de
plusieurs Raspberry, et `container:` est résolu par le runner. Un job qui
atterrit là où l'image manque échoue AVANT sa première étape.
Trois choix de conception :
1. L'image hérite de runner-images:ubuntu-latest-ca, donc du certificat de la CA
interne (step-ca). Repartir de node:20-bookworm obligerait à réinjecter le CA
à la main, et tout job parlant à gitea.arcodange.lab échouerait en TLS.
2. TROIS `RUN` séparés (bun / dépendances système / navigateur), délibérément.
La version qui a échoué faisait une couche de 1,36 Go. Ne pas les fusionner
pour « gagner une couche ».
3. L'image est ÉPINGLÉE par un conteneur factice — ce qui implémente enfin la
section 1 de docs/adr/20260407-docker-storage-gitea-runner.md, restée à
l'état de proposition : system_docker.yml n'applique que le data-root sur
disque externe et les log-opts. Sans épinglage, le ramasse-miettes de Docker
supprime l'image dès que le disque se remplit — panne déjà constatée sur les
images de runner elles-mêmes.
Le rôle vérifie sa sortie (`docker run … bun --version`) au lieu de supposer que
le build a suffi, et l'image écrit ses versions dans /etc/ci-base.versions pour
que la CI de kadans puisse les confronter à son bun.lock et échouer FORT sur une
dérive, plutôt que de la découvrir en « Executable doesn't exist ».
Nouveau label runner `ci-node-playwright`. `container.force_pull: false` est
déjà en place et devient REQUIS pour ce label : sans lui, act_runner tenterait
un pull d'une image qui n'est dans aucun registre.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Incident 2026-07-23: an uncapped nuxt generate (3.5G RSS) on pi1 starved the
k3s control-plane and traefik (load >150, no swap, no OOM-kill) — every
*.arcodange.lab endpoint went dark, Gitea included, while Gitea itself was
healthy on pi2. Job containers are spawned via the host docker socket, so
cgroup caps on the job container are the only guardrail.
Applied live on pi1+pi3 via 03_cicd.yml on 2026-07-24 (both runners
re-registered; pi3's runner was down and is back in service).
Co-Authored-By: Claude Fable 5 <[email protected]>
Removes the commented PACKAGES_TOKEN/HOMELAB_CA_CERT blocks and the legacy
"Deploy Argo CD" play that were left behind during the migration to
Helm-based ArgoCD.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Pins the actcache server to a fixed port (43707) and exposes it, then
mounts /mnt/arcodange/gitea-runner-cache and /mnt/arcodange/gitea-runner-act
into the runner so the actions/cache and act image layer cache survive
container restarts. Moves the runner onto a dedicated `gitea_action_network`
so CI job containers can reach the cache server by name without sharing the
host network.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>