
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 »Ces trois commandes ne construisent rien : elles vous placent dans les conditions de départ et vous font lire le cahier des charges. Prenez le temps de la deuxième étape, c'est le seul document qui définit ce qui sera considéré comme livré. Contrairement aux labs précédents, aucune correction progressive ne vous attend entre les étapes, l'évaluation se fait à la fin sur le pipeline complet.
-
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 »Chaque ligne de cette liste correspond à un lab du bloc industrialisation et constitue un critère observable : soit l'élément est présent dans votre pipeline et vérifiable, soit il ne l'est pas. Traitez-les comme des exigences indépendantes plutôt que comme un ordre de rédaction. La dernière, le contrat de pipeline, est celle qu'on oublie le plus souvent : c'est un job qui vérifie la structure de votre propre CI et échoue si un fichier manque.
- 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 »Commencez par le squelette de fichiers, sans écrire un seul job. Un
.gitlab-ci.yml réduit à ses stages et à ses include vaut mieux qu'un
fichier monolithique que vous découperez plus tard : la modularité se décide
au début. Créez les six fichiers ci/*.yml avec au moins un job chacun avant de
lancer un lint : un include qui pointe vers un fichier vide est rejeté par
GitLab avec le message Included file ... is empty or does not exist!.
-
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 »L'ordre proposé n'est pas arbitraire : la matrice et le child pipeline
définissent les jobs dont dépendra docker-build, et un needs qui pointe vers
un job inexistant fait échouer le lint. Traitez donc les producteurs avant les
consommateurs. Le job de contrat vient en dernier, puisqu'il vérifie une
structure qui doit déjà être en place.
- 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 avant de pousser : glab ci lint interroge l'API de votre instance et
détecte les erreurs de structure sans consommer de minutes de runner. Ajoutez
--dry-run pour aller plus loin et simuler la création du pipeline, et
--include-jobs pour obtenir la liste des jobs qui existeraient réellement,
ce qui est la meilleure façon de vérifier vos rules avant le premier push.
-
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 origin/starter/lab-19..origin/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 »Ne dépliez ces blocs qu'après avoir tenté votre propre implémentation : le but du
capstone est de produire un comportement équivalent, pas de recopier une
référence. Ils restent utiles pour un point précis qui bloque, par exemple la
syntaxe exacte du trigger vers un artefact ou la position du resource_group.
Chaque fichier correspond à un include du .gitlab-ci.yml orchestrateur.
📄 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@sha256:57cd7c3a7a273101a6485ba99423ee568157882804b1124b4dd04266317710de 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@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc 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@sha256:aa3df78ecf320f5fafdce71c659f1629e96e9de0968305fe1de670e0ca9176ce 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@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc 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@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc 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@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc 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@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc 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@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc script: - echo "Extra checks on default branch"YAMLfi
cat child-pipeline.ymlVérification
Section intitulée « Vérification »Cochez ces points sur un pipeline réellement exécuté, pas sur la lecture de votre
fichier : un .gitlab-ci.yml qui passe le lint peut très bien produire un
pipeline vide si vos rules ne correspondent à rien. Le troisième point se
vérifie dans l'interface, sur la vue du pipeline, où les jobs issus des templates
locaux apparaissent au même titre que les autres.
- 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
Pour aller plus loin
Section intitulée « Pour aller plus loin »- GitLab CI : 7 attaques qui ciblent vos pipelines : Les vecteurs qui visent précisément le type de pipeline que vous venez de livrer.
- GitLab CI : durcir vos pipelines en 10 mesures : La checklist à appliquer sur votre capstone avant de le proposer en production.
- GitLab CI : gérer les secrets sans les exposer : Sortir les identifiants de déploiement du YAML et des logs.