From fdecabf8caff22ea491647f093bf1d9913684e27 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Wed, 29 Jul 2026 20:23:59 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(ci):=20construire=20l'image=20des=20jo?= =?UTF-8?q?bs=20CI=20lourds=20sur=20chaque=20machine=20=C3=A0=20runner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../arcodange/factory/playbooks/03_cicd.yml | 18 ++++- .../roles/ci_base_image/defaults/main.yml | 43 ++++++++++++ .../roles/ci_base_image/files/Dockerfile | 65 +++++++++++++++++++ .../roles/ci_base_image/tasks/main.yml | 53 +++++++++++++++ 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 ansible/arcodange/factory/roles/ci_base_image/defaults/main.yml create mode 100644 ansible/arcodange/factory/roles/ci_base_image/files/Dockerfile create mode 100644 ansible/arcodange/factory/roles/ci_base_image/tasks/main.yml diff --git a/ansible/arcodange/factory/playbooks/03_cicd.yml b/ansible/arcodange/factory/playbooks/03_cicd.yml index f274e65..353bc7e 100644 --- a/ansible/arcodange/factory/playbooks/03_cicd.yml +++ b/ansible/arcodange/factory/playbooks/03_cicd.yml @@ -5,6 +5,13 @@ roles: - arcodange.factory.gitea_token # generate gitea_api_token used to replace generated token with set name if required + # Image de base des jobs CI lourds (Node + Bun + Chromium), construite ICI, + # sur chaque machine à runner, puis épinglée contre le ramasse-miettes Docker. + # Le même groupe d'hôtes que le runner, et ce n'est pas un détail : avec + # `capacity: 1` (ci-dessous), le parallélisme vient de PLUSIEURS machines, et + # `container:` est résolu par le runner — un job qui atterrit là où l'image + # manque échoue AVANT sa première étape. + - arcodange.factory.ci_base_image tasks: @@ -32,7 +39,7 @@ http://{{ hostvars[groups.gitea[0]].ansible_host }}:3000 GITEA_RUNNER_REGISTRATION_TOKEN: "{{ gitea_runner_token_cmd.stdout }}" GITEA_RUNNER_NAME: arcodange_global_runner_{{ inventory_hostname }} - GITEA_RUNNER_LABELS: ubuntu-latest:docker://gitea.arcodange.lab/arcodange-org/runner-images:ubuntu-latest-ca,ubuntu-latest-ca:docker://gitea.arcodange.lab/arcodange-org/runner-images:ubuntu-latest-ca + GITEA_RUNNER_LABELS: ubuntu-latest:docker://gitea.arcodange.lab/arcodange-org/runner-images:ubuntu-latest-ca,ubuntu-latest-ca:docker://gitea.arcodange.lab/arcodange-org/runner-images:ubuntu-latest-ca,ci-node-playwright:docker://ci-node-playwright:latest ports: - "43707:43707" networks: @@ -91,6 +98,15 @@ labels: - "ubuntu-latest:docker://gitea.arcodange.lab/arcodange-org/runner-images:ubuntu-latest-ca" - "ubuntu-latest-ca:docker://gitea.arcodange.lab/arcodange-org/runner-images:ubuntu-latest-ca" + # Jobs CI lourds (Node + Bun + Chromium préinstallés) — + # image construite LOCALEMENT par le rôle ci_base_image, sur + # cette machine. Elle n'est volontairement PAS dans le + # registre : 3,81 Go dont une couche de 1,36 Go, dont le + # push casse en « connection reset by peer » (mesuré + # 2026-07-29, kadans#225). `force_pull: false` ci-dessous + # est donc REQUIS pour ce label — sans lui, act_runner + # tenterait un pull et échouerait. + - "ci-node-playwright:docker://ci-node-playwright:latest" cache: # Enable cache server to use actions/cache. diff --git a/ansible/arcodange/factory/roles/ci_base_image/defaults/main.yml b/ansible/arcodange/factory/roles/ci_base_image/defaults/main.yml new file mode 100644 index 0000000..39ce6f2 --- /dev/null +++ b/ansible/arcodange/factory/roles/ci_base_image/defaults/main.yml @@ -0,0 +1,43 @@ +--- +# Image de base des jobs CI lourds (Node + Bun + Chromium), construite SUR CHAQUE +# machine qui héberge un runner Gitea. +# +# POURQUOI CONSTRUIRE PLUTÔT QUE POUSSER (mesuré le 2026-07-29, kadans#224/#225) : +# une image Node + Playwright + Chromium pèse 3,81 Go, avec une couche unique de +# 1,36 Go. Son `docker push` vers gitea.arcodange.lab casse en +# « connection reset by peer » : 7 couches passent, 3 sont réinitialisées et +# retentées 50 fois avant abandon. À titre de comparaison, +# `runner-images:ubuntu-latest-ca` (534,7 Mo, plus grosse couche 261 Mo) passe +# sans problème — la limite est donc entre 261 Mo et ~500 Mo par couche. +# +# Construire localement supprime le problème : aucune couche ne traverse le +# réseau. Et comme `capacity: 1` par runner (03_cicd.yml), le parallélisme vient +# de PLUSIEURS machines — l'image doit donc exister sur CHACUNE d'elles, ce que +# ce rôle garantit. + +# On hérite de l'image de runner maison : elle porte DÉJÀ le certificat de la CA +# interne (step-ca). Repartir de `node:20-bookworm` obligerait à réinjecter le CA +# à la main, et un job qui parle à gitea.arcodange.lab échouerait en TLS. +ci_base_image_from: gitea.arcodange.lab/arcodange-org/runner-images:ubuntu-latest-ca + +ci_base_image_name: ci-node-playwright +ci_base_image_tag: latest + +# ⚠ Ces versions doivent suivre le `bun.lock` du dépôt kadans. Le dépôt s'en +# protège : l'image écrit ce qu'elle a cuit dans /etc/ci-base.versions, et la CI +# de kadans CONFRONTE ce fichier à son lockfile pour échouer FORT plutôt que de +# dériver en silence (des navigateurs qui ne correspondent plus au client +# Playwright donnent « Executable doesn't exist », loin de la cause). +ci_base_image_bun_version: '1.3.14' +ci_base_image_playwright_version: '1.61.1' + +# Épinglage par conteneur factice — remède décrit par +# docs/adr/20260407-docker-storage-gitea-runner.md §1, jusqu'ici resté à l'état +# de proposition (system_docker.yml n'applique que le data-root et les log-opts). +# Sans lui, le ramasse-miettes de Docker supprime l'image dès que le disque se +# remplit, et la CI casse sur une image manquante — panne déjà constatée sur les +# images de runner elles-mêmes. +ci_base_image_pin: true + +# Reconstruire même si l'image existe déjà (utile après un changement de version). +ci_base_image_force_rebuild: false diff --git a/ansible/arcodange/factory/roles/ci_base_image/files/Dockerfile b/ansible/arcodange/factory/roles/ci_base_image/files/Dockerfile new file mode 100644 index 0000000..ea85c29 --- /dev/null +++ b/ansible/arcodange/factory/roles/ci_base_image/files/Dockerfile @@ -0,0 +1,65 @@ +# Image de base des jobs CI lourds — construite SUR CHAQUE machine à runner. +# +# Elle cuit une fois pour toutes ce que chaque exécution de CI réinstallait. +# Mesures du dépôt kadans (run 628, 2026-07-29, runner ARM64 2 vCPU / 3 Gio) : +# +# npm install -g bun → 8,6 s par run +# playwright install --with-deps chromium → 104,0 s par run (apt-get) +# ──────── +# 112,6 s jetées à CHAQUE run, +# sur le job du 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-get` quoi qu'il arrive. +# +# ⚠ ON HÉRITE DE L'IMAGE DE RUNNER MAISON, ET C'EST ESSENTIEL : elle porte le +# certificat de la CA interne (step-ca). Une image repartant de `node:20-bookworm` +# ne ferait pas confiance à gitea.arcodange.lab, et tout job qui lui parle +# échouerait en TLS. +ARG CI_BASE_FROM=gitea.arcodange.lab/arcodange-org/runner-images:ubuntu-latest-ca +FROM ${CI_BASE_FROM} + +ARG BUN_VERSION=1.3.14 +ARG PLAYWRIGHT_VERSION=1.61.1 + +ENV DEBIAN_FRONTEND=noninteractive + +# Chemin des navigateurs, figé et hors du HOME : un job qui tourne sous un autre +# utilisateur doit les retrouver. +ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright + +# ══════════════════════════════════════════════════════════════════════════ +# ⚠ TROIS `RUN` SÉPARÉS, ET C'EST LE POINT DE CONCEPTION DE CE FICHIER. +# +# La première version faisait `npm i -g bun` puis `playwright install --with-deps` +# en deux couches, dont une de 1,36 Go — irrecevable par le registre. Ici, même +# si l'on décidait un jour de pousser cette image, chaque couche reste du même +# ordre de grandeur que celles qui passent déjà (261 Mo pour la plus grosse de +# `runner-images:ubuntu-latest-ca`). +# +# Ne pas fusionner ces `RUN` pour « gagner une couche » : le gain serait nul et +# la couche redeviendrait impossible à transporter. +# ══════════════════════════════════════════════════════════════════════════ + +# 1. Bun (~274 Mo) +RUN npm install -g "bun@${BUN_VERSION}" \ + && npm cache clean --force + +# 2. Les dépendances SYSTÈME de Chromium — c'est CETTE couche qui rachète les +# 104 s d'apt-get de chaque run. +RUN npx --yes "playwright@${PLAYWRIGHT_VERSION}" install-deps chromium \ + && rm -rf /var/lib/apt/lists/* + +# 3. Le navigateur lui-même, séparé de ses dépendances système : les deux ne +# bougent pas au même rythme, et Docker ne réinvalide alors que la bonne. +RUN npx --yes "playwright@${PLAYWRIGHT_VERSION}" install chromium + +# La trace opposable de ce qui est réellement cuit ici. La CI de kadans la LIT et +# la confronte à son `bun.lock` : une dérive de version doit échouer FORT, avec sa +# cause, plutôt que de se manifester par un « Executable doesn't exist » à +# vingt minutes de là. +RUN printf 'bun=%s\nplaywright=%s\n' \ + "$(bun --version)" "${PLAYWRIGHT_VERSION}" \ + > /etc/ci-base.versions \ + && cat /etc/ci-base.versions diff --git a/ansible/arcodange/factory/roles/ci_base_image/tasks/main.yml b/ansible/arcodange/factory/roles/ci_base_image/tasks/main.yml new file mode 100644 index 0000000..427459c --- /dev/null +++ b/ansible/arcodange/factory/roles/ci_base_image/tasks/main.yml @@ -0,0 +1,53 @@ +--- +# Construit l'image de base des jobs CI sur la machine courante, puis l'épingle. +# +# À exécuter sur les MÊMES hôtes que le runner Gitea (03_cicd.yml) : comme +# `capacity: 1`, le parallélisme vient de plusieurs machines, et un job qui +# atterrit sur une machine sans l'image échouerait AVANT sa première étape — +# `runs-on`/`container:` est résolu par le runner, pas par le workflow. + +- name: Construire {{ ci_base_image_name }}:{{ ci_base_image_tag }} + community.docker.docker_image_build: + name: '{{ ci_base_image_name }}' + tag: '{{ ci_base_image_tag }}' + path: '{{ role_path }}/files/' + rebuild: '{{ "always" if ci_base_image_force_rebuild else "never" }}' + args: + CI_BASE_FROM: '{{ ci_base_image_from }}' + BUN_VERSION: '{{ ci_base_image_bun_version }}' + PLAYWRIGHT_VERSION: '{{ ci_base_image_playwright_version }}' + register: ci_base_image_build + +# ⚠ CE CONTENEUR NE TOURNE JAMAIS — il ne sert qu'à référencer l'image. +# Remède décrit par docs/adr/20260407-docker-storage-gitea-runner.md §1, resté +# jusqu'ici à 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, et qui casse la CI de tous les dépôts. +# +# `state: present` (et non `started`) : Docker considère l'image comme utilisée +# tant qu'un conteneur la référence, même à l'arrêt. Aucun CPU, aucune mémoire. +- name: Épingler {{ ci_base_image_name }} contre le ramasse-miettes Docker + community.docker.docker_container: + name: 'pin-{{ ci_base_image_name }}' + image: '{{ ci_base_image_name }}:{{ ci_base_image_tag }}' + state: present + command: ['sh', '-c', 'sleep infinity'] + auto_remove: false + restart_policy: 'no' + when: ci_base_image_pin + +# Contrôle de sortie : on VÉRIFIE que l'image répond, plutôt que de supposer que +# le build a suffi. Une image construite mais dont `bun` n'est pas dans le PATH +# passerait le build et casserait tous les jobs. +- name: Vérifier que l'image livre bien bun et chromium + ansible.builtin.command: + cmd: >- + docker run --rm {{ ci_base_image_name }}:{{ ci_base_image_tag }} + sh -c "bun --version && cat /etc/ci-base.versions" + register: ci_base_image_check + changed_when: false + +- name: Ce que l'image contient réellement + ansible.builtin.debug: + var: ci_base_image_check.stdout_lines From 9f438c4968319b742968f59ab94561772fcf2ee5 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Wed, 29 Jul 2026 23:09:14 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix(ci=5Fbase=5Fimage)=20=E2=80=94=20l'imag?= =?UTF-8?q?e=20livrait=20Node=2018=20:=20deux=20d=C3=A9fauts=20que=20seule?= =?UTF-8?q?=20sa=20CONSTRUCTION=20a=20montr=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit J'avais écrit dans cette PR « je n'ai pas pu construire l'image, sa base vit derrière le certificat interne ». C'était une SUPPOSITION NON TESTÉE, et elle est fausse : `docker pull gitea.arcodange.lab/…/runner-images:ubuntu-latest-ca` passe sans rien configurer. En la construisant vraiment, deux défauts sont sortis — et aucun n'était visible à la lecture du Dockerfile. 1. `runner-images:ubuntu-latest-ca` LIVRE NODE 18 (v18.20.8). Or `nuxi` importe `node:util.styleText`, absent de Node 18 : c'est la raison d'être du `container: node:20-bookworm` de la CI de kadans, que son CLAUDE.md interdit de retirer. Sans correctif, basculer la CI sur cette image cassait `nuxt build` sur un message parlant d'un import introuvable — jamais d'une version de Node. → Node 20 installé depuis NodeSource. 2. ET INSTALLER NE SUFFISAIT PAS. Après l'installation, `node --version` rendait TOUJOURS v18.20.8 : l'image de base précuit un node pour le toolcache d'act et le met EN TÊTE du PATH. which node → /opt/acttoolcache/node/18.20.8/arm64/bin/node /usr/bin/node --version → v20.20.2 ← le bon, mais il PERD → l'entrée 18 du toolcache est retirée ; la résolution retombe sur /usr/bin/node. ⚠ Conséquence assumée : `actions/setup-node` ne trouvera plus de Node 18 préinstallé — aucun workflow de kadans ne l'utilise, et l'image n'est servie qu'aux jobs qui DEMANDENT le label. Le Dockerfile porte désormais une ASSERTION DE BUILD (`node --version | grep -q "^v${NODE_MAJOR}\."`) : l'image ne peut plus se construire si la résolution redevient mauvaise. Et le rôle vérifie la version au déploiement (`failed_when`), au lieu de la supposer. MESURES RÉELLES (construite en linux/arm64, l'architecture des runners) : TOTAL 4,58 Go ├─ playwright install chromium 1,01 Go ├─ playwright install-deps 405 Mo ├─ Node 20 (NodeSource) 183 Mo └─ bun 179 Mo Vérifié dans l'image : which node → /usr/bin/node v20.20.2 · bun 1.3.14 · chromium-1228 + headless-shell + ffmpeg · /etc/ci-base.versions cohérent. ⚠ Ce que ces chiffres tranchent : le découpage en RUN séparés N'A PAS suffi à rendre l'image poussable — 1,01 Go pour la plus grosse couche, soit ~4× les 261 Mo que le registre accepte (runner-images:ubuntu-latest-ca). Le build LOCAL n'est donc pas une préférence, c'est la seule voie. Mesuré, plus supposé. Co-Authored-By: Claude Opus 5 (1M context) --- .../roles/ci_base_image/defaults/main.yml | 9 ++++ .../roles/ci_base_image/files/Dockerfile | 42 +++++++++++++++++-- .../roles/ci_base_image/tasks/main.yml | 11 ++++- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/ansible/arcodange/factory/roles/ci_base_image/defaults/main.yml b/ansible/arcodange/factory/roles/ci_base_image/defaults/main.yml index 39ce6f2..e242e9a 100644 --- a/ansible/arcodange/factory/roles/ci_base_image/defaults/main.yml +++ b/ansible/arcodange/factory/roles/ci_base_image/defaults/main.yml @@ -31,6 +31,15 @@ ci_base_image_tag: latest ci_base_image_bun_version: '1.3.14' ci_base_image_playwright_version: '1.61.1' +# ⚠ NODE 20 EST OBLIGATOIRE, ET CE N'EST PAS UN CONFORT. +# `runner-images:ubuntu-latest-ca` livre Node **18** (v18.20.8, constaté en +# lançant l'image). Or `nuxi` importe `node:util.styleText`, absent de Node 18 : +# c'est la raison d'être du `container: node:20-bookworm` que portait la CI de +# kadans, et que son CLAUDE.md interdit de retirer. Sans cette surcharge, tout +# job Nuxt basculé sur cette image casse au premier build, sur un message qui +# parle d'un import introuvable et jamais d'une version de Node. +ci_base_image_node_major: 20 + # Épinglage par conteneur factice — remède décrit par # docs/adr/20260407-docker-storage-gitea-runner.md §1, jusqu'ici resté à l'état # de proposition (system_docker.yml n'applique que le data-root et les log-opts). diff --git a/ansible/arcodange/factory/roles/ci_base_image/files/Dockerfile b/ansible/arcodange/factory/roles/ci_base_image/files/Dockerfile index ea85c29..560aba0 100644 --- a/ansible/arcodange/factory/roles/ci_base_image/files/Dockerfile +++ b/ansible/arcodange/factory/roles/ci_base_image/files/Dockerfile @@ -42,7 +42,43 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright # la couche redeviendrait impossible à transporter. # ══════════════════════════════════════════════════════════════════════════ -# 1. Bun (~274 Mo) +# 0. NODE 20, ET C'EST OBLIGATOIRE — pas une préférence. +# +# ⚠ `runner-images:ubuntu-latest-ca` livre **Node 18** (v18.20.8, vérifié en le +# lançant). Or `nuxi` importe `node:util.styleText`, ABSENT de Node 18 : c'est la +# raison d'être du `container: node:20-bookworm` que la CI de kadans portait, et +# que son `CLAUDE.md` interdit explicitement de retirer. +# +# Sans cette couche, une CI qui bascule sur cette image casse au premier `nuxt +# build` — et le message parle d'un import introuvable, pas d'une version de Node. +# Le défaut a été trouvé en CONSTRUISANT l'image puis en lançant `node --version` +# dedans ; aucune lecture du Dockerfile ne l'aurait montré. +# ⚠⚠ ET INSTALLER NE SUFFIT PAS — il faut aussi que `node` RÉSOLVE vers le bon. +# Constaté en lançant l'image : après l'installation de Node 20 par apt, +# `node --version` rendait toujours **v18.20.8**, parce que l'image de base +# précuit un node pour le toolcache d'act et le met EN TÊTE du PATH : +# +# which node → /opt/acttoolcache/node/18.20.8/arm64/bin/node +# PATH → /opt/acttoolcache/node/18.20.8/arm64/bin:/usr/local/sbin:/usr/bin:… +# /usr/bin/node --version → v20.20.2 ← le bon, mais il PERD +# +# On retire donc l'entrée 18 du toolcache : le segment de PATH devient inexistant +# (inoffensif) et la résolution retombe sur /usr/bin/node, en 20. +# ⚠ Conséquence assumée : `actions/setup-node` ne trouvera plus de Node 18 +# préinstallé dans cette image. Aucun workflow de kadans ne l'utilise, et l'image +# n'est servie qu'aux jobs qui DEMANDENT le label `ci-node-playwright`. +ARG NODE_MAJOR=20 +RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" -o /tmp/nodesource.sh \ + && bash /tmp/nodesource.sh \ + && apt-get install -y --no-install-recommends nodejs \ + && rm -f /tmp/nodesource.sh \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /opt/acttoolcache/node \ + && echo "node résolu : $(which node) $(node --version)" \ + && node --version | grep -q "^v${NODE_MAJOR}\." + +# 1. Bun (~180 Mo) — installé APRÈS Node 20, pour que son npm global soit celui +# de Node 20 et non celui de Node 18. RUN npm install -g "bun@${BUN_VERSION}" \ && npm cache clean --force @@ -59,7 +95,7 @@ RUN npx --yes "playwright@${PLAYWRIGHT_VERSION}" install chromium # la confronte à son `bun.lock` : une dérive de version doit échouer FORT, avec sa # cause, plutôt que de se manifester par un « Executable doesn't exist » à # vingt minutes de là. -RUN printf 'bun=%s\nplaywright=%s\n' \ - "$(bun --version)" "${PLAYWRIGHT_VERSION}" \ +RUN printf 'node=%s\nbun=%s\nplaywright=%s\n' \ + "$(node --version)" "$(bun --version)" "${PLAYWRIGHT_VERSION}" \ > /etc/ci-base.versions \ && cat /etc/ci-base.versions diff --git a/ansible/arcodange/factory/roles/ci_base_image/tasks/main.yml b/ansible/arcodange/factory/roles/ci_base_image/tasks/main.yml index 427459c..5ac76f2 100644 --- a/ansible/arcodange/factory/roles/ci_base_image/tasks/main.yml +++ b/ansible/arcodange/factory/roles/ci_base_image/tasks/main.yml @@ -14,6 +14,7 @@ rebuild: '{{ "always" if ci_base_image_force_rebuild else "never" }}' args: CI_BASE_FROM: '{{ ci_base_image_from }}' + NODE_MAJOR: '{{ ci_base_image_node_major }}' BUN_VERSION: '{{ ci_base_image_bun_version }}' PLAYWRIGHT_VERSION: '{{ ci_base_image_playwright_version }}' register: ci_base_image_build @@ -40,13 +41,19 @@ # Contrôle de sortie : on VÉRIFIE que l'image répond, plutôt que de supposer que # le build a suffi. Une image construite mais dont `bun` n'est pas dans le PATH # passerait le build et casserait tous les jobs. -- name: Vérifier que l'image livre bien bun et chromium +- name: Vérifier que l'image livre bien Node {{ ci_base_image_node_major }}, bun et chromium ansible.builtin.command: cmd: >- docker run --rm {{ ci_base_image_name }}:{{ ci_base_image_tag }} - sh -c "bun --version && cat /etc/ci-base.versions" + sh -c "node --version && bun --version && ls /ms-playwright && cat /etc/ci-base.versions" register: ci_base_image_check changed_when: false + # ⚠ La version de Node est VÉRIFIÉE, pas supposée : l'image de base en livre + # une trop ancienne (18), et une régression silencieuse ici casserait tout job + # Nuxt sur un message qui ne nomme pas la cause. + failed_when: >- + ci_base_image_check.rc != 0 + or ('v' ~ ci_base_image_node_major ~ '.') not in ci_base_image_check.stdout - name: Ce que l'image contient réellement ansible.builtin.debug: