
Vous avez travaillé les 13 modules précédents 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.
Ne cherchez pas à tout comprendre d'un bloc. Lisez-le dans l'ordre des numéros de commentaire : les sections 1 à 3 posent le squelette (stages, variables, cache) et ne contiennent aucun job. Les sections 4 à 9 déclarent les jobs eux-mêmes, du plus rapide au plus engageant. Chaque décision qui n'est pas évidente à la lecture est reprise et justifiée juste après.
# ── 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:3.24.1 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:3.24.1 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:3.24.1 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. GitLab déclenche le job stop:staging dans trois situations : quand vous cliquez sur Stop dans l'interface, quand la branche associée est supprimée, ou à l'expiration d'un délai auto_stop_in si vous en déclarez un. Sans on_stop, un environnement de test créé par branche reste listé indéfiniment et ses ressources ne sont jamais libérées.
→ 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: productionUn déploiement en production doit résister à deux scénarios : le déclenchement accidentel (un commit sur la mauvaise branche) et le déploiement concurrent (deux pipelines qui écrivent en même temps sur le même serveur). Aucun mot-clé ne couvre les deux, d'où l'empilement de quatre garde-fous complémentaires listés ci-dessous.
Le plus souvent oublié est resource_group : sans lui, deux tags poussés à une minute d'intervalle lancent deux jobs de déploiement en parallèle, et l'ordre d'arrivée sur le serveur cible n'est pas garanti.
| 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. Les six familles ci-dessous correspondent aux six endroits où un pipeline se dégrade avec le temps : la structure, les variables, le cache, les conditions, les environnements et la validation.
Structure
Section intitulée « Structure »La structure est ce que vous lisez en premier quand un pipeline échoue six mois plus tard. Le nom du job est la seule information affichée dans la vue pipeline et dans la notification d'échec : test:unit vous dit immédiatement quoi rejouer, job1 vous oblige à rouvrir le YAML. Quant à l'ordre des stages, il conditionne le temps de retour : mal ordonné, un pipeline peut mettre huit minutes à signaler une faute de style détectable en dix secondes.
- 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 »Le point critique est la portée. Une variable déclarée à la racine s'applique à tous les jobs, une variable de job écrase la valeur globale, et une variable définie dans l'interface du projet l'emporte sur les deux. Cette précédence explique la majorité des « ma variable est vide » : la valeur existe, mais un autre niveau l'a remplacée.
Côté secrets, deux attributs se cumulent et ne font pas la même chose. Masked remplace la valeur par des [MASKED] dans les logs de job. Protected interdit purement l'injection de la variable en dehors des branches et tags protégés. Un secret de production doit porter les deux.
- 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 »Les deux mécanismes se ressemblent et servent des buts opposés. Le cache est une optimisation : s'il est perdu, le pipeline reste correct, seulement plus lent. Un artifact est un livrable : s'il est perdu, le job suivant échoue. Un dist/ mis en cache au lieu d'être publié en artifact produit exactement ce symptôme, un déploiement qui marche une fois sur deux selon le runner qui a récupéré le job.
Le when: always sur les artifacts de test mérite une attention particulière : par défaut, GitLab ne conserve les artifacts que si le job réussit. Or c'est précisément quand les tests échouent que vous voulez lire le rapport JUnit.
- 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)
Une règle trop permissive ne provoque jamais d'erreur visible : le job tourne, simplement il tourne trop souvent. Le coût se paie en minutes de compute et, pour un job de déploiement, en incidents. Un deploy déclenché depuis une merge request pousse le code d'une branche non relue sur un serveur réel.
Relisez chaque bloc rules en vous posant une seule question : quelles sont toutes les situations où cette condition devient vraie ? Les pipelines de merge request, les pipelines de tag et les pipelines planifiés comptent parmi elles, et ce sont ceux qu'on oublie.
- 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 »Déclarer un environment ne change rien à l'exécution du script : c'est une déclaration à destination de GitLab, qui construit à partir d'elle l'historique des déploiements, le lien vers l'application et le bouton d'arrêt. Un job de déploiement sans environment fonctionne, mais vous perdez la trace de qui a déployé quoi et quand, exactement l'information réclamée le jour d'un incident.
- 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 »Une erreur de syntaxe YAML ne casse pas un job, elle empêche le pipeline entier de démarrer. Le retour arrive alors sous forme d'un message générique dans l'interface, plusieurs minutes après le push. Valider avant de pousser ramène ce délai à quelques secondes et évite de polluer l'historique de la branche avec des commits « fix ci » successifs.
La validation locale ne couvre en revanche que la syntaxe. Vérifiez séparément le comportement : un pipeline correct en mode push peut ne rien déclencher en mode merge request si les rules ne prévoient pas ce cas.
- 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 un pipeline complet ne fonctionne pas, la tentation est de relire le YAML de haut en bas. C'est rarement rentable : sur un pipeline assemblé à partir des briques du Volet 1, cinq causes couvrent l'écrasante majorité des échecs, et chacune se reconnaît à un symptôme distinct avant d'ouvrir le fichier.
Suivez donc l'ordre ci-dessous, du plus global au plus local : d'abord le pipeline existe-t-il, ensuite le job démarre-t-il, enfin le script fait-il ce qu'on attend.
-
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
Testez vos connaissances
Section intitulée « Testez vos connaissances »Ce quiz porte uniquement sur les briques assemblées dans ce module. Une réponse hésitante sur une question signale le module du Volet 1 à relire avant de passer à l'industrialisation.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
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.