
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é.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Assembler un
.gitlab-ci.ymlcomplet 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)
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »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
Le pipeline cible
Section intitulée « Le pipeline cible »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.
# ── 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: productionDécortiquer chaque décision
Section intitulée « Décortiquer chaque décision »Pourquoi cet ordre de stages ?
Section intitulée « Pourquoi cet ordre de stages ? »stages: [lint, test, build, deploy]| Ordre | Stage | Raison |
|---|---|---|
| 1 | lint | Feedback le plus rapide — erreurs de style en quelques secondes |
| 2 | test | Valide la logique métier — plus lent que le lint |
| 3 | build | Construit l’artefact — inutile si les tests échouent |
| 4 | deploy | Dé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)
Pourquoi package-lock.json comme clé de cache ?
Section intitulée « Pourquoi package-lock.json comme clé de cache ? »cache: key: files: - package-lock.jsonLe 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)
Pourquoi rules et pas only/except ?
Section intitulée « Pourquoi rules et pas only/except ? »rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHrules 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)
Pourquoi des rapports JUnit et Cobertura ?
Section intitulée « Pourquoi des rapports JUnit et Cobertura ? »artifacts: reports: junit: junit.xml coverage_report: coverage_format: cobertura path: coverage/cobertura-coverage.xmlCes 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)
Pourquoi environment et on_stop ?
Section intitulée « Pourquoi environment et on_stop ? »environment: name: staging url: https://staging.example.com on_stop: stop:stagingLes 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: falseresource_group: productionDeux garde-fous distincts :
| Mécanisme | Protection |
|---|---|
when: manual | Un humain doit cliquer pour déployer — pas de déploiement accidentel |
allow_failure: false | Le pipeline reste bloqué tant que le déploiement n’est pas fait (pas de faux vert) |
resource_group | Un seul déploiement en production à la fois — évite les race conditions |
| Regex sur le tag | Seuls les tags au format vX.Y.Z déclenchent le job |
→ Modules de référence : Déclencheurs (V1-09) et Variables (V1-07)
Checklist du pipeline propre
Section intitulée « Checklist du pipeline propre »Avant de pousser votre pipeline, vérifiez chaque point :
Structure
Section intitulée « Structure »- Les stages sont ordonnés du plus rapide au plus lent
- Chaque job a un
stageexplicite - Pas de job sans
script(outrigger) - Les noms de jobs sont descriptifs (
test:unit, pasjob1)
Variables
Section intitulée « Variables »- 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é
Cache et artifacts
Section intitulée « Cache et artifacts »- Le cache utilise une clé basée sur un fichier de lock (
package-lock.json,Gemfile.lock…) - Les artifacts ont un
expire_inraisonnable (pas de rétention infinie) - Les rapports (JUnit, Cobertura) sont déclarés dans
artifacts:reports -
when: alwayssur les artifacts de test (pour voir les échecs)
- Chaque job a des
rulesexplicites (pas deonly/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_BRANCHsans filtre)
Environnements
Section intitulée « Environnements »- Chaque job de déploiement déclare un
environment - Les environnements temporaires ont un
on_stop - La production utilise
resource_group
Validation
Section intitulée « Validation »- 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 :
-
Pipeline skipped ?
Vérifiez
workflow:rulesau niveau racine. Si aucune règle ne matche, le pipeline entier est ignoré. -
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.
-
Job échoué avec “file not found” ?
L’artefact d’un job précédent n’est pas transmis. Vérifiez
artifacts:pathsdu job producteur et que les stages sont dans le bon ordre. -
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.
-
Règle qui ne matche pas ?
Ajoutez un job de debug temporaire qui affiche les variables CI :
debug:rules:stage: lintscript:- echo "SOURCE=$CI_PIPELINE_SOURCE"- echo "BRANCH=$CI_COMMIT_BRANCH"- echo "TAG=$CI_COMMIT_TAG"- echo "MR=$CI_MERGE_REQUEST_IID"rules:- when: always
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.
| # | Question | Module |
|---|---|---|
| 1 | Je sais écrire un .gitlab-ci.yml sans copier-coller un exemple | V1-01, V1-02 |
| 2 | Je sais valider ma syntaxe YAML avant de pousser | V1-03 |
| 3 | Je sais lire les logs d’un job pour trouver l’erreur | V1-04 |
| 4 | Je comprends la différence entre un runner shared et un runner project | V1-05 |
| 5 | Je sais quand utiliser cache et quand utiliser artifacts | V1-06 |
| 6 | Je sais protéger un secret et comprendre la précédence des variables | V1-07 |
| 7 | Je sais écrire des rules avec if, changes et exists | V1-08 |
| 8 | Je sais configurer un pipeline pour MR, tags et schedules | V1-09 |
| 9 | Je sais créer un environnement avec une stop action | V1-10 |
| 10 | Je sais intégrer des rapports JUnit et couverture | V1-11 |
À retenir
Section intitulée « À retenir »- 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
rulesremplacentonly/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
Prochaines étapes
Section intitulée « Prochaines étapes »Vous maîtrisez les fondamentaux. C’est le moment de passer à la vitesse supérieure : factoriser, accélérer et orchestrer vos pipelines.