
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.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- 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
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »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.
Prérequis
Section intitulée « Prérequis »- Lab 18 — Fiabilité et robustesse terminé
- Avoir relu Synthèse pipeline et Capstone industriel
Point de départ
Section intitulée « Point de départ »-
Basculez sur le starter capstone
Fenêtre de terminal cd pipeline-craftgit checkout starter/lab-19 -
Lisez le cahier des charges
Fenêtre de terminal cat CAPSTONE-LAB-19.md -
Constatez l’état initial
Le starter ne contient pas de
.gitlab-ci.ymlprêt à exécuter. C’est normal.
Le problème
Section intitulée « Le problème »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.
L’exercice
Section intitulée « L’exercice »Cahier des charges à couvrir
Section intitulée « Cahier des charges à couvrir »- 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é
Étape 1 — Reconstruire la structure
Section intitulée « Étape 1 — Reconstruire la structure »-
Créez
.gitlab-ci.ymlorchestrateur -
Créez les fichiers
ci/lint.yml,ci/test.yml,ci/orchestration.yml,ci/build.yml,ci/deploy.yml,ci/quality.yml -
Ajoutez le script de génération enfant
scripts/generate-child-pipeline.shIndice : le starter ne contient aucun fichier CI, vous partez d’une base vierge.
👉 Vérifier votre solution (Étape 1)
1️⃣ Structure cible minimale
Section intitulée « 1️⃣ Structure cible minimale ».gitlab-ci.ymlci/ lint.yml test.yml orchestration.yml build.yml deploy.yml quality.ymlscripts/ generate-child-pipeline.shLe 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 »- Branchez la matrice de tests et ses dépendances
- Déclenchez correctement le child pipeline
- Appliquez les règles workflow et la fiabilité sur build/deploy
- Ajoutez un job de contrat pour valider la présence des composants CI
👉 Vérifier votre solution (Étape 2)
1️⃣ Comportements clés attendus
Section intitulée « 1️⃣ Comportements clés attendus »workflowMR-first avec anti-duplication branche/MR ;- matrice
pytest-matrix(3.11-slim,3.12-slimxcore,api) ; - orchestration parent-enfant (
generate-child-pipeline->run-child-pipeline) ; docker-buildattendpytest-matrixetrun-child-pipeline;- déploiements fiabilisés avec
resource_group,retry,timeout,interruptible: false; pipeline-contractvérifie les fichiers de la CI.
Étape 3 — Valider puis comparer
Section intitulée « Étape 3 — Valider puis comparer »-
Validez le pipeline
Fenêtre de terminal glab ci lint .gitlab-ci.yml -
Commit et push
Fenêtre de terminal git add -Agit commit -m "ci: complete lab-19 industrial capstone"git push origin starter/lab-19 -
Comparez avec la référence
Fenêtre de terminal git diff starter/lab-19..solution/lab-19
👉 Vérifier votre solution (Étape 3)
1️⃣ Résultats attendus
Section intitulée « 1️⃣ Résultats attendus »- 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-19montre un comportement équivalent.
Le fichier complet
Section intitulée « Le fichier complet »📄 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 bashset -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"YAMLfi
cat child-pipeline.ymlVérification
Section intitulée « Vérification »- 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
Pièges fréquents
Section intitulée « Pièges fréquents »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.
À retenir
Section intitulée « À retenir »- 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-19est un outil d’audit, pas un raccourci - La robustesse industrielle repose sur cohérence + validation systématique