Publication d’un paquet NPM depuis une CI Gitlab
21/12/2023 - 5 minutes
Enjeu
Publier un paquet NPM aussi bien sur NPMJS que sur les différentes échelles (projet et instance) du Gitlab registry via la CI Gitlab. Il est tout à fait possible de faire soit l’un, soit l’autre, soit les deux.
Prérequis
Le paquet en question peut-être aussi bien public que scoped pour une publication uniquement sur NPM. Il sera nécessairement scoped sur le registry Gitlab et, de ce fait, sur NPMJS.
⚠️ La publication d’un paquet scoped et privé sur NPMJS est payante, votre paquet sera donc public par défaut.
Dans notre cas, nous traiterons une double publication registry Gitlab et NPMJS, notre publication sera donc scoped et publique.
Pour cela il faut :
- Une organisation Gitlab du nom du scope suivant la convention
- Un paquet préfixé du nom du namespace NPM dans lequel vous voulez publier (e.g @mynamespace/mypackagename)
- Un token NPMJS que vous ajouterez en variable d’environnement de vôtre CI Gitlab (e.g $NPM_TOKEN).
Je suggère d’utiliser un token de type Automation qui est dédié aux workflows automatisés.
1. Configuration
Je n’aborde pas les étapes de test mais vous recommande fortementde tester votre code ainsi que de suivre des normes de formatage et de présentation standards grâce a Eslint et Prettier. Il est aussi intéressant de développer vos librairies avec Typescript afin de fournir un typage aux futurs utilisateurs. Cela facilitera également la scalabilité de votre projet.
Une fois votre projet initialisé, créez un fichier .gitlab-ci.yml à la racine de votre projet.
1.1. Image Docker
L’image Docker n’a pas une grande incidence sur l’étape de publication, essayez toutefois d’utiliser une image LTS light dans la mesure du possible :
ymlimage: node:lts-alpine
1.2. Définition des étapes et paramétrage de la mise en cache
image: node:lts-alpine
stages:
- test
- build
- deploy
# Configuration de la mise en cache des dépendances pour persister leur installation au sein des différentes étapes
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
# Installation des dépendances avant l'exécution des jobs
before_script:
- npm i
# Vos étapes de tests...
1.3. Build et artifacts
Peu de choses à dire niveau build, pensez simplement à bien ajouter les différents chemins de vos artifacts. À la fin le paquet correspondra a ces fichiers/dossiers, le package.json et le README.md :
# Vos étapes de tests...
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist
# - vos autres dossiers/fichiers a inclure
2. Déploiement Gitlab registry
Nous voulons déployer notre librairie sur le registry Gitlab, aux échelles du projet et de l’instance de notre organisation. Il est nécessaire de :
- Attendre la fin du build
- Charger les artifacts
- Configurer l’URL du package
- S’authentifier
- Configurer la publication a l’échelle du projet
- Configurer la publication a l’échelle de l’organisation
- Publier
publish_gitlab_registry:
stage: deploy
needs:
- job: build
artifacts: true
dependencies:
- build
script:
# Configuration de l'URL du package, remplacer l'organisation par le nom de votre organisation, le CI_PROJECT_ID est fourni par la CI
- npm config set ${ORGANIZATION}:registry https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
# Configuration de la publication a l'échelle du projet, le CI_JOB_TOKEN est fourni et suffit a l'authentification
- npm config set -- '//gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken' "${CI_JOB_TOKEN}"
# Configuration de la publication a l'échelle de l'instance, le CI_JOB_TOKEN est fourni et suffit a l'authentification
- npm config set -- '//gitlab.com/api/v4/packages/npm/:_authToken' "${CI_JOB_TOKEN}"
# Publication
- npm publish
# Nous conseillons de ne pas trigger la publication a chaque CI, ici la règle demande si le tag existe, restreignant ainsi la publication au tag
rules:
- if: $CI_COMMIT_TAG
3. Déploiement sur NPMJS
Enfin pour publier sur NPMJS ce paquet scoped (suivant le nom du paquet dans le package.json), la logique est la même, seule la configuration change :
publish_npm:
stage: deploy
needs:
- job: build
artifacts: true
dependencies:
- build
script:
# Configuration pour la publication sur NPMJS, NPM_TOKEN est le token NPM de type automation généré plus tôt et ajouté aux variables d'environnement de votre CI Gitlab
- npm config set -- '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
# Etant donné que le paquet est scope, il est nécessaire de spécifier le fait qu'il est public
- npm publish --access public
rules:
- if: $CI_COMMIT_TAG
Résumé .gitlab-ci.yml :
image: node:lts-alpine
stages:
- test
- build
- deploy
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
before_script:
- npm install
# Vos étapes de tests...
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist
publish_gitlab_registry:
stage: deploy
needs:
- job: build
artifacts: true
dependencies:
- build
script:
- npm config set ${ORANIZATION}:registry https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
- npm config set -- '//gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken' "${CI_JOB_TOKEN}"
- npm config set -- '//gitlab.com/api/v4/packages/npm/:_authToken' "${CI_JOB_TOKEN}"
- npm publish
rules:
- if: $CI_COMMIT_TAG
publish_npm:
stage: deploy
needs:
- job: build
artifacts: true
dependencies:
- build
script:
- npm config set -- '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- npm publish --access public
rules:
- if: $CI_COMMIT_TAG
4. La suite
4.1. NPMJS
La publication sur NPMJS vous notifiera par mail, vous pourrez ensuite utiliser le paquet depuis n’importe quel projet en l’installant comme n’importe quelle autre dépendance :
npm install @myscope/mypackage
⚠️ Il est impossible de publier plusieurs fois le même paquet avec la même version, pensez donc à bump la version dans le package.json à chaque publication️
Vous pouvez aussi gérer votre paquet depuis votre compte NPMJS.
4.2. Gitlab registry
Vous retrouverez votre paquet dans le repository de votre projet dans Packages & Registries -> Package Registry
Il vous suffira de paramétrer votre authentification au registry selon votre préférence. J’utilise personnellement un personnal access token avec un scope api. Une fois authentifié, vous pouvez installer votre paquet comme n’importe quelle autre dépendance :
npm install @myscope/mypackage
Tout devrait fonctionner pour peu que votre paquet soit fonctionnel !