Aller au contenu
CI/CD & Automatisation medium

Conditions d'exécution GitLab CI/CD (rules)

18 min de lecture

logo gitlab

Votre pipeline lance tous les jobs à chaque commit, même quand ce n'est pas nécessaire ? Les rules permettent de contrôler précisément quand un job s'exécute, sur quelle branche, pour quel type de changement, avec quelle approbation.

Ce guide vous montre comment optimiser vos pipelines en exécutant uniquement ce qui est nécessaire, et comment éviter le piège classique des pipelines qui "skip" sans raison apparente.

Les quatre cartes ci-dessous mènent chacune à une section du guide. Choisissez celle qui décrit votre situation actuelle : les trois premières traitent d'écriture de règles, la dernière donne des .gitlab-ci.yml complets à adapter. Si votre pipeline apparaît skipped sans message d'erreur, prenez la troisième en priorité, la cause est presque toujours dans workflow:rules et non dans les règles de vos jobs.

Avant de continuer, assurez-vous de maîtriser :

Sans rules, tous les jobs s'exécutent à chaque pipeline. C'est un gaspillage de ressources et de temps :

  • Tests complets alors que seul le README a changé
  • Build Docker alors que le code n'a pas bougé
  • Déploiement automatique sans validation humaine

Les rules permettent d'exécuter uniquement ce qui est nécessaire, quand c'est nécessaire.

La règle la plus utilisée. Teste une expression qui renvoie vrai ou faux.

job:
script: echo "Déploiement"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'

Opérateurs disponibles :

OpérateurSignificationExemple
==Égal à$VAR == "value"
!=Différent de$VAR != "skip"
=~Match regex$VAR =~ /^feature-/
!~Ne match pas regex$VAR !~ /wip/
&&ET logique$A == "x" && $B == "y"
||OU logique$A == "x" || $A == "y"

Variables les plus utilisées dans les rules :

VariableValeurCas d'usage
$CI_COMMIT_BRANCHNom de la brancheLimiter à main, develop
$CI_COMMIT_TAGNom du tag (si tag)Déployer uniquement les releases
$CI_PIPELINE_SOURCESource du pipelinepush, merge_request_event, schedule
$CI_MERGE_REQUEST_IDID de la MRJobs spécifiques aux MRs
# Exemples pratiques
rules:
# Sur la branche main uniquement
- if: '$CI_COMMIT_BRANCH == "main"'
# Sur un tag de release (v1.0.0, v2.3.4...)
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
# Uniquement pour les merge requests
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
# Pipeline planifié (cron)
- if: '$CI_PIPELINE_SOURCE == "schedule"'

Exécute le job uniquement si certains fichiers ont été modifiés dans le commit.

build_frontend:
script: npm run build
rules:
- changes:
- "frontend/**/*"
- "package.json"
- "package-lock.json"

Patterns supportés :

PatternSignification
src/*.jsFichiers .js dans src/
src/**/*.jsFichiers .js dans src/ et sous-dossiers
*.mdFichiers .md à la racine
**/*.mdTous les fichiers .md du projet

Exécute le job si un fichier existe dans le repository.

docker_build:
script: docker build -t myapp .
rules:
- exists:
- Dockerfile

Utile pour les monorepos où tous les projets n'ont pas les mêmes fichiers.

Vous pouvez combiner if, changes et exists dans une même règle :

deploy_docs:
script: ./deploy-docs.sh
rules:
# Sur main ET si des fichiers docs ont changé
- if: '$CI_COMMIT_BRANCH == "main"'
changes:
- "docs/**/*"
- "mkdocs.yml"

Attention : dans une règle, les conditions sont en ET. Pour un OU, utilisez plusieurs règles :

job:
rules:
# Sur main OU sur un tag de release
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_TAG =~ /^v\d+/'

when définit comment le job démarre si la règle matche.

Six valeurs seulement, et la nuance qui compte se situe entre never et les autres. never exclut le job du pipeline : il n'apparaît même pas dans l'interface. Les cinq autres l'incluent et se contentent de décider quand il démarre. Retenez aussi que on_success est la valeur par défaut : un job sans when explicite démarre dès que les étapes précédentes ont réussi. Dans workflow:rules, seules always et never sont acceptées.

ValeurComportement
on_successDémarre automatiquement si les jobs précédents ont réussi (défaut)
on_failureDémarre uniquement si un job précédent a échoué
alwaysDémarre toujours, quel que soit le résultat des jobs précédents
manualAttend un clic dans l'interface pour démarrer
delayedDémarre automatiquement après un délai
neverNe démarre jamais (exclut le job)

Les quatre extraits qui suivent couvrent l'essentiel de ce que vous écrirez au quotidien. Observez surtout le dernier : il montre le couple d'exclusion, une première règle when: never qui écarte les branches concernées, puis une règle finale sans condition qui rattrape tout le reste. Sans cette seconde règle, le job ne s'exécuterait nulle part, car un job dont aucune règle ne matche est simplement retiré du pipeline.

Déploiement manuel en production :

deploy_prod:
script: ./deploy.sh production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual

Notification en cas d'échec :

notify_failure:
script: ./send-slack-alert.sh
rules:
# Condition explicite + when: on_failure
- if: '$CI_PIPELINE_SOURCE'
when: on_failure

Déploiement différé (canary) :

deploy_canary:
script: ./deploy.sh canary
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: delayed
start_in: 30 minutes

Exclure explicitement :

heavy_tests:
script: ./run-all-tests.sh
rules:
# Pas sur les branches WIP
- if: '$CI_COMMIT_BRANCH =~ /^wip-/'
when: never
# Sinon, exécuter normalement
- when: on_success

workflow:rules détermine si le pipeline lui-même doit être créé. C'est différent des rules au niveau des jobs qui contrôlent chaque job individuellement.

Sans workflow:rules, GitLab crée un pipeline pour chaque push, même si aucun job ne doit s'exécuter. Cela crée du bruit dans l'interface.

Avec workflow:rules, vous pouvez empêcher la création du pipeline dans certains cas.

workflow:
rules:
# Pas de pipeline si c'est un tag (on déploie via autre workflow)
- if: '$CI_COMMIT_TAG'
when: never
# Pas de pipeline si la branche a une MR ouverte
# (le pipeline MR suffit)
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
when: never
# Pour les merge requests
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
# Pour les pushs sur les branches
- if: '$CI_COMMIT_BRANCH'

C'est le symptôme le plus déroutant de GitLab CI, parce qu'il ne produit aucun message d'erreur : le fichier est valide, le lint passe, et pourtant rien ne se lance. La cause tient à une règle implicite qu'on oublie vite : un workflow:rules dont aucune entrée ne matche empêche la création du pipeline. Il n'existe pas de comportement par défaut de repli.

Symptôme : vous poussez du code, le pipeline apparaît comme "skipped" ou ne démarre pas du tout.

Cause probable : workflow:rules empêche la création du pipeline.

Diagnostic :

  1. Vérifiez votre section workflow:rules
  2. Identifiez quelle règle matche avec vos conditions actuelles
  3. Si aucune règle ne matche, le pipeline est skipped
# ❌ Problème : ce workflow ne matche jamais pour les pushs simples
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
# ✅ Solution : ajouter les autres cas
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH'

Les deux fichiers suivants sont complets et copiables tels quels dans un .gitlab-ci.yml. Ils répondent à la demande la plus fréquente : ne pas relancer un build et une suite de tests quand seule la documentation a bougé.

Ce fichier combine les trois mécanismes vus plus haut. Le job lint n'a volontairement aucune règle : il tourne partout, parce qu'il coûte quelques secondes. Les jobs build et test sont conditionnés par changes sur src/**/*, doublé d'un if sur $CI_PIPELINE_SOURCE pour neutraliser le cas où changes est toujours vrai. Le déploiement en production est le seul à porter when: manual : le pipeline s'arrête là et attend un clic.

workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_TAG'
stages:
- lint
- build
- test
- deploy
# Lint : toujours (rapide, peu coûteux)
lint:
stage: lint
script: npm run lint
# Build : seulement si le code a changé
build:
stage: build
script: npm run build
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"'
changes:
- "src/**/*"
- "package*.json"
# Tests : seulement si le code a changé
test:
stage: test
script: npm test
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"'
changes:
- "src/**/*"
- "tests/**/*"
- "package*.json"
# Deploy staging : auto sur main
deploy_staging:
stage: deploy
script: ./deploy.sh staging
environment:
name: staging
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
# Deploy prod : manuel sur tags
deploy_prod:
stage: deploy
script: ./deploy.sh production
environment:
name: production
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+/'
when: manual

Quand une merge request est ouverte sur une branche, chaque push y crée deux pipelines : celui de la branche et celui de la MR. Ce workflow supprime le doublon grâce à $CI_OPEN_MERGE_REQUESTS, une variable qui n'est renseignée que si la branche courante est la source d'au moins une MR ouverte. L'ordre est déterminant : la règle when: never doit précéder la règle générale $CI_COMMIT_BRANCH, sinon cette dernière matche d'abord et le doublon revient.

workflow:
rules:
# Un tag déclenche son propre pipeline, pas besoin du push
- if: '$CI_COMMIT_TAG'
# Pour les MRs, utiliser le pipeline MR
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
# Pour les pushs sur branches sans MR ouverte
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
when: never
- if: '$CI_COMMIT_BRANCH'

Ce tableau se lit par le symptôme, celui que vous constatez dans l'interface GitLab. Le réflexe utile est de distinguer d'abord ce qui est skipped au niveau du pipeline de ce qui est absent au niveau du job : dans le premier cas rien n'apparaît du tout, dans le second le pipeline existe mais un job manque à l'appel. Cette distinction oriente vers workflow:rules ou vers les rules du job, et évite de modifier le mauvais fichier.

SymptômeCause probableSolution
Pipeline entier "skipped"workflow:rules ne matche pasAjouter une règle pour votre cas
Job jamais exécutéOrdre des règles (never en dernier)Mettre les exclusions en premier
Job toujours exécutéPas de règle, comportement par défautAjouter rules: explicites
changes toujours vraiPipeline schedule ou webchanges ne fonctionne qu'avec push/MR
Variables non définies dans rulesVariable inexistanteTester avec $VAR (vide = faux)

only et except figurent dans la liste des mots-clés dépréciés de GitLab et ne sont plus activement développés. Migrez vos pipelines vers rules, qui exprime les mêmes conditions avec des expressions de variables plutôt qu'avec des listes de branches.

Faites la bascule job par job jusqu'au bout. GitLab déconseille de mélanger des jobs only/except et des jobs rules dans un même pipeline : les comportements par défaut diffèrent, notamment sur les pipelines de merge request, et le résultat devient difficile à prévoir. Un job qui porterait les deux syntaxes en même temps provoque en outre une erreur de configuration.

# ❌ Ancien style (deprecated)
deploy:
script: ./deploy.sh
only:
- main
except:
- schedules
# ✅ Nouveau style
deploy:
script: ./deploy.sh
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never
- if: '$CI_COMMIT_BRANCH == "main"'
  1. rules contrôle quand un job s'exécute, workflow:rules contrôle quand le pipeline est créé.
  2. Première règle qui matche gagne, ordonnez vos règles du plus spécifique au plus général.
  3. if teste des variables, changes teste les fichiers modifiés, exists teste l'existence.
  4. when: never en premier pour exclure, when: on_success en dernier par défaut.
  5. changes ne filtre rien sur les pipelines schedule et web ni sur une nouvelle branche : faute de base de comparaison, GitLab considère tous les fichiers comme modifiés et la condition vaut vrai.
  6. Pipeline "skipped" = vérifier workflow:rules en premier.

Mettez ces règles en pratique dans le Lab 06, Contrôler l'exécution.

Dix questions tirées de la banque GitLab CI/CD, centrées sur l'ordre d'évaluation des règles et sur la frontière entre rules et workflow:rules. Comptez cinq minutes et visez 70 % pour considérer le sujet acquis. Un échec sur les questions changes est le signal qu'il faut relire l'encadré sur les pièges avant d'attaquer le lab.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
5 min.
70% requis

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

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn