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

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, mais que le build et le déploiement n’aient lieu que sur main
  • 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
Push sur main✅ (auto)✅ (manuel)
Tag Git✅ (manuel)

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 — Contrôler quand le pipeline démarre

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

    workflow:
    rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_TAG

    Ce bloc définit les trois seuls cas où un pipeline doit démarrer :

    • $CI_MERGE_REQUEST_IID : cette variable est définie quand le push fait partie d’une Merge Request ouverte. Le pipeline associé s’appelle un “MR pipeline”.
    • $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH : $CI_DEFAULT_BRANCH vaut main par défaut. Cette règle déclenche le pipeline uniquement quand on pousse sur main.
    • $CI_COMMIT_TAG : cette variable est définie quand on pousse un tag Git (git tag v1.0 && git push --tags). Utile pour déclencher des déploiements sur des releases.

    Conséquence : un développeur qui pousse sur feature/ma-fonctionnalite sans MR ouverte ne déclenche aucun pipeline. Dès qu’il ouvre une MR, le pipeline démarre.

  1. Ajoutez des rules: sur le job ruff-lint

    Le lint doit tourner sur les MR et sur main :

    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_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  2. Ajoutez les mêmes rules: sur le job pytest

    pytest:
    ...
    rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  3. Ajoutez des rules: plus restrictives sur le job docker-build

    Le build Docker ne doit se déclencher que sur main ou quand des fichiers critiques changent :

    docker-build:
    stage: build
    image: docker:27
    services:
    - docker:27-dind
    variables:
    DOCKER_TLS_CERTDIR: "/certs"
    script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - docker build -t $CI_REGISTRY_IMAGE:latest .
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest
    rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - changes:
    - app/**/*
    - Dockerfile
    - requirements.txt

    La règle changes: est intéressante : elle déclenche le job uniquement si l’un des fichiers listés a été modifié dans le commit. Si vous poussez sur main en ne modifiant que la documentation, le build Docker ne tourne pas.

  1. Ajoutez deploy dans la liste des stages

    stages:
    - lint
    - test
    - build
    - deploy
  2. Ajoutez le job de déploiement en staging (automatique sur main)

    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

    Ce job utilise alpine:3.20, une image très légère (~5 Mo) adaptée aux scripts shell simples. Le script deploy-demo.sh est fourni dans le dépôt — il simule un déploiement (n’ouvre pas de vraie connexion).

  3. Ajoutez le job de déploiement en production (manuel sur main)

    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

    La clé when: manual est le détail crucial. Sans elle, le job démarre automatiquement. Avec elle, GitLab affiche un bouton ▶ dans l’interface — le job ne démarre que quand quelqu’un clique dessus. C’est la façon la plus simple d’ajouter une validation humaine avant un déploiement en production.

stages:
- lint
- test
- build
- deploy
workflow:
rules:
- if: $CI_MERGE_REQUEST_IID
- 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_MERGE_REQUEST_IID
- 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_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
docker-build:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker build -t $CI_REGISTRY_IMAGE:latest .
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- changes:
- app/**/*
- Dockerfile
- requirements.txt
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, mais pas docker-build ni les jobs de déploiement.

  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 uniquement lint + tests
  • 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
stages:
- lint
- test
- build
- deploy
workflow:
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
ruff-lint:
...
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
pytest:
...
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
docker-build:
...
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- changes:
- app/**/*
- Dockerfile
- requirements.txt
deploy-staging:
stage: deploy
...
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-production:
stage: deploy
...
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
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 starter/lab-06..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_MERGE_REQUEST_IID (pipeline de MR), $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