Aller au contenu
CI/CD & Automatisation medium

Lab 19 — Capstone industriel

11 min de lecture

logo gitlab

Vous arrivez au capstone du bloc industrialisation. Le starter est volontairement minimal : pas de pipeline prêt à l’emploi, uniquement un cahier des charges. Votre mission est de reconstruire une CI complète, cohérente et robuste, comme dans un vrai contexte de reprise ou de migration.

  • Traduire un cahier des charges CI en implémentation concrète
  • Structurer une solution modulaire sans dépendre d’un tutoriel pas à pas
  • Vérifier la cohérence globale d’un pipeline complexe
  • Faire un auto-audit par comparaison avec une branche de référence

Ce type d’exercice reflète les situations réelles où vous devez reprendre un pipeline partiellement absent ou obsolète, puis le remettre au niveau des standards d’équipe.

Vous êtes dans un contexte de :

  • migration vers une nouvelle base CI ;
  • onboarding d’un projet sans historisation claire ;
  • évaluation de compétence DevOps orientée production.
  1. Basculez sur le starter capstone

    Fenêtre de terminal
    cd pipeline-craft
    git checkout starter/lab-19
  2. Lisez le cahier des charges

    Fenêtre de terminal
    cat CAPSTONE-LAB-19.md
  3. Constatez l’état initial

    Le starter ne contient pas de .gitlab-ci.yml prêt à exécuter. C’est normal.

Vous devez livrer une CI complète à partir d’un état vierge, tout en respectant les contraintes d’industrialisation définies dans les labs précédents.

  • Pipeline multi-stages : lint, test, orchestrate, build, deploy
  • Templates locaux en ci/*.yml
  • Matrice de tests Python (3.11, 3.12)
  • Orchestration parent-enfant avec YAML généré en artifact
  • Workflow MR/branche/tag cohérent et non redondant
  • Robustesse : retry, timeout, interruptible, resource_group
  • Contrat de pipeline vérifiable via un job dédié
  1. Créez .gitlab-ci.yml orchestrateur

  2. Créez les fichiers ci/lint.yml, ci/test.yml, ci/orchestration.yml, ci/build.yml, ci/deploy.yml, ci/quality.yml

  3. Ajoutez le script de génération enfant scripts/generate-child-pipeline.sh

    Indice : le starter ne contient aucun fichier CI, vous partez d’une base vierge.

👉 Vérifier votre solution (Étape 1)
.gitlab-ci.yml
ci/
lint.yml
test.yml
orchestration.yml
build.yml
deploy.yml
quality.yml
scripts/
generate-child-pipeline.sh

Le script doit être exécutable pour que le job d’orchestration fonctionne.

Étape 2 — Implémenter les comportements attendus

Section intitulée « Étape 2 — Implémenter les comportements attendus »
  1. Branchez la matrice de tests et ses dépendances
  2. Déclenchez correctement le child pipeline
  3. Appliquez les règles workflow et la fiabilité sur build/deploy
  4. Ajoutez un job de contrat pour valider la présence des composants CI
👉 Vérifier votre solution (Étape 2)
  • workflow MR-first avec anti-duplication branche/MR ;
  • matrice pytest-matrix (3.11-slim, 3.12-slim x core, api) ;
  • orchestration parent-enfant (generate-child-pipeline -> run-child-pipeline) ;
  • docker-build attend pytest-matrix et run-child-pipeline ;
  • déploiements fiabilisés avec resource_group, retry, timeout, interruptible: false ;
  • pipeline-contract vérifie les fichiers de la CI.
  1. Validez le pipeline

    Fenêtre de terminal
    glab ci lint .gitlab-ci.yml
  2. Commit et push

    Fenêtre de terminal
    git add -A
    git commit -m "ci: complete lab-19 industrial capstone"
    git push origin starter/lab-19
  3. Comparez avec la référence

    Fenêtre de terminal
    git diff starter/lab-19..solution/lab-19
👉 Vérifier votre solution (Étape 3)
  • le lint GitLab passe sans erreur ;
  • les jobs des 5 stages s’affichent (lint, test, orchestrate, build, deploy) ;
  • la comparaison starter/lab-19..solution/lab-19 montre un comportement équivalent.
📄 Voir le fichier .gitlab-ci.yml complet
stages:
- lint
- test
- orchestrate
- build
- deploy
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_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/orchestration.yml
- local: ci/build.yml
- local: ci/deploy.yml
- local: ci/quality.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-matrix:
extends: .python-base
stage: test
parallel:
matrix:
- PYTHON_VERSION: ["3.11-slim", "3.12-slim"]
TEST_SUITE: ["core", "api"]
image: python:$PYTHON_VERSION
needs: []
before_script:
- pip install -r requirements-dev.txt
script:
- |
if [ "$TEST_SUITE" = "core" ]; then
pytest -v tests/test_health.py tests/test_version.py --junitxml=report.xml --cov=app --cov-report=term-missing
else
pytest -v tests/test_items.py --junitxml=report.xml --cov=app --cov-report=term-missing
fi
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/orchestration.yml complet
generate-child-pipeline:
stage: orchestrate
image: alpine:3.20
before_script:
- apk add --no-cache bash git
script:
- chmod +x scripts/generate-child-pipeline.sh
- ./scripts/generate-child-pipeline.sh
artifacts:
paths:
- child-pipeline.yml
expire_in: 1 day
run-child-pipeline:
stage: orchestrate
needs:
- generate-child-pipeline
trigger:
include:
- artifact: child-pipeline.yml
job: generate-child-pipeline
strategy: depend
rules:
- if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
📄 Voir le fichier ci/build.yml complet
.docker-vars: &docker_vars
DOCKER_TLS_CERTDIR: "/certs"
docker-build:
stage: build
needs:
- pytest-matrix
- run-child-pipeline
image: docker:27
services:
- docker:27-dind
variables:
<<: *docker_vars
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
timeout: 20m
interruptible: true
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
resource_group: staging
retry:
max: 1
when:
- runner_system_failure
timeout: 10m
interruptible: false
rules:
- if: $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train"
- 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
resource_group: production
retry:
max: 1
when:
- runner_system_failure
timeout: 15m
interruptible: false
rules:
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
📄 Voir le fichier ci/quality.yml complet
pipeline-contract:
stage: lint
image: alpine:3.20
script:
- test -f .gitlab-ci.yml
- test -f ci/lint.yml
- test -f ci/test.yml
- test -f ci/orchestration.yml
- test -f ci/build.yml
- test -f ci/deploy.yml
- test -x scripts/generate-child-pipeline.sh
- echo "Pipeline contract checks passed"
📄 Voir le fichier scripts/generate-child-pipeline.sh complet
#!/usr/bin/env bash
set -euo pipefail
# Dynamic child pipeline used in Lab 16.
# It adapts jobs according to branch context.
cat > child-pipeline.yml <<'YAML'
stages:
- verify
child-smoke:
stage: verify
image: alpine:3.20
script:
- echo "Smoke check in child pipeline"
YAML
if [[ "${CI_COMMIT_BRANCH:-}" == "${CI_DEFAULT_BRANCH:-}" ]]; then
cat >> child-pipeline.yml <<'YAML'
child-default-branch-check:
stage: verify
image: alpine:3.20
script:
- echo "Extra checks on default branch"
YAML
fi
cat child-pipeline.yml
  • Le pipeline lint passe sur la branche capstone
  • Les 5 stages attendus sont bien présents
  • Les templates locaux sont utilisés
  • Le child pipeline est fonctionnel
  • Les garde-fous de fiabilité sont appliqués

Le piège principal est d’oublier un sous-ensemble de contraintes (par exemple workflow correct mais fiabilité absente). Utilisez une checklist stricte et validez point par point.

Autre piège : vouloir reproduire exactement la solution ligne à ligne. L’objectif est d’atteindre un comportement équivalent, pas une copie textuelle parfaite.

  • Un capstone valide la maîtrise d’ensemble, pas un fragment de compétence
  • Un cahier des charges CI doit se traduire en critères observables
  • La comparaison avec solution/lab-19 est un outil d’audit, pas un raccourci
  • La robustesse industrielle repose sur cohérence + validation systématique

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