Aller au contenu
CI/CD & Automatisation medium

Lab 14 — Externaliser les templates CI

9 min de lecture

logo gitlab

Votre YAML est plus propre depuis le lab précédent, mais il reste concentré dans un seul fichier. Dès que plusieurs équipes ou projets doivent partager la même base CI, cette approche atteint ses limites. Dans ce lab, vous passez d’un pipeline DRY local à une structure modulaire avec include: local.

  • Structurer un pipeline en plusieurs fichiers CI cohérents
  • Utiliser include: local pour charger des templates internes au repo
  • Réduire la taille du fichier racine sans perdre en lisibilité
  • Préparer une future mutualisation vers include: project

Ce refactoring intervient quand un pipeline devient une base partagée. Tant que vous êtes seul sur un projet, un gros .gitlab-ci.yml reste gérable. Quand plusieurs services veulent réutiliser les mêmes patterns (lint, test, build, deploy), le fichier unique devient un goulot de maintenance.

Ce lab sert dans des situations concrètes :

  • vous maintenez plusieurs microservices avec une CI très proche ;
  • votre équipe veut isoler les responsabilités par fichier ;
  • vous préparez une industrialisation vers un dépôt de templates commun.
  1. Basculez sur la branche de travail

    Fenêtre de terminal
    cd pipeline-craft
    git checkout starter/lab-14
  2. Ouvrez le pipeline racine

    Vérifiez que la majorité des jobs est encore dans .gitlab-ci.yml.

  3. Lancez un run baseline

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

Un fichier unique rallonge les reviews, augmente le risque de conflit Git et rend la réutilisation difficile. Vous devez découper sans casser la logique d’exécution.

  1. Créez le dossier CI si nécessaire

    Fenêtre de terminal
    mkdir -p ci
  2. Créez les fichiers de base

    Fenêtre de terminal
    touch ci/lint.yml ci/test.yml ci/build.yml ci/deploy.yml
  3. Déplacez les jobs vers leur fichier logique

    Indice : gardez les jobs Python ensemble, et les jobs Docker/deploy dans leurs fichiers dédiés.

👉 Vérifier votre solution (Étape 1)

Répartition attendue :

  • ci/lint.yml : .python-base et ruff-lint
  • ci/test.yml : pytest
  • ci/build.yml : .docker-vars et docker-build
  • ci/deploy.yml : deploy-staging et deploy-production

Extrait clé pour ci/lint.yml :

.python-base:
image: python:3.12-slim
cache:
key:
files:
- requirements-dev.txt
paths:
- .pip-cache/
ruff-lint:
extends: .python-base
stage: lint
cache:
policy: pull
before_script:
- pip install ruff
script:
- ruff check app/ tests/

Étape 2 — Transformer le fichier racine en orchestrateur

Section intitulée « Étape 2 — Transformer le fichier racine en orchestrateur »
  1. Gardez uniquement le squelette global

    Indice : conservez stages, workflow, default, variables.

  2. Ajoutez les includes locaux

    Indice : un include par fichier, avec des chemins relatifs à la racine du dépôt.

  3. Conservez les blocs globaux pertinents

    Par exemple workflow, default, variables.

👉 Vérifier votre solution (Étape 2)

Le fichier racine final ne contient plus les jobs, uniquement l’orchestration globale et les includes.

stages:
- lint
- test
- build
- deploy
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
default:
interruptible: true
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"
include:
- local: ci/lint.yml
- local: ci/test.yml
- local: ci/build.yml
- local: ci/deploy.yml
  1. Validez la configuration

    Fenêtre de terminal
    glab ci lint .gitlab-ci.yml
  2. Committez les fichiers extraits

    Fenêtre de terminal
    git add .gitlab-ci.yml ci/
    git commit -m "ci: split monolithic pipeline into local templates"
    git push origin starter/lab-14
  3. Vérifiez que le pipeline reste vert

    Le résultat fonctionnel doit être identique à la baseline.

👉 Vérifier votre solution (Étape 3)

Après découpage, vous devez conserver :

  • les mêmes stages (lint, test, build, deploy) ;
  • les mêmes scripts de jobs ;
  • les mêmes règles de déploiement (default branch, manual pour prod) ;
  • les mêmes artefacts de test (report.xml).

Si le pipeline change de comportement, le découpage n’est pas encore fidèle.

📄 Voir le fichier .gitlab-ci.yml complet
stages:
- lint
- test
- build
- deploy
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
default:
interruptible: true
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"
include:
- local: ci/lint.yml
- local: ci/test.yml
- local: ci/build.yml
- local: ci/deploy.yml
📄 Voir le fichier ci/lint.yml complet
.python-base:
image: python:3.12-slim
cache:
key:
files:
- requirements-dev.txt
paths:
- .pip-cache/
ruff-lint:
extends: .python-base
stage: lint
cache:
policy: pull
before_script:
- pip install ruff
script:
- ruff check app/ tests/
📄 Voir le fichier ci/test.yml complet
pytest:
extends: .python-base
stage: test
needs: []
before_script:
- pip install -r requirements-dev.txt
script:
- pytest -v --junitxml=report.xml --cov=app --cov-report=term-missing
coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
artifacts:
when: always
paths:
- report.xml
reports:
junit: report.xml
expire_in: 7 days
📄 Voir le fichier ci/build.yml complet
.docker-vars: &docker_vars
DOCKER_TLS_CERTDIR: "/certs"
docker-build:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
<<: *docker_vars
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
📄 Voir le fichier ci/deploy.yml complet
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
  • .gitlab-ci.yml est un orchestrateur lisible
  • Les jobs sont répartis dans ci/lint.yml, ci/test.yml, ci/build.yml, ci/deploy.yml
  • Les includes locaux se résolvent sans erreur
  • Le pipeline garde le même comportement global

L’erreur la plus courante est un mauvais chemin dans include: local:. Le chemin est relatif à la racine du dépôt, pas au fichier courant.

Autre piège : déplacer des anchors YAML vers un autre fichier en supposant qu’elles restent visibles partout. Les anchors sont souvent limitées au contexte du fichier évalué.

  • Un pipeline modulaire est plus simple à maintenir qu’un monolithe YAML
  • include: local est la première étape vers la réutilisation multi-projets
  • Le fichier racine doit rester un point d’orchestration, pas un dump de jobs
  • Découper ne doit pas changer le comportement métier du pipeline

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