Aller au contenu
CI/CD & Automatisation medium

Lab 06 — Contrôler l'exécution

16 min de lecture

logo gitlab

Votre pipeline s'exécute sur chaque push, peu importe la branche. Un développeur qui pousse 10 commits sur une branche de feature déclenche 10 builds Docker complets et 10 docker push — pour rien. Et le déploiement en production s'exécute automatiquement dès qu'un commit atterrit sur main, sans validation humaine. Dans ce lab, vous allez mettre des conditions d'exécution sur le pipeline et les jobs pour qu'ils ne tournent qu'au bon moment.

  • Comprendre workflow: rules pour contrôler quand un pipeline démarre
  • Écrire des rules: par job avec des conditions sur la branche, l'événement ou les fichiers modifiés
  • Créer un job manuel avec when: manual pour valider un déploiement en production
  • Distinguer un pipeline de Merge Request d'un pipeline de branche

Ce lab est la suite logique du Lab 05. Si vous débutez sur GitLab CI/CD, faites 01 à 05 avant de traiter les rules. Si vous êtes déjà à l'aise avec la syntaxe YAML, vous pouvez démarrer directement sur starter/lab-06.

Un pipeline sans condition d'exécution est un pipeline qui fait trop de choses trop souvent. Sur un projet avec 5 développeurs qui poussent chacun 5 commits par jour sur leurs branches, ça représente 25 builds Docker inutiles chaque jour. Les runners partagés de gitlab.com ont des quotas de minutes CI — mieux vaut ne pas les gaspiller.

Plus important encore : un déploiement en production qui se déclenche automatiquement à chaque merge est risqué. En pratique, toutes les équipes mettent un frein au moins sur la production : quelqu'un doit valider manuellement avant que le code parte en prod.

Situations réelles où ce lab vous aide :

  • Votre quota de minutes CI est épuisé mi-mois à cause de builds inutiles sur des branches de feature
  • Un merge accidentel sur main a déclenché un déploiement en production avant que vous puissiez réagir
  • Vous voulez que le lint et les tests tournent sur les MR, avec un build Docker seulement quand des fichiers critiques changent
  • Vous cherchez à ajouter un bouton « déployer en production » dans GitLab sans plugins supplémentaires
  1. Passez sur la branche de départ

    Fenêtre de terminal
    cd pipeline-craft
    git checkout starter/lab-06
  2. Poussez pour déclencher le pipeline

    Fenêtre de terminal
    git push origin starter/lab-06

    Le pipeline démarre sur cette branche de feature — et déclenche un docker push complet, alors que vous ne testez que des règles CI. C'est exactement le comportement que vous allez corriger.

Le .gitlab-ci.yml de départ ne contient aucune condition d'exécution. Chaque push, sur n'importe quelle branche, déclenche l'intégralité du pipeline : lint, test, build et push Docker.

Le pipeline dont vous avez besoin doit se comporter différemment selon le contexte :

ContexteLint + TestsBuild DockerDéploiement stagingDéploiement prod
Push sur une branche de feature
Merge Request ouverte✅ si fichiers critiques modifiés
Push sur main✅ (auto)✅ (manuel)
Tag Git

Pour obtenir ce comportement, vous allez utiliser deux mécanismes de GitLab CI : les workflow: rules (qui contrôlent si un pipeline démarre) et les rules: par job (qui contrôlent si un job s'exécute dans un pipeline démarré).

Étape 1 — À vous de contrôler quand le pipeline démarre

Section intitulée « Étape 1 — À vous de contrôler quand le pipeline démarre »
  1. Ajoutez un bloc workflow: rules en haut du fichier, après stages: et variables:.

  2. Définissez trois cas de démarrage du pipeline :

    • pipeline de Merge Request
    • push sur la branche par défaut
    • push de tag Git
  3. Vérifiez le comportement attendu :

    • push feature sans MR => pas de pipeline
    • ouverture de MR => pipeline déclenché
👉 Vérifier votre solution (Étape 1)

Placez ce bloc après stages: et avant les jobs :

workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
  • $CI_PIPELINE_SOURCE == "merge_request_event" : démarre un pipeline de Merge Request
  • $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH : démarre un pipeline sur main
  • $CI_COMMIT_TAG : démarre un pipeline pour un tag Git
ContextePipeline démarre ?
Push sur feature/foo sans MR❌ Non
Ouverture ou mise à jour d'une MR✅ Oui
Push direct sur main✅ Oui
Création d'un tag Git✅ Oui

Le point important : workflow: décide si GitLab crée un pipeline. Si aucune règle ne matche, le pipeline n'existe même pas.

  1. Ajoutez des rules: sur ruff-lint et pytest pour les exécuter sur :

    • Merge Request
    • branche par défaut
  2. Ajoutez des rules: plus restrictives sur docker-build :

    • exécution sur branche par défaut
    • déclenchement aussi quand des fichiers critiques changent
  3. Vérifiez la logique de sélection :

    • lint/tests restent actifs sur MR
    • build Docker reste lié à main ou à une modification applicative significative
👉 Vérifier votre solution (Étape 2)

1️⃣ Ajouter des rules: sur ruff-lint et pytest

Section intitulée « 1️⃣ Ajouter des rules: sur ruff-lint et pytest »
ruff-lint:
stage: lint
image: python:3.12-slim
cache:
key:
files:
- requirements-dev.txt
paths:
- .pip-cache/
policy: pull
before_script:
- pip install ruff
script:
- ruff check app/ tests/
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
pytest:
stage: test
image: python:3.12-slim
cache:
key:
files:
- requirements-dev.txt
paths:
- .pip-cache/
before_script:
- pip install -r requirements-dev.txt
script:
- pytest -v --junitxml=report.xml
artifacts:
when: always
paths:
- report.xml
reports:
junit: report.xml
expire_in: 7 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
docker-build:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- app/**/*
- Dockerfile
- requirements.txt
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
  • ruff-lint et pytest tournent sur MR et sur main
  • en MR, docker-build tourne seulement si les fichiers critiques changent
  • sur main et sur tag, docker-build tourne systématiquement
  • une modification de documentation seule ne déclenche pas docker-build en MR

Dans la branche solution/lab-06, les règles de docker-build sont explicites par contexte : MR ciblée avec changes, branche par défaut, et tag Git.

Étape 3 — À vous d'ajouter un stage de déploiement

Section intitulée « Étape 3 — À vous d'ajouter un stage de déploiement »
  1. Ajoutez le stage deploy dans la liste des stages.

  2. Créez un job de déploiement staging avec exécution automatique sur la branche par défaut.

  3. Créez un job de déploiement production en mode manuel sur la branche par défaut.

  4. Validez le résultat attendu : staging auto, production avec bouton manuel.

👉 Vérifier votre solution (Étape 3)
stages:
- lint
- test
- build
- deploy
deploy-staging:
stage: deploy
image: alpine:3.20
script:
- echo "Deploying $CI_COMMIT_SHORT_SHA to staging..."
- ./scripts/deploy-demo.sh staging
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-production:
stage: deploy
image: alpine:3.20
script:
- echo "Deploying $CI_COMMIT_SHORT_SHA to production..."
- ./scripts/deploy-demo.sh production
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
  • sur main, deploy-staging démarre automatiquement après le build
  • deploy-production apparaît avec un bouton ▶
  • tant que personne ne clique, la production ne part pas

when: manual doit être placé dans la règle qui matche le contexte. C'est ce qui ajoute une validation humaine avant l'exécution.

📄 Voir le fichier .gitlab-ci.yml complet
stages:
- lint
- test
- build
- deploy
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"
ruff-lint:
stage: lint
image: python:3.12-slim
cache:
key:
files:
- requirements-dev.txt
paths:
- .pip-cache/
policy: pull
before_script:
- pip install ruff
script:
- ruff check app/ tests/
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
pytest:
stage: test
image: python:3.12-slim
cache:
key:
files:
- requirements-dev.txt
paths:
- .pip-cache/
before_script:
- pip install -r requirements-dev.txt
script:
- pytest -v --junitxml=report.xml
artifacts:
when: always
paths:
- report.xml
reports:
junit: report.xml
expire_in: 7 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
docker-build:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- app/**/*
- Dockerfile
- requirements.txt
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
deploy-staging:
stage: deploy
image: alpine:3.20
script:
- echo "Deploying $CI_COMMIT_SHORT_SHA to staging..."
- ./scripts/deploy-demo.sh staging
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-production:
stage: deploy
image: alpine:3.20
script:
- echo "Deploying $CI_COMMIT_SHORT_SHA to production..."
- ./scripts/deploy-demo.sh production
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
  1. Committez et poussez

    Fenêtre de terminal
    git add .gitlab-ci.yml
    git commit -m "ci: add workflow rules, job rules and deploy stages"
    git push origin starter/lab-06
  2. Observez : aucun pipeline ne démarre

    La branche starter/lab-06 n'est pas main et il n'y a pas de MR ouverte. Le workflow: rules filtre ce push — aucun pipeline ne démarre.

  3. Ouvrez une Merge Request de starter/lab-06 vers main sur votre fork

    Allez dans Merge Requests > New merge request. Un pipeline démarre — il exécute ruff-lint et pytest. Le job docker-build ne s'exécute que si la MR modifie des fichiers critiques (app/**/*, Dockerfile, requirements.txt).

  4. Poussez directement sur main pour voir le pipeline complet

    Fenêtre de terminal
    git checkout main
    git merge starter/lab-06
    git push origin main

    Cette fois, tous les jobs tournent : lint, test, build Docker et déploiement staging. Le job deploy-production apparaît avec le bouton ▶ et attend votre clic.

  • Un push sur starter/lab-06 seul (sans MR) ne déclenche aucun pipeline
  • Une MR ouverte déclenche lint + tests, et docker-build seulement si des fichiers critiques changent
  • Un push sur main déclenche lint + tests + build Docker + deploy staging
  • Le job deploy-production affiche un bouton ▶ et ne s'exécute pas automatiquement
SymptômeCauseSolution
Pipeline ne démarre jamaisworkflow: rules trop restrictifVérifier que la branche ou l'événement correspond à une règle du workflow:
Job toujours skippedrules: ne correspond à aucun contexte actuelVérifier les valeurs des variables avec `env
deploy-production démarre automatiquementwhen: manual absent de la règleAjouter when: manual sur la ligne de la règle (pas au niveau du job)
Double pipeline sur les MRworkflow: manquant — GitLab crée un pipeline de MR ET un pipeline de brancheAjouter workflow: rules pour éviter les doublons
  • only/except vs rules: : les anciennes documentations utilisent only: et except:. Ces mots-clés sont dépréciés — utilisez toujours rules: qui est plus flexible et plus lisible.
  • Comparez avec solution/lab-06 : git diff origin/starter/lab-06..origin/solution/lab-06 montre les modifications complètes.
  • workflow: rules contrôle si un pipeline démarre. rules: par job contrôle si un job s'exécute dans un pipeline démarré. Les deux se complètent.
  • Les variables clés : $CI_PIPELINE_SOURCE (source du pipeline), $CI_DEFAULT_BRANCH (branche principale), $CI_COMMIT_TAG (tag Git)
  • when: manual sur une règle ajoute un bouton ▶ dans l'interface — le job ne démarre que sur clic humain. C'est la façon la plus simple d'ajouter une validation avant un déploiement
  • La règle changes: évite des builds inutiles quand seule la documentation change

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