Cas pratiques

Installer Node.js, npm et Yarn dans un pack

Un pack outillage : versions figées et surchargeables, exposées comme des commandes natives.

Ce tutoriel construit un pack nodejs qui installe Node.js, npm et Yarn dans des versions précises, et expose trois commandes — node, npm, yarn — utilisables directement depuis le host, sans rien installer sur la machine elle-même.

Pourquoi un pack pour ça ?

Installer Node.js, npm et Yarn directement sur une machine pose toujours la même question : quelle version, et pour combien de temps ? Un projet fige des versions précises, un autre en exige d’autres ; nvm ou des installations manuelles finissent par cohabiter tant bien que mal.

Un pack nodejs déplace le problème : la toolchain est décrite une fois, dans un xbee-pack.yaml versionné (potentiellement dans son propre dépôt Git, partagé par toute une équipe), avec des versions par défaut qui restent surchargeables projet par projet — sans jamais toucher à la machine hôte. Une fois provisionné puis installé (voir plus bas), il s’utilise comme un outil natif : xbee node, xbee npm, xbee yarn. Contrairement au pack hello des tutoriels précédents, pensé pour un service qu’on démarre/arrête (up/down), ce pack-ci est un outil qu’on invoque à la demande — il n’a besoin ni d’up, ni de down, ni même d’un environnement.

Créer le pack

xbee new pack nodejs
cd nodejs

Écrire le xbee-pack.yaml

schema-version: "1.0"
description: "Toolchain Node.js : node, npm et yarn, dans des versions figées et surchargeables."

require: ubuntu:24.04

var:
  node:
    version: "20"
  npm:
    version: "10.4.0"
  yarn:
    version: "1.22.19"

provision: |
  apt-get update
  wget -qO- https://deb.nodesource.com/setup_{{ .node.version }}.x | bash -
  DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs
  npm install -g npm@{{ .npm.version }} yarn@{{ .yarn.version }}  

command:
  node: node
  npm: npm
  yarn: yarn
  • var.node.version, var.npm.version et var.yarn.version font partie du modèle de données du pack : ce sont ces valeurs par défaut qu’un projet surchargera pour figer ses propres versions — voir plus bas.
  • provision regroupe les commandes shell qui installent réellement Node.js, npm et Yarn dans l’image — l’étape qui construit l’image du pack lui-même, juste après celle du système (provision system) ; voir Packs. deploy, qui construit lui aussi sa propre image par-dessus, sert plutôt à intégrer une couche applicative buildée à partir de code source — sans build à intégrer ici, il n’a pas lieu d’être : mieux vaut toujours remonter au niveau provision quand il n’y a rien à construire, seulement à installer. up, down et configure restent eux aussi inutiles : ce pack n’est pas un service.
  • command.node, command.npm et command.yarn exposent trois commandes de pack (xbee <cmdPack>), chacune un raccourci Shell vers le binaire correspondant. Les arguments passés après le nom de la commande lui sont transmis tels quels — comme dans l’exemple xbee --name tom catalina run des Commandes CLI.

{{ .node.version }}, {{ .npm.version }} et {{ .yarn.version }} sont interpolés au moment de l’exécution de provision — voir Modèle de données. Lors de la création de l’image du pack, le modèle de données résulte de la fusion de const et var : les templates y accèdent directement, sans préfixe var. ni const..

Provisionner

xbee pack

provision system prépare ubuntu:24.04, puis provision installe Node.js, npm et Yarn dans les versions déclarées — voir Packs. build (aucune dépendance builder) et deploy (aucune couche applicative à intégrer) n’ont ici rien à faire.

Exposer les commandes du pack

Le xbee-pack.yaml décrit les commandes du pack — node, npm, yarn — mais ne les expose pas : tant que le pack n’est pas installé, xbee node --version échoue, même depuis le répertoire du pack lui-même. Deux façons de l’installer — voir Installer un pack :

  • xbee install <chemin-ou-origin> expose ses commandes globalement, utilisables depuis n’importe quel répertoire. Sans projet associé, chaque appel doit alors monter lui-même son répertoire sur /xbee/app, le répertoire par défaut de .xbee.app (voir le tutoriel Pack minimal) — au format -v hôte:container des Commandes CLI — et fixer un --name pour réutiliser le même container d’un appel à l’autre :

    xbee install ./nodejs
    xbee npm -v .:/xbee/app --name nodetools install
    xbee node -v .:/xbee/app --name nodetools app.js
    xbee yarn -v .:/xbee/app --name nodetools add lodash

    package.json, node_modules ou tout autre fichier créé par ces commandes restent sur le host, dans le répertoire monté — pas dans le container.

  • Mieux, pour un projet : un xbee-install.yaml référence le pack une fois pour toutes, et n’expose ses commandes que dans son propre répertoire — chaque appel y reçoit en plus ce répertoire monté automatiquement sur /xbee/app, sans jamais préciser -v ni --name. C’est cette seconde approche que suit la suite de ce tutoriel.

Une mini application, avec xbee-install.yaml

Créez un nouveau répertoire, à côté de nodejs/ (pas dedans) :

mkdir hello-express
cd hello-express

Écrire le xbee-install.yaml

schema-version: "1.0"

pack: ../nodejs
  • pack référence le pack nodejs de ce tutoriel, avec la même syntaxe que le pack d’un hôte d’environnement — voir Utiliser le pack dans un environnement — y compris la forme longue origin/var, pour surcharger les versions depuis ce projet sans toucher au pack nodejs.
  • Dès que ce fichier est présent, les commandes qu’expose nodejsnode, npm, yarn — s’invoquent directement depuis ce répertoire, chacune avec ce répertoire monté sur /xbee/app : plus besoin de -v ni de --name.
xbee node --version
xbee npm --version
xbee yarn --version

Chaque appel démarre un container xbee, y exécute la commande, puis rend la main — un outil qui se comporte comme s’il était installé nativement.

Installer une dépendance et écrire l’application

xbee npm install express

npm s’exécute dans le pack, mais écrit package.json, package-lock.json et node_modules/ dans le répertoire courant, sur le host, monté automatiquement grâce à l’xbee-install.yaml.

// app.js
const express = require('express');

const app = express();
const port = 3000;

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Server listening on http://localhost:${port}`);
});

Lancer et vérifier

xbee node app.js

démarre le serveur dans le pack, au premier plan — le terminal reste occupé tant que le process tourne. Depuis un autre terminal, dans le même répertoire :

curl http://localhost:3000
# Hello World!

Le container démarré par la commande tourne en réseau host : localhost:3000 à l’intérieur du pack est directement localhost:3000 sur le host, sans publication de port à gérer. Ctrl+C dans le premier terminal arrête le serveur ; le container, propre à cet appel, est alors supprimé.

Surcharger les versions

Comme toute valeur var, les versions se surchargent sans toucher au pack nodejs lui-même — par exemple depuis un hôte d’environnement qui le référence via pack, exactement comme dans Utiliser le pack dans un environnement :

host:
  a:
    pack:
      origin: ./nodejs
      var:
        node:
          version: "18"
        yarn:
          version: "1.22.10"

Pour aller plus loin

  • Le détail des types d’actions disponibles dans provision au-delà du raccourci shell (url, copy, github, …) — voir Actions.
  • Le rôle de build et deploy pour un pack qui intègre une couche applicative buildée à partir de code source — non couvert par ce tutoriel.
  • Toutes les propriétés reconnues dans un xbee-pack.yaml — voir xbee-pack.yaml.
  • Les options de commande de pack (-v, --name, --detached, --rm, --env) — voir Commandes CLI.
  • Référencer plusieurs packs dans un même xbee-install.yaml (pack en liste, avec alias) — non couvert par ce tutoriel.