Aller au contenu
CI/CD & Automatisation medium

Synthèse : construire un pipeline complet

16 min de lecture

logo gitlab

Vous avez travaillé les 13 modules du Volet 1 séparément — stages, variables, rules, cache, environnements, rapports. Maintenant, assemblez-les. Ce module est un mini-capstone : vous construisez un pipeline de bout en bout pour une application réelle, en appliquant toutes les briques apprises.

L’objectif n’est pas d’apprendre du neuf, mais de vérifier que tout s’imbrique. Si vous bloquez, chaque section renvoie au module concerné.

  • Assembler un .gitlab-ci.yml complet combinant toutes les briques du Volet 1
  • Structurer les stages dans un ordre logique (lint → test → build → deploy)
  • Appliquer les bonnes pratiques : variables protégées, rules ciblées, cache optimisé
  • Diagnostiquer rapidement un pipeline cassé avec une méthode systématique
  • Évaluer si votre pipeline est prêt pour l’industrialisation (Volet 2)

Vous avez terminé les 13 premiers modules. Vous savez créer un job, configurer un runner, utiliser le cache et les artifacts, écrire des rules, configurer des environnements, générer des rapports. Mais quand vous ouvrez un .gitlab-ci.yml de production, vous voyez 200 lignes et vous ne savez pas par où commencer.

Ce guide vous met dans cette situation :

  • Vous rejoignez un projet avec un pipeline incomplet
  • Vous devez construire un pipeline complet pour une app Node.js
  • Le pipeline doit compiler, tester, analyser la qualité et déployer en staging puis en production
  • Vous devez gérer les cas : push sur branche, merge request, tag de release

Voici le pipeline complet que vous allez construire, brique par brique. Chaque section est annotée avec le module du Volet 1 qui l’explique en détail.

.gitlab-ci.yml
# ── 1. Stages : l'ordre d'exécution (V1-01) ──────────────────────
stages:
- lint
- test
- build
- deploy
# ── 2. Variables globales (V1-07) ─────────────────────────────────
variables:
NODE_VERSION: "20"
APP_NAME: "mon-app"
DEPLOY_PATH: "/srv/$APP_NAME"
# Variables CI_ automatiques : CI_COMMIT_REF_NAME, CI_PIPELINE_SOURCE…
# ── 3. Cache global (V1-06) ───────────────────────────────────────
default:
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull
# ── 4. Lint (V1-01, V1-08) ────────────────────────────────────────
lint:
stage: lint
image: node:${NODE_VERSION}
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull-push
script:
- npm ci --prefer-offline
- npm run lint
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# ── 5. Tests unitaires + couverture (V1-06, V1-11) ────────────────
test:unit:
stage: test
image: node:${NODE_VERSION}
script:
- npm ci --prefer-offline
- npm test -- --coverage --reporters=default --reporters=jest-junit
artifacts:
when: always
paths:
- coverage/
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
expire_in: 7 days
coverage: '/All files\s*\|\s*(\d+\.?\d*)\s*\|/'
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
# ── 6. Build (V1-06, V1-07) ───────────────────────────────────────
build:
stage: build
image: node:${NODE_VERSION}
script:
- npm ci --prefer-offline
- npm run build
- echo "BUILD_VERSION=$(node -p 'require(\"./package.json\").version')" >> build.env
artifacts:
paths:
- dist/
reports:
dotenv: build.env
expire_in: 30 days
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
# ── 7. Déploiement staging (V1-10, V1-08) ─────────────────────────
deploy:staging:
stage: deploy
image: alpine:latest
environment:
name: staging
url: https://staging.example.com
on_stop: stop:staging
script:
- echo "Deploying $APP_NAME v$BUILD_VERSION to staging..."
- echo "Target → $DEPLOY_PATH on staging server"
# Remplacer par votre méthode de déploiement (rsync, scp, kubectl…)
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# ── 8. Stop staging (V1-10) ───────────────────────────────────────
stop:staging:
stage: deploy
image: alpine:latest
environment:
name: staging
action: stop
script:
- echo "Stopping staging environment..."
when: manual
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
# ── 9. Déploiement production (V1-10, V1-09, V1-07) ───────────────
deploy:production:
stage: deploy
image: alpine:latest
environment:
name: production
url: https://www.example.com
variables:
DEPLOY_STRATEGY: "rolling"
script:
- echo "Deploying $APP_NAME v$BUILD_VERSION to production..."
- echo "Strategy → $DEPLOY_STRATEGY"
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
when: manual
allow_failure: false
resource_group: production
stages: [lint, test, build, deploy]
OrdreStageRaison
1lintFeedback le plus rapide — erreurs de style en quelques secondes
2testValide la logique métier — plus lent que le lint
3buildConstruit l’artefact — inutile si les tests échouent
4deployDéploie — uniquement si tout est vert

