
Vous poussez une branche, ouvrez une merge request, et deux pipelines tournent en parallèle ? C'est le problème le plus courant quand on débute avec GitLab CI/CD. Le mot-clé workflow:rules permet de décider au niveau global quel type de pipeline créer selon le contexte : push sur une branche, ouverture d'une MR ou création d'un tag.
Sans workflow structuré, vous gaspillez des minutes de CI, polluez l'historique des pipelines et perdez en lisibilité. Ce guide vous donne les patterns éprouvés pour chaque situation.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre pourquoi GitLab crée parfois deux pipelines (branche + MR)
- Utiliser
workflow:rulespour contrôler la création du pipeline au niveau global - Implémenter les trois workflows standard : branch-only, MR-only, hybride
- Configurer un workflow release avec tags sémantiques
- Activer les merge trains pour sérialiser les intégrations
- Éviter les anti-patterns classiques (doublons, déploiement depuis une MR)
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Vous avez un pipeline fonctionnel mais des questions reviennent régulièrement :
- Pourquoi deux pipelines tournent quand j'ouvre une MR ?
- Comment lancer un pipeline uniquement sur les merge requests ?
- Comment déclencher un déploiement uniquement quand je crée un tag
v1.2.3? - Comment empêcher le pipeline de tourner sur les branches de feature inutiles ?
Ce guide répond à toutes ces questions avec des patterns concrets et testés.
Le problème des doublons
Section intitulée « Le problème des doublons »Avant de corriger, il faut comprendre d'où vient le doublon. GitLab ne raisonne
pas en « une branche, un pipeline » mais en événements : chaque action sur
le dépôt déclenche son propre pipeline, avec sa propre valeur de
CI_PIPELINE_SOURCE. Deux événements simultanés donnent donc mécaniquement deux
pipelines, et non un pipeline dédoublé par erreur.
Pourquoi deux pipelines ?
Section intitulée « Pourquoi deux pipelines ? »Par défaut, GitLab crée un pipeline pour chaque événement :
| Événement | Pipeline créé | CI_PIPELINE_SOURCE |
|---|---|---|
git push sur une branche | ✅ | push |
| Ouverture d'une MR | ✅ | merge_request_event |
| Mise à jour d'une branche avec une MR ouverte | ✅✅ | push ET merge_request_event |
| Création d'un tag | ✅ | push |
| Planification (schedule) | ✅ | schedule |
Le piège : quand vous poussez sur une branche qui a une MR ouverte, GitLab crée deux pipelines, un pour le push, un pour la MR. Si vos jobs n'ont pas de rules, ils tournent dans les deux.
CI_OPEN_MERGE_REQUESTS
Section intitulée « CI_OPEN_MERGE_REQUESTS »GitLab fournit la variable CI_OPEN_MERGE_REQUESTS qui contient la liste des MR ouvertes pour la branche courante. Elle permet d'éviter les doublons :
job: rules: # Ne pas tourner sur les push qui ont déjà une MR ouverte - if: $CI_PIPELINE_SOURCE == "push" && $CI_OPEN_MERGE_REQUESTS when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHMais cette approche job par job est fragile. La solution robuste : workflow:rules.
workflow:rules, le gardien global
Section intitulée « workflow:rules, le gardien global »workflow:rules décide si le pipeline est créé ou non, avant même d'évaluer les rules de chaque job. C'est le premier filtre.
workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAGCe pipeline ne sera créé que pour :
- Les événements merge request
- Les push sur la branche par défaut (main)
- Les tags
Tout le reste est ignoré : push sur les branches de feature (sauf si une MR existe), forks, etc.
Les trois workflows standard
Section intitulée « Les trois workflows standard »Presque tous les projets se rangent dans l'un des trois modèles ci-dessous. Ils se distinguent par un seul critère : quels événements créent un pipeline. Le premier accepte tous les push, le deuxième ne retient que les merge requests et la branche par défaut, le troisième combine les deux en excluant explicitement le cas qui produirait un doublon. Choisissez selon la manière dont votre équipe travaille, pas selon la complexité de la configuration.
1. Branch pipeline (le plus simple)
Section intitulée « 1. Branch pipeline (le plus simple) »Chaque push crée un pipeline. Pas de notion de MR.
workflow: rules: - if: $CI_COMMIT_BRANCH - if: $CI_COMMIT_TAG
# Tous les jobs tournent sur chaque pushlint: script: npm run lint
test: script: npm testQuand l'utiliser : projets personnels, petites équipes sans process de review.
Inconvénient : pas de pipeline dédié MR, pas de review visuelle dans la MR.
2. MR pipeline (le plus courant en équipe)
Section intitulée « 2. MR pipeline (le plus courant en équipe) »Le pipeline tourne uniquement sur les merge requests et la branche par défaut.
workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAGCe que ça donne :
| Action | Pipeline ? |
|---|---|
Push sur feature/xxx (sans MR) | ❌ |
Ouverture MR feature/xxx → main | ✅ (source: merge_request_event) |
Push sur feature/xxx avec MR ouverte | ✅ (source: merge_request_event) |
Merge dans main | ✅ (source: push sur default branch) |
Tag v1.0.0 | ✅ (source: push tag) |
Quand l'utiliser : la majorité des projets en équipe. Pas de doublons, pipeline visible dans la MR.
3. Workflow hybride (branch + MR)
Section intitulée « 3. Workflow hybride (branch + MR) »Variante pour ceux qui veulent un pipeline même sur les branches sans MR (pour le retour rapide) :
workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS when: never - if: $CI_COMMIT_BRANCH - if: $CI_COMMIT_TAGLogique :
- Si c'est un événement MR → pipeline MR ✅
- Si c'est un push et qu'une MR est ouverte → pas de pipeline (la MR s'en charge) ❌
- Si c'est un push sans MR → pipeline branch ✅
- Si c'est un tag → pipeline ✅
Ce pattern donne un pipeline dès le premier push (retour rapide) puis bascule sur le pipeline MR dès qu'une MR est ouverte. Aucun doublon.
Workflow release avec tags
Section intitulée « Workflow release avec tags »Une fois les doublons réglés, reste la question du déploiement. Le tag est
le déclencheur le plus sûr : contrairement à une branche, il désigne un commit
précis et immuable, ce qui rend la version déployée traçable. GitLab expose le
tag dans la variable $CI_COMMIT_TAG, absente de tous les autres types de
pipeline.
Semantic versioning
Section intitulée « Semantic versioning »La convention retenue ici : les déploiements en production sont déclenchés par
des tags au format vX.Y.Z. L'expression régulière /^v\d+\.\d+\.\d+$/ est
volontairement stricte, ancrée au début et à la fin, pour qu'un tag de travail
comme v2-rc1 ne parte pas en production. Le when: manual ajoute une
validation humaine, et resource_group: production empêche deux déploiements de
tourner en même temps sur le même environnement.
deploy:production: stage: deploy environment: name: production url: https://www.example.com script: - echo "Deploying $CI_COMMIT_TAG" - ./deploy.sh production rules: - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ when: manual allow_failure: false resource_group: productionPipeline complet avec release
Section intitulée « Pipeline complet avec release »L'exemple ci-dessous rassemble tout : le filtre global, puis des rules: par
job qui restreignent encore. Repérez la progression, elle est volontaire. test
tourne partout, build seulement sur la branche par défaut et les tags,
deploy:production uniquement sur un tag de version. Un job sans règle
correspondante n'apparaît tout simplement pas dans le pipeline.
workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAG =~ /^v\d+/
stages: - test - build - deploy - release
test: stage: test script: npm test rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAG
build: stage: build script: npm run build artifacts: paths: [dist/] rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAG
deploy:staging: stage: deploy environment: name: staging script: ./deploy.sh staging rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy:production: stage: deploy environment: name: production script: ./deploy.sh production rules: - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ when: manual resource_group: production
create:release: stage: release image: registry.gitlab.com/gitlab-org/release-cli:v0.24.0 script: - echo "Creating release for $CI_COMMIT_TAG" release: tag_name: $CI_COMMIT_TAG name: "Release $CI_COMMIT_TAG" description: "Automatic release" rules: - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/Merged results et merge trains
Section intitulée « Merged results et merge trains »Ces deux fonctionnalités répondent au même angle mort : un pipeline MR valide le
code tel qu'il est aujourd'hui, alors que le merge aura lieu sur une branche
cible qui aura peut-être bougé entre-temps. Elles se cumulent, les merge trains
s'appuyant sur les merged results. Toutes deux exigent une édition Premium
et se règlent dans les paramètres du projet, pas dans le fichier
.gitlab-ci.yml.
Pipeline for merged results
Section intitulée « Pipeline for merged results »Par défaut, le pipeline MR teste le code de votre branche. Mais au moment du merge, la branche cible a peut-être avancé. Le pipeline merged results teste le résultat anticipé du merge :
Pour l'activer : Settings > Merge requests > Merge pipelines → activer "Pipelines must succeed" + "Merged results pipelines".
Merge trains
Section intitulée « Merge trains »Les merge trains vont plus loin : quand plusieurs MR sont prêtes à merger, GitLab les sérialise et teste chaque MR en incluant les précédentes :
- MR-A est dans le train → teste
main + MR-A - MR-B arrive → teste
main + MR-A + MR-B - MR-A passe → merge → MR-B reteste si nécessaire
Avantage : la branche main ne casse jamais. Chaque merge est testé avec tout ce qui le précède.
Inconvénient : plus lent, chaque MR attend son tour. Réservé aux branches critiques.
Pour activer : Settings > Merge requests > Merge trains.
workflow: rules: - if: $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train" - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAGReview apps
Section intitulée « Review apps »Les review apps créent un environnement temporaire par merge request, ce qui
permet à un relecteur de cliquer sur la fonctionnalité au lieu de la deviner
dans un diff. Le mécanisme repose entièrement sur le mot-clé environment : un
nom dynamique bâti sur $CI_MERGE_REQUEST_IID crée un environnement distinct
par MR, et GitLab affiche le lien directement dans l'interface de la merge
request. Le risque, avec ce modèle, est l'accumulation d'environnements
abandonnés, d'où les deux mécanismes de nettoyage présents dans l'exemple :
on_stop et auto_stop_in.
review: stage: deploy environment: name: review/$CI_MERGE_REQUEST_IID url: https://$CI_MERGE_REQUEST_IID.review.example.com on_stop: stop:review auto_stop_in: 1 week script: - ./deploy-review.sh "$CI_MERGE_REQUEST_IID" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event"
stop:review: stage: deploy environment: name: review/$CI_MERGE_REQUEST_IID action: stop script: - ./destroy-review.sh "$CI_MERGE_REQUEST_IID" when: manual rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manualPoints clés :
auto_stop_in: 1 weekdétruit l'environnement automatiquement après 1 semaine- Le nom
review/$CI_MERGE_REQUEST_IIDcrée un environnement unique par MR - L'
on_stopdéclenche le nettoyage à la fermeture de la MR
→ Voir aussi Environnements (V1-10) pour les bases.
Anti-patterns à éviter
Section intitulée « Anti-patterns à éviter »Ces quatre erreurs reviennent constamment dans les .gitlab-ci.yml d'équipes
expérimentées. Aucune ne provoque d'erreur de syntaxe : le pipeline se lance,
mais il fait autre chose que ce qui était prévu. Chaque cas montre la version
fautive puis sa correction.
1. Pipeline doublon branch + MR
Section intitulée « 1. Pipeline doublon branch + MR »Sans workflow:rules, GitLab décide de créer les deux pipelines avant de
lire les rules: des jobs. Ajuster ces règles job par job ne supprime donc
jamais le pipeline en trop : il continue d'apparaître dans l'historique, vide ou
partiel. Le filtre doit être posé au niveau global.
# ❌ Pas de workflow:rules → doublonstest: script: npm test rules: - if: $CI_COMMIT_BRANCH # Matche sur push ET MR# ✅ workflow:rules filtre en amontworkflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAG2. Déploiement depuis une MR
Section intitulée « 2. Déploiement depuis une MR »Le code d'une merge request n'a pas encore été relu ni fusionné, et il peut provenir d'un fork. Déployer depuis ce pipeline revient à mettre en ligne du code que personne n'a validé. Réservez le déploiement à la branche par défaut ou à un tag.
# ❌ Dangereux : déploie du code non mergédeploy: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event"# ✅ Déploie uniquement après mergedeploy: rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH3. only/except mélangé avec rules
Section intitulée « 3. only/except mélangé avec rules »only/except et rules sont deux syntaxes concurrentes, héritées de deux
générations de GitLab CI. Les mélanger dans un même job produit une erreur de
validation du fichier, pas un comportement dégradé : le pipeline ne démarre pas.
# ❌ only/except et rules ne se mélangent pastest: only: - branches rules: - if: $CI_COMMIT_TAGUtilisez uniquement rules: (recommandé depuis GitLab 12.3).
4. workflow:rules trop restrictif
Section intitulée « 4. workflow:rules trop restrictif »Le piège inverse du premier. Un workflow:rules qui ne liste que les MR et la
branche par défaut fait disparaître silencieusement les pipelines
planifiés (schedule) et ceux lancés depuis l'interface (web). Aucun message
d'erreur : le pipeline n'est pas créé, et la sauvegarde nocturne ne tourne plus.
# ❌ Bloque les schedules et les triggers manuelsworkflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH# ✅ Autoriser aussi les schedules et les triggersworkflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAG - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_PIPELINE_SOURCE == "web"Dépannage
Section intitulée « Dépannage »La quasi-totalité des symptômes ci-dessous se diagnostique au même endroit : la
valeur de $CI_PIPELINE_SOURCE dans le pipeline concerné, comparée aux
conditions de votre workflow:rules. Affichez-la dans un job avec
echo $CI_PIPELINE_SOURCE avant de modifier quoi que ce soit, cela évite de
corriger la mauvaise règle.
| Symptôme | Cause probable | Solution |
|---|---|---|
| Deux pipelines au même moment | Pas de workflow:rules | Ajouter le workflow MR recommandé |
| Pipeline absent sur une MR | workflow:rules n'inclut pas merge_request_event | Ajouter la règle MR |
| Pipeline sur branche de feature non voulu | workflow:rules accepte tous les push | Ajouter $CI_OPEN_MERGE_REQUESTS check |
| Merge bloqué "pipeline required" | Le pipeline MR est manquant ou en échec | Vérifier que le workflow crée bien un pipeline MR |
| Merge train qui ne démarre pas | Feature non activée dans les settings | Activer dans Settings > Merge requests |
| Variables MR non disponibles dans un pipeline push | $CI_MERGE_REQUEST_IID n'existe que dans les MR pipelines | Utiliser le bon CI_PIPELINE_SOURCE |
À retenir
Section intitulée « À retenir »workflow:rulesest le premier filtre, il décide si le pipeline est créé ou non- Le workflow MR (merge_request_event + default branch + tags) est recommandé pour les équipes
- Sans workflow, GitLab crée des doublons (un pipeline push + un pipeline MR)
- Utilisez
$CI_OPEN_MERGE_REQUESTSpour le pattern hybride (branch puis MR) - Les merged results testent le résultat anticipé du merge, pas la branche seule
- Les merge trains sérialisent les MR pour garder
maintoujours verte - Ne déployez jamais depuis un pipeline MR, uniquement après merge ou sur tag
- Les review apps créent un environnement temporaire par MR avec nettoyage automatique
On passe à la pratique
Section intitulée « On passe à la pratique »Appliquez ces règles dans le Lab 17, Workflows branches et MR.
Testez vos connaissances
Section intitulée « Testez vos connaissances »Dix questions pour vérifier que les notions de ce guide sont acquises : origine des doublons, ordre d'évaluation des règles et choix du workflow selon le contexte.
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