Nouvelle page runbook « CI des apps Bun/Nuxt » : le runner par défaut est en Node 18, or nuxi/nuxt importent styleText de node:util (Node 20+) → la CI casse au build. Parade tenue (validée sur arcodange/kadans) : container node:20-bookworm + Bun via npm. Inclut un ci.yml canonique à copier + les pistes infra runner. Co-Authored-By: Claude Opus 4.8 <[email protected]>
6.6 KiB
Factory > Doc > Runbooks > Nouvelle application web > 6b. CI des apps Bun/Nuxt
6b. CI des apps Bun/Nuxt (.gitea/workflows/ci.yml)
Status: ✅ Active Upstream: 6. Workflows CI Related: 1. Dépôt Gitea (secrets d'org) · Conventions de nommage · ADR CI/CD
Summary
Une app Bun + Nuxt qui fait tourner ses gates (lint / typecheck / tests / nuxt build) directement sur le runner heurte un piège : le runner Gitea Actions par défaut expose Node 18, or nuxi/nuxt importent styleText de node:util, une API ajoutée en Node 20 (v20.12) — la CI casse au premier bun run build. La parade tenue : lancer le job dans un conteneur node:20-bookworm et y installer Bun via npm. Ce workflow (ci.yml) est distinct des deux workflows de l'étape 6 (vault.yaml = tofu apply, dockerimage.yaml = build image) : il porte les gates qualité de l'app.
Warning
Le piège Node 18 → 20. Symptôme au premier run : un crash
nuxidu typeThe requested module 'node:util' does not provide an export named 'styleText', alors quebun installa réussi. Cause :styleTextn'existe pas en Node 18. Ce n'est pas un souci de Bun — Bun orchestre, mais Nuxt/Nuxi s'exécutent sous le Node de l'environnement.
Pourquoi le runner est en Node 18
Les runners act_runner de la plateforme enregistrent deux labels qui pointent la même image — ansible/…/playbooks/03_cicd.yml :
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
Cette image est construite par ansible/…/playbooks/ssl/ssl.yml, FROM gitea/runner-images:ubuntu-latest (+ le CA du homelab). Le Node embarqué dans cette base upstream est Node 18 : sans conteneur explicite, tout job (app ou factory) s'exécute avec ce Node.
La parade : container: node:20-bookworm
Modèle de .gitea/workflows/ci.yml à copier dans une app Bun/Nuxt (repris de arcodange/kadans) :
name: CI
on:
push: { branches: [main] }
pull_request:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
gates:
runs-on: ubuntu-latest
# Node 20 requis : nuxi/nuxt importent `styleText` de `node:util` (Node 20+),
# absent du Node 18 de l'image runner par défaut. `node:20-bookworm` = Node 20 + git.
container: node:20-bookworm
steps:
- uses: actions/checkout@v4
- name: Install Bun
run: npm install -g bun
- name: Install deps (lockfile figé)
run: bun install --frozen-lockfile
- name: Gates (lint / typecheck / tests — adapter aux scripts de l'app)
run: bun run check
- name: Build Nuxt
run: bun run build
Points de vigilance :
- Bun via npm. L'image
node:20-bookwormn'a pas Bun ;npm install -g bunsuffit. (Alternative : l'imageoven/bun:1— mais elle n'embarque pas Node, ce qui rejoue le problème inverse pour les outils qui veulent un binairenode.) --frozen-lockfileen CI : échoue si le lockfile n'est pas à jour (garde-fou de reproductibilité).bookworm(Debian) plutôt qu'alpine:alpine(musl) casse certaines dépendances natives de l'écosystème Nuxt.
Caution
CA du homelab.
node:20-bookwormne fait pas confiance au CA privé du homelab (contrairement àrunner-images:ubuntu-latest-ca). La parade convient tant que les gates ne joignent que des endpoints publics (registre npm,actions/checkoutvia l'instance Gitea interne). Si un gate doit atteindrehttps://gitea.arcodange.lab(paquet privé, registre interne), le conteneur vanilla échouera la validation TLS — il faut alors une image Node 20 avec le CA (voir Pistes ci-dessous).
Recommandation
Pour toute app Bun/Nuxt dont la CI fait nuxt build/nuxi sur le runner, ajouter container: node:20-bookworm au job (parade ci-dessus). C'est le contrat par défaut tant que l'image runner par défaut reste en Node 18.
Le build d'image (dockerimage.yaml, étape 6) n'est pas concerné : la compilation Nuxt s'y fait dans le Dockerfile (base node:20/oven/bun au choix de l'app), pas sur le Node du runner.
Pistes (infra runner — à valider par l'admin)
Ces options retirent le contournement par-repo mais touchent la prod runner (rebuild d'image + re-run Ansible 03_cicd sur pi1/pi3, pas un simple merge). Laissées à l'arbitrage :
- Bumper l'image runner. Dans
ssl.yml, baserrunner-images:*-casur une imagegitea/runner-imagesqui embarque Node 20+ (vérifier le Node du tag), rebuild + push, puis re-run03_cicd. Rayon d'impact large : change l'environnement par défaut de tous les jobs, y compris les workflows factoryiac.yaml/postgres.yaml(runs-on: ubuntu-latest-ca) — à revalider. - Ajouter un label
node20dédié. Publier une imagerunner-images:node20-ca(FROM node:20-bookworm+ CA du homelab) et l'exposer via un labelnode20dans03_cicd.yml. Les apps opt-in avecruns-on: node20, sanscontainer:par job, et avec le CA (résout la limite ci-dessus). Rayon d'impact opt-in (plus sûr), mais ajoute une image à maintenir. - Action réutilisable
bun-nuxt-ci. Sur le modèle des dépôts d'action de l'org (arcodange-org/vault-action), factoriser install-Bun + gates + build dans une action composite appelée en 3 lignes. Évite le copier-coller du YAML, indépendamment du Node du runner.
Related
- 6. Workflows CI —
vault.yaml(tofu apply) etdockerimage.yaml(build image) ;ci.yml(cette page) porte les gates de l'app. - 1. Dépôt Gitea — secrets d'org hérités par la CI.
- Sources runner :
03_cicd.yml(labels) ·ssl/ssl.yml(image + CA). - Exemple vivant :
arcodange/kadans—ci.yml.