Principe : les jobs les plus rapides et les moins coûteux d’abord. Si le lint échoue en 10 secondes, inutile de lancer 5 minutes de tests.

Module de référence : Concepts de base (V1-01)

cache:
key:
files:
- package-lock.json

Le cache est invalidé uniquement quand les dépendances changent. Tant que package-lock.json ne bouge pas, les node_modules/ sont réutilisés. C’est plus fiable que $CI_COMMIT_REF_SLUG qui crée un cache par branche.

La politique pull-push sur le job lint (premier à tourner) alimente le cache. Les jobs suivants utilisent pull (lecture seule) pour éviter les écritures concurrentes.

Module de référence : Artifacts et cache (V1-06)

rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

rules est le mécanisme recommandé depuis GitLab 12.3. Il remplace only/except qui ne gère pas les conditions complexes. Chaque if est évalué dans l’ordre : la première règle qui matche détermine si le job tourne et avec quel when.

Attention au piège classique : si aucune règle ne matche, le job est exclu du pipeline. Pas de rules par défaut = le job tourne toujours.

Module de référence : Conditions d’exécution — rules (V1-08)

artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml

Ces rapports s’intègrent directement dans l’interface GitLab :

  • JUnit : les résultats de tests apparaissent dans l’onglet “Tests” du pipeline et dans la merge request
  • Cobertura : l’indicateur de couverture apparaît sur le badge du projet et la diff de la MR montre les lignes couvertes ou non

Sans ces rapports, vous devez fouiller les logs pour trouver quels tests ont échoué.

Module de référence : Rapports qualité (V1-11)

environment:
name: staging
url: https://staging.example.com
on_stop: stop:staging

Les environnements GitLab offrent :

  • Un historique des déploiements visible dans l’interface
  • Un lien direct vers l’URL de l’application
  • Un bouton Stop pour les environnements temporaires (review apps)

Le on_stop est essentiel pour le nettoyage : quand une branche est supprimée ou manuellement, GitLab déclenche le job stop:staging pour libérer les ressources.

Module de référence : Environnements (V1-10)

Pourquoi when: manual et resource_group en production ?

Section intitulée « Pourquoi when: manual et resource_group en production ? »
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
when: manual
allow_failure: false
resource_group: production

Deux garde-fous distincts :

MécanismeProtection
when: manualUn humain doit cliquer pour déployer — pas de déploiement accidentel
allow_failure: falseLe pipeline reste bloqué tant que le déploiement n’est pas fait (pas de faux vert)
resource_groupUn seul déploiement en production à la fois — évite les race conditions
Regex sur le tagSeuls les tags au format vX.Y.Z déclenchent le job

Modules de référence : Déclencheurs (V1-09) et Variables (V1-07)

Avant de pousser votre pipeline, vérifiez chaque point :

  • Les stages sont ordonnés du plus rapide au plus lent
  • Chaque job a un stage explicite
  • Pas de job sans script (ou trigger)
  • Les noms de jobs sont descriptifs (test:unit, pas job1)
  • Pas de secret dans le .gitlab-ci.yml — tout dans Settings > CI/CD > Variables
  • Les variables sensibles sont masked et protected
  • Les variables globales sont dans variables: au niveau racine
  • Les variables spécifiques sont dans le job concerné
  • Le cache utilise une clé basée sur un fichier de lock (package-lock.json, Gemfile.lock…)
  • Les artifacts ont un expire_in raisonnable (pas de rétention infinie)
  • Les rapports (JUnit, Cobertura) sont déclarés dans artifacts:reports
  • when: always sur les artifacts de test (pour voir les échecs)
  • Chaque job a des rules explicites (pas de only/except)
  • Les jobs de déploiement ne tournent pas sur les merge requests
  • Le déploiement production nécessite when: manual
  • Pas de règle trop permissive (if: $CI_COMMIT_BRANCH sans filtre)
  • Chaque job de déploiement déclare un environment
  • Les environnements temporaires ont un on_stop
  • La production utilise resource_group
  • Le YAML est validé avant le push (CI Lint ou extension VS Code)
  • Le pipeline tourne correctement en mode MR et en mode push
  • Les rapports s’affichent dans l’interface GitLab

Diagnostic rapide : les 5 erreurs les plus fréquentes

Section intitulée « Diagnostic rapide : les 5 erreurs les plus fréquentes »

Quand votre pipeline complet ne fonctionne pas, voici la méthode :

  1. Pipeline skipped ?

    Vérifiez workflow:rules au niveau racine. Si aucune règle ne matche, le pipeline entier est ignoré.

    Debug : pipeline skipped (V1-13)

  2. Job pending depuis 10 minutes ?

    Le runner n’est pas disponible. Vérifiez les tags du job vs ceux du runner, et que le runner est bien actif.

    Debug : job pending (V1-12)

  3. Job échoué avec “file not found” ?

    L’artefact d’un job précédent n’est pas transmis. Vérifiez artifacts:paths du job producteur et que les stages sont dans le bon ordre.

    Artifacts et cache (V1-06)

  4. Variable vide dans le script ?

    Vérifiez la précédence : une variable de job écrase une variable globale, une variable protégée n’est disponible que sur les branches protégées.

    Variables et secrets (V1-07)

  5. Règle qui ne matche pas ?

    Ajoutez un job de debug temporaire qui affiche les variables CI :

    debug:rules:
    stage: lint
    script:
    - echo "SOURCE=$CI_PIPELINE_SOURCE"
    - echo "BRANCH=$CI_COMMIT_BRANCH"
    - echo "TAG=$CI_COMMIT_TAG"
    - echo "MR=$CI_MERGE_REQUEST_IID"
    rules:
    - when: always

    Conditions d’exécution — rules (V1-08)

Auto-évaluation : êtes-vous prêt pour le Volet 2 ?

Section intitulée « Auto-évaluation : êtes-vous prêt pour le Volet 2 ? »

Répondez honnêtement à ces questions. Si vous répondez oui à au moins 8 sur 10, vous êtes prêt pour l’industrialisation.

#QuestionModule
1Je sais écrire un .gitlab-ci.yml sans copier-coller un exempleV1-01, V1-02
2Je sais valider ma syntaxe YAML avant de pousserV1-03
3Je sais lire les logs d’un job pour trouver l’erreurV1-04
4Je comprends la différence entre un runner shared et un runner projectV1-05
5Je sais quand utiliser cache et quand utiliser artifactsV1-06
6Je sais protéger un secret et comprendre la précédence des variablesV1-07
7Je sais écrire des rules avec if, changes et existsV1-08
8Je sais configurer un pipeline pour MR, tags et schedulesV1-09
9Je sais créer un environnement avec une stop actionV1-10
10Je sais intégrer des rapports JUnit et couvertureV1-11
  • Un pipeline de production combine toutes les briques : stages, variables, cache, rules, artifacts, environnements, rapports
  • L’ordre des stages suit le principe du feedback rapide : lint → test → build → deploy
  • Le cache utilise un fichier de lock comme clé, pas le nom de branche
  • Les rules remplacent only/except — chaque job doit avoir des conditions explicites
  • Les secrets vont dans Settings > CI/CD > Variables, jamais dans le YAML
  • La production nécessite when: manual + resource_group + tag regex
  • Les rapports JUnit et Cobertura s’intègrent dans l’interface GitLab — utilisez-les
  • Un pipeline qui ne se valide pas avant le push = du temps perdu

Vous maîtrisez les fondamentaux. C’est le moment de passer à la vitesse supérieure : factoriser, accélérer et orchestrer vos pipelines.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn