fix(ci_base_image) — le contexte de build doit être SUR la machine, pas sur le contrôleur

Le playbook 03_cicd est mort sur les deux hôtes :

  "/Users/…/roles/ci_base_image/files/" is not an existing directory

`docker_image_build` s'exécute SUR LA CIBLE : son `path:` est un chemin de la
cible. Je passais `{{ role_path }}/files/`, un chemin du CONTRÔLEUR.

Le motif venait du rôle `playwright`, qui l'emploie LÉGITIMEMENT parce qu'il
construit en local. Recopié pour un build distant, il ne pouvait pas marcher —
et aucune relecture ne l'aurait montré, seule l'exécution le dit.

⚠ L'échec est arrivé AVANT les tâches qui déploient le runner : les deux
runners sont restés `Up 6 days`, rien n'a été cassé. Le seul effet fut un jeton
d'API Gitea créé par le rôle gitea_token, son comportement normal.

Le contexte est désormais déposé sur la machine (`/tmp/ci-base-image`), et la
RECONSTRUCTION DEVIENT CONDITIONNELLE : `never` en régime normal — le playbook
ne rebâtit pas 3,3 Go à chaque passage — mais `always` dès que le Dockerfile a
CHANGÉ sur la machine. Ajouter une bibliothèque devient donc effectif sans avoir
à penser à un drapeau.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
CI Bot
2026-07-29 23:41:32 +02:00
co-authored by Claude Opus 5
parent 863ac68065
commit ba791ed055
2 changed files with 46 additions and 3 deletions
@@ -48,5 +48,15 @@ ci_base_image_node_major: 20
# images de runner elles-mêmes. # images de runner elles-mêmes.
ci_base_image_pin: true ci_base_image_pin: true
# Reconstruire même si l'image existejà (utile après un changement de version). # Où le contexte de build est déposé SUR LA MACHINE CIBLE. `docker_image_build`
# s'exécute sur la cible : son `path:` est un chemin de la cible, jamais du
# contrôleur. (Première version : `{{ role_path }}/files/` → le playbook mourait
# sur « is not an existing directory », sur les deux hôtes.)
ci_base_image_contexte: /tmp/ci-base-image
# Reconstruire même si l'image existe déjà. ⚠ Inutile pour un simple changement
# du Dockerfile : le rôle le détecte et reconstruit tout seul (voir tasks/).
# Ce drapeau sert aux cas que le Dockerfile ne montre pas — une montée de
# `bun.lock` côté kadans, par exemple, qui change les VERSIONS attendues sans
# changer le fichier.
ci_base_image_force_rebuild: false ci_base_image_force_rebuild: false
@@ -6,12 +6,45 @@
# atterrit sur une machine sans l'image échouerait AVANT sa première étape — # 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. # `runs-on`/`container:` est résolu par le runner, pas par le workflow.
# ══════════════════════════════════════════════════════════════════════════
# ⚠ LE CONTEXTE DE BUILD DOIT ÊTRE SUR LA MACHINE, PAS SUR LE CONTRÔLEUR.
#
# `docker_image_build` s'exécute SUR LA CIBLE : son `path:` est un chemin de la
# cible. La première version passait `{{ role_path }}/files/` — un chemin du
# CONTRÔLEUR — et le playbook mourait sur les deux hôtes :
#
# "/Users/…/roles/ci_base_image/files/" is not an existing directory
#
# Le motif venait du rôle `playwright`, qui l'utilise LÉGITIMEMENT parce qu'il
# construit en local ; recopié tel quel pour un build distant, il ne peut pas
# marcher. On copie donc le contexte d'abord.
# ══════════════════════════════════════════════════════════════════════════
- name: Créer le répertoire de contexte de build sur la machine
ansible.builtin.file:
path: '{{ ci_base_image_contexte }}'
state: directory
mode: '0755'
- name: Déposer le Dockerfile sur la machine
ansible.builtin.copy:
src: Dockerfile
dest: '{{ ci_base_image_contexte }}/Dockerfile'
mode: '0644'
register: ci_base_image_dockerfile
- name: Construire {{ ci_base_image_name }}:{{ ci_base_image_tag }} - name: Construire {{ ci_base_image_name }}:{{ ci_base_image_tag }}
community.docker.docker_image_build: community.docker.docker_image_build:
name: '{{ ci_base_image_name }}' name: '{{ ci_base_image_name }}'
tag: '{{ ci_base_image_tag }}' tag: '{{ ci_base_image_tag }}'
path: '{{ role_path }}/files/' path: '{{ ci_base_image_contexte }}'
rebuild: '{{ "always" if ci_base_image_force_rebuild else "never" }}' # RECONSTRUCTION CONDITIONNELLE : `never` en régime normal (le playbook ne
# rebâtit pas 3,3 Go à chaque passage), mais `always` dès que le Dockerfile
# a CHANGÉ sur la machine — c'est ce qui rend l'ajout d'une bibliothèque
# effectif sans avoir à penser à un drapeau.
rebuild: >-
{{ "always"
if (ci_base_image_force_rebuild or ci_base_image_dockerfile is changed)
else "never" }}
args: args:
CI_BASE_FROM: '{{ ci_base_image_from }}' CI_BASE_FROM: '{{ ci_base_image_from }}'
NODE_MAJOR: '{{ ci_base_image_node_major }}' NODE_MAJOR: '{{ ci_base_image_node_major }}'