CI/CD avec GitHub Actions et GitLab CI : tutoriel Node.js

Comprendre le CI/CD en pratiquant : un projet Node.js avec Jest, deux configurations pour GitHub Actions et GitLab CI, et les erreurs à éviter pour réussir son premier pipeline.
Dans ce tutoriel, je présente les bases du CI/CD avec GitHub Actions et GitLab CI, puis deux configurations pour automatiser les tests d’un petit projet Node.js avec Jest.
J’ai créé le dépôt CI-GitHub-GitLab comme support de live coding et de veille technique. Cet article prolonge ma présentation sur le CI/CD avec un exemple que vous pouvez explorer, modifier et utiliser pour apprendre.
L’objectif est concret : à chaque changement concerné, lancer les tests automatiquement et obtenir un résultat visible avant de fusionner le code.
CI, livraison continue et déploiement continu : quelles différences ?
Ces trois notions sont liées, mais elles ne désignent pas exactement le même travail.
| Notion | Objectif | Exemple |
|---|---|---|
| Intégration continue, ou CI | Intégrer fréquemment les changements et les vérifier automatiquement | Exécuter les tests lors d’une pull request |
| Livraison continue, ou Continuous Delivery | Garder une version prête à être déployée | Préparer une version validée dont la mise en production peut nécessiter une décision humaine |
| Déploiement continu, ou Continuous Deployment | Déployer automatiquement les changements qui passent les validations prévues | Mettre une version en production après la réussite du pipeline |
Un pipeline est l’ensemble des tâches automatisées qui réalisent ces vérifications ou ces livraisons. Il peut commencer par un seul job de test, puis évoluer selon les besoins du projet.
Un pipeline qui lance des tests ne déploie pas automatiquement une application. Le projet présenté ici est avant tout une démonstration d’intégration continue. Les notions de livraison et de déploiement permettent de comprendre la suite possible.
Le CI/CD peut accompagner une organisation Agile. Il ne remplace ni la méthode de travail de l’équipe, ni la revue de code.
Pourquoi automatiser les tests ?
Une modification peut fonctionner sur un poste de développement et casser un comportement déjà présent. Sans vérification automatique, le problème peut être découvert après la fusion, ou par un utilisateur.
La CI permet de répéter les mêmes contrôles dans un environnement défini. Elle rend aussi leur résultat visible pour les personnes qui relisent le changement.
Sa valeur dépend cependant des contrôles configurés. Un pipeline vert signifie que ces contrôles ont réussi ; il ne prouve pas que toute l’application est correcte.
Pour commencer, un test simple suffit à comprendre ce mécanisme.
Ce que contient mon projet CI-GitHub-GitLab
Voici les principaux fichiers du dépôt :
| Fichier | Rôle |
|---|---|
sum.js | Fonction JavaScript qui additionne deux nombres |
sum.test.js | Test Jest qui vérifie que 1 + 2 donne 3 |
jest.config.cjs | Configuration de Jest |
package.json et package-lock.json | Scripts du projet et dépendances |
.github/workflows/ci.yml | Workflow exécuté par GitHub Actions |
.gitlab-ci.yml | Configuration destinée à GitLab CI/CD |
Le test présent dans le dépôt est volontairement court :
const sum = require('./sum');
test('adds 1 + 2 = 3', () => {
expect(sum(1, 2)).toBe(3);
});
Ce test rend un échec facile à provoquer et à comprendre. Il sert à observer le fonctionnement de la CI, pas à représenter une suite de tests complète.
Dans la version du dépôt utilisée pour cet article, le workflow GitHub lance l’installation, les tests, une commande de build et l’archivage du dossier dist. Le pipeline GitLab contient uniquement un job de test.
Il faut préciser une limite de la démonstration : le script build exécute echo build step. Il ne compile pas une application. Le dossier dist est déjà présent dans le dépôt ; son archivage ne prouve donc pas qu’un build l’a généré. Aucun déploiement n’est configuré.
Préparer le projet en local
Pour suivre l’exemple, il faut Git, Node.js, npm et un dépôt sur la plateforme choisie. Les configurations proposées ci-dessous utilisent Node.js 24, une branche LTS maintenue à la date de préparation de l’article.
Vous pouvez récupérer le projet ainsi :
git clone https://github.com/Keltoummalouki/CI-GitHub-GitLab.git
cd CI-GitHub-GitLab
npm ci
npm test
Pour pousser vos propres changements, faites d’abord un fork sur GitHub ou importez le projet dans votre compte GitLab. Le clonage du dépôt d’origine ne donne pas de droits d’écriture sur celui-ci.
La commande npm ci installe les dépendances à partir du fichier de verrouillage. Elle nécessite un package-lock.json cohérent avec package.json et échoue si les deux ne correspondent pas. C’est utile en CI pour éviter de modifier les versions verrouillées pendant l’installation.
Les deux configurations suivantes sont des propositions pour ce tutoriel, et non une copie exacte des fichiers du dépôt. Elles recentrent l’exemple sur les tests, utilisent
npm ciet remplacent Node.js 18, présent dans le workflow initial, par Node.js 24. Validez-les dans votre fork avant de les adopter.
Configurer GitHub Actions pour les tests Node.js
GitHub Actions lit les workflows placés dans .github/workflows/.
Dans votre fork, le fichier .github/workflows/ci.yml peut contenir :
name: Node.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
tests:
name: Jest tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Retrieve repository files
uses: actions/checkout@v7
with:
persist-credentials: false
- name: Prepare Node.js
uses: actions/setup-node@v7
with:
node-version: '24'
cache: npm
- name: Install locked dependencies
run: npm ci
- name: Run Jest
run: npm test -- --ci
Le workflow se déclenche lors d’un push sur main et pour les événements habituels d’une pull request ciblant main, notamment son ouverture et l’ajout de nouveaux commits. Si votre branche principale porte un autre nom, adaptez les deux filtres.
Le job utilise une machine fournie par GitHub. Ses étapes récupèrent le code, préparent Node.js, installent les dépendances, puis lancent Jest.
Le cache npm permet de réutiliser des téléchargements de paquets. Il ne remplace pas npm ci.
Après avoir poussé le fichier, ouvrez l’onglet Actions du dépôt. En cas d’échec, sélectionnez l’exécution puis l’étape concernée pour lire les logs.
Les versions majeures des actions rendent l’exemple lisible. Dans un projet qui exige un verrouillage strict des dépendances CI, vous pouvez les remplacer par des SHA de commits vérifiés et organiser leurs mises à jour.
Configurer le même contrôle avec GitLab CI/CD
Sur GitLab, le point d’entrée habituel est le fichier .gitlab-ci.yml à la racine.
Voici une proposition qui lance les tests pour les merge requests et pour les pushs sur la branche par défaut :
default:
image: node:24-bookworm-slim
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
stages:
- test
jest_tests:
stage: test
timeout: 10m
script:
- npm ci
- npm test -- --ci
L’image fournit l’environnement Node.js. Cette configuration suppose un runner compatible avec les images de conteneurs, par exemple un runner utilisant l’exécuteur Docker. Avec un exécuteur Shell, il faut préparer Node.js sur la machine du runner.
Le job appartient au stage test. Les commandes de son script sont exécutées dans l’ordre. Les règles limitent les déclenchements ; les tags et les exécutions planifiées ne sont pas inclus dans cet exemple.
Après l’import du projet, vérifiez qu’un runner adapté est disponible, puis consultez le pipeline et les logs du job dans GitLab.
Déposer un fichier .gitlab-ci.yml sur GitHub ne suffit pas à lancer GitLab CI/CD. Le projet doit aussi être présent et configuré sur GitLab.
Vérifier que la CI détecte réellement une erreur
La meilleure démonstration consiste à faire échouer volontairement le test dans une branche d’exercice.
- Créez une branche dans votre fork ou votre copie du projet.
- Remplacez temporairement
toBe(3)partoBe(4)dans le test. - Poussez la branche et ouvrez une pull request ou une merge request vers la branche principale.
- Vérifiez que le job de test échoue et que les logs montrent la différence entre le résultat attendu et le résultat reçu.
- Rétablissez
toBe(3), poussez la correction et vérifiez la nouvelle exécution.
Le résultat attendu est un pipeline rouge avec l’attente incorrecte, puis vert après la correction.
Une vérification automatique ne bloque pas nécessairement une fusion à elle seule. Pour en faire une condition de fusion, configurez les contrôles obligatoires ou les règles correspondantes sur votre plateforme.
GitHub Actions et GitLab CI : les repères à retenir
| Besoin | GitHub Actions | GitLab CI/CD |
|---|---|---|
| Définir l’automatisation | Workflow YAML dans .github/workflows/ | Configuration .gitlab-ci.yml |
| Définir les déclencheurs | Événements sous on | Règles du workflow et des jobs |
| Organiser les tâches | Jobs contenant des steps | Jobs associés à des stages |
| Définir des dépendances | needs entre jobs | Stages et, si nécessaire, needs |
| Exécuter les tâches | Runner | Runner |
| Conserver des résultats | Artifacts | Artifacts |
| Réutiliser des fichiers pour accélérer les exécutions | Cache | Cache |
Les deux outils permettent de construire une CI utile. Pour ce premier exercice, le choix peut simplement suivre l’hébergement de votre projet.
Cache et artifacts : quelle différence ?
Un cache sert surtout à accélérer une nouvelle exécution, par exemple en réutilisant des paquets téléchargés.
Un artifact conserve un résultat à consulter ou à transmettre : rapport de tests, archive ou fichiers produits par un build.
Dans ce projet, l’archivage de dist illustre le mécanisme des artifacts. Pour aller plus loin, il faudrait une vraie commande de build qui produit ce dossier à partir des sources.
Les erreurs fréquentes quand on débute
Le workflow ne démarre pas. Vérifiez le chemin du fichier, l’indentation YAML, la branche ciblée et les règles de déclenchement.
Les tests fonctionnent en local, mais pas en CI. Comparez la version de Node.js, les dépendances verrouillées, les variables nécessaires et les chemins de fichiers. La sensibilité à la casse peut aussi différer entre votre ordinateur et le runner.
Le job GitLab reste en attente. Vérifiez la disponibilité du runner et, si vous en utilisez, les tags demandés par le job.
Le pipeline est vert, mais l’application n’a pas changé. Vérifiez qu’une vraie étape de déploiement existe. Un test réussi ou un artifact créé ne réalise pas une mise en production.
Le build réussit sans produire de fichiers. Lisez le script lancé par npm run build. Dans cette démonstration, il affiche seulement un message.
Faire évoluer ce premier pipeline
Une fois les tests compris et vérifiés, ajoutez progressivement les contrôles utiles à votre application : lint, vérification TypeScript, tests d’intégration ou véritable build.
Vous pourrez ensuite préparer un déploiement vers un environnement de test, puis définir les conditions de mise en production. Gardez les secrets dans les mécanismes prévus par la plateforme et limitez les permissions aux besoins des jobs.
Le prochain exercice que je propose est simple : ajouter un deuxième test, provoquer son échec, puis suivre sa correction dans le pipeline. Cela permet de vérifier que l’automatisation apporte un retour exploitable à chaque changement.
Code source et documentation
- Mon projet CI-GitHub-GitLab sur GitHub.
- Version du dépôt utilisée pour cet article.
- Workflows GitHub Actions.
- Événements GitHub Actions.
- Actions officielles checkout et setup-node.
- Référence YAML GitLab CI/CD et règles de workflow.
- Cache GitLab CI/CD.
- Documentation de npm ci.
- Versions maintenues de Node.js.
Keltoum Malouki — développeuse Full Stack. Retrouvez mes projets et mon parcours sur mon portfolio.