GitLab CI : rules, cache et pipelines DAG
🎯 Objectifs
À la fin de ce module, vous serez capable de :
- ✅ Utiliser
rules:pour contrôler l'exécution des jobs - ✅ Optimiser les pipelines avec le cache
- ✅ Lancer des services (bases de données) aux côtés des jobs
- ✅ Créer des pipelines DAG rapides avec
needs: - ✅ Réutiliser des configurations avec
extends:et les ancres YAML - ✅ Planifier et déclencher des pipelines
📋 Prérequis
| Requis | Niveau |
|---|---|
GitLab CI (.gitlab-ci.yml basique) | Complété (module débutant) |
| Docker | Débutant |
| Git | Intermédiaire |
📏 Rules : contrôler l'exécution des jobs
rules: est la méthode moderne pour décider quand un job s'exécute (remplace only/except).
Syntaxe de base
deploy:
stage: deploy
script:
- ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: always
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
- when: neverLes règles sont évaluées dans l'ordre - la première qui matche est appliquée.
Conditions courantes
| Condition | Quand |
|---|---|
$CI_COMMIT_BRANCH == "main" | Push sur la branche main |
$CI_PIPELINE_SOURCE == "merge_request_event" | Pipeline déclenché par une Merge Request |
$CI_COMMIT_TAG | Un tag Git est présent |
$CI_PIPELINE_SOURCE == "schedule" | Pipeline planifié (cron) |
$CI_PIPELINE_SOURCE == "web" | Déclenché manuellement depuis l'interface |
Filtre par fichiers modifiés
test-frontend:
rules:
- changes:
- "frontend/**"
- "package.json"
script:
- npm testLe job ne s'exécute que si des fichiers dans frontend/ ou package.json ont changé.
Valeurs de when
| Valeur | Comportement |
|---|---|
always | Le job s'exécute automatiquement |
manual | Bouton de déclenchement dans l'interface |
never | Le job est exclu du pipeline |
delayed | Le job s'exécute après un délai (avec start_in:) |
💡 Terminez toujours vos
rules:par- when: neverpour éviter les exécutions involontaires.
⚡ Cache
Le cache réutilise des fichiers entre exécutions pour accélérer les pipelines.
Cache vs Artefacts
| Aspect | Cache | Artefacts |
|---|---|---|
| But | Accélérer les jobs | Passer des fichiers entre stages |
| Persistance | Entre pipelines | Dans le même pipeline |
| Fiabilité | Best-effort | Garanti |
| Usage typique | node_modules/, .pip/ | dist/, rapports de tests |
Configuration du cache
test:
image: node:20-alpine
cache:
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
policy: pull-push
script:
- npm ci
- npm test| Paramètre | Rôle |
|---|---|
key | Identifiant du cache (un cache par valeur) |
paths | Dossiers à mettre en cache |
policy | pull-push (défaut), pull (lecture seule), push (écriture seule) |
Clés de cache dynamiques
cache:
key:
files:
- package-lock.json
paths:
- node_modules/La clé est générée à partir du hash du fichier lock. Si les dépendances changent, le cache est recréé.
Cache par branche
cache:
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/$CI_COMMIT_REF_SLUG crée un cache distinct par branche → pas de conflit entre branches.
💡 Utilisez
policy: pullsur les jobs qui lisent le cache sans le modifier (ex: tests) pour gagner du temps.
🐘 Services
Les services lancent des conteneurs auxiliaires accessibles depuis votre job (bases de données, Redis, etc.).
PostgreSQL pour les tests
test:
image: node:20-alpine
services:
- name: postgres:15
alias: db
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
DATABASE_URL: "postgresql://testuser:testpass@db:5432/testdb"
script:
- npm ci
- npm testLe service est accessible à l'adresse db (l'alias) ou postgres (le nom de l'image).
Redis pour le cache applicatif
test:
image: python:3.12
services:
- redis:7
variables:
REDIS_URL: "redis://redis:6379"
script:
- pip install -r requirements.txt
- pytestPlusieurs services simultanés
test:
image: node:20-alpine
services:
- postgres:15
- redis:7
- name: elasticsearch:8.11.0
alias: es
command: ["elasticsearch", "-Ediscovery.type=single-node"]
script:
- npm ci
- npm test💡 Le nom de l'hôte pour joindre un service est le nom de l'image (sans le tag) ou l'alias si défini.
🔀 Pipelines DAG avec needs:
Par défaut, les stages s'exécutent séquentiellement (tout le stage précédent doit finir). needs: permet de créer un graphe de dépendances (DAG) pour plus de parallélisme.
Sans needs: (séquentiel par stage)
Stage lint: [lint-js] [lint-css] ← attend les deux
Stage test: [test-js] [test-css] ← attend les deux
Stage build: [build]Avec needs: (DAG - plus rapide)
lint-js:
stage: lint
script: npm run lint
lint-css:
stage: lint
script: npm run lint:css
test-js:
stage: test
needs: ["lint-js"]
script: npm test
test-css:
stage: test
needs: ["lint-css"]
script: npm run test:css
build:
stage: build
needs: ["test-js", "test-css"]
script: npm run buildlint-js → test-js ─┐
├→ build
lint-css → test-css ┘test-js démarre dès que lint-js finit, sans attendre lint-css.
| Avantage | Explication |
|---|---|
| Pipeline plus rapide | Les jobs n'attendent que leurs vraies dépendances |
| Feedback ciblé | Vous savez exactement quel chemin a échoué |
| Contrôle fin | Chaque job déclare ses propres prérequis |
💡 Combiné avec
artifacts,needs:télécharge automatiquement les artefacts des jobs dont il dépend.
📐 Templates avec extends:
Réutilisez des configurations communes avec extends: pour éviter la duplication.
Définir un template
.node-base:
image: node:20-alpine
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
before_script:
- npm ciLe point (.) au début du nom rend le job caché - il ne s'exécute pas, il sert uniquement de template.
Utiliser le template
lint:
extends: .node-base
stage: lint
script:
- npm run lint
test:
extends: .node-base
stage: test
script:
- npm test
build:
extends: .node-base
stage: build
script:
- npm run build
artifacts:
paths:
- dist/Les trois jobs héritent de image, cache et before_script sans duplication.
Ancres YAML (&, *)
Pour réutiliser des blocs spécifiques (pas un job entier) :
.cache-config: &node-cache
key:
files:
- package-lock.json
paths:
- node_modules/
test:
image: node:20-alpine
cache:
<<: *node-cache
policy: pull
script:
- npm ci
- npm test💡 Préférez
extends:pour réutiliser des jobs entiers et les ancres YAML pour réutiliser des blocs à l'intérieur d'un job.
⏰ Pipelines Planifiés et Triggers
Pipelines planifiés (schedules)
Configurez dans CI/CD → Schedules un cron pour lancer des pipelines automatiquement :
nightly-test:
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
script:
- npm ci
- npm run test:e2eCas d'usage : tests end-to-end nocturnes, scans de sécurité hebdomadaires, nettoyage.
Déclenchement par API (trigger)
Déclenchez un pipeline via l'API REST :
curl --request POST \
--form "token=$TRIGGER_TOKEN" \
--form "ref=main" \
"https://gitlab.com/api/v4/projects/$PROJECT_ID/trigger/pipeline"Trigger depuis un autre pipeline
trigger-deploy:
stage: deploy
trigger:
project: group/deploy-project
branch: main
strategy: depend| Paramètre | Rôle |
|---|---|
project | Projet cible à déclencher |
branch | Branche sur laquelle lancer le pipeline |
strategy: depend | Le job attend que le pipeline déclenché finisse |
💡 Les triggers sont parfaits pour les architectures micro-services où chaque projet a son propre pipeline.
🔧 Pipeline Complet Intermédiaire
stages:
- lint
- test
- build
- deploy
# Template réutilisable
.node-base:
image: node:20-alpine
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
before_script:
- npm ci
lint:
extends: .node-base
stage: lint
script:
- npm run lint
allow_failure: true
test:unit:
extends: .node-base
stage: test
needs: ["lint"]
services:
- postgres:15
variables:
POSTGRES_DB: testdb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
script:
- npm test
artifacts:
reports:
junit: test-results.xml
test:e2e:
extends: .node-base
stage: test
needs: ["lint"]
script:
- npm run test:e2e
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- when: never
build:
extends: .node-base
stage: build
needs: ["test:unit"]
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
deploy:
stage: deploy
needs: ["build"]
script:
- echo "Déploiement de dist/..."
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
- when: neverFlux d'exécution (DAG)
lint → test:unit → build → deploy (manuel, main seulement)
↘ test:e2e❌ Erreurs Courantes
| Erreur | Cause | Solution |
|---|---|---|
| Job ne s'exécute jamais | rules: trop restrictives | Ajoutez un - when: on_success comme fallback |
| Cache manquant | Clé qui ne matche pas | Vérifiez la clé avec key: files: |
| Service inaccessible | Mauvais hostname | Utilisez le nom de l'image ou l'alias |
needs: job introuvable | Nom de job avec erreur | Vérifiez l'orthographe exacte |
extends: ne fonctionne pas | Job template sans le point (.) | Préfixez le nom avec . |
📌 Points Clés
rules:remplaceonly/except- plus flexible et lisible- Le cache accélère les pipelines entre exécutions (best-effort)
- Les services lancent des conteneurs auxiliaires (DB, Redis) pour les tests
needs:crée des pipelines DAG plus rapides que l'ordre séquentiel des stagesextends:et les ancres YAML éliminent la duplication de configuration- Les schedules et triggers automatisent les pipelines sans push
📚 Ressources
🚀 Prochaines étapes
Vous maîtrisez les pipelines intermédiaires avec GitLab CI. Passez au niveau avancé :
- GitLab CI avancé : review apps, cache et multi-projets - Variables protégées, environnements, Container Registry et templates partagés