
À chaque run, votre pipeline réinstalle 15 paquets pip depuis zéro. Deux fois par job. Et quand les tests échouent, il faut aller chercher les logs dans l'interface GitLab pour comprendre ce qui s'est passé — rien n'est conservé. Dans ce lab, vous allez mettre les dépendances pip en cache (pour ne plus les télécharger à chaque fois) et publier les rapports de tests comme artifacts accessibles directement depuis GitLab.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre la différence entre un artifact et un cache dans GitLab CI/CD
- Configurer un cache pip avec une clé basée sur le hash du fichier
requirements-dev.txt - Publier un rapport JUnit en tant qu'artifact et le visualiser dans GitLab
- Versionner les images Docker avec les variables prédéfinies de GitLab
Quel lab commencer ?
Section intitulée « Quel lab commencer ? »Ce lab s'adresse surtout aux apprenants ayant complété Lab 03. Si vous êtes en débutant (L1), commencez par les labs 01-03. Si vous êtes intermédiaire (L2) ou avancé, vous pouvez commencer par Lab 04 directement depuis la branche starter/lab-04.
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Un pipeline qui réinstalle toutes ses dépendances à chaque run est clairement sous-optimal. Sur un projet professionnel avec 50 dépendances et 20 runs par jour, ça représente des heures de temps machine perdues chaque semaine. Le cache est souvent la première optimisation qu'on ajoute après un pipeline fonctionnel.
Les artifacts, eux, servent à transmettre des fichiers entre jobs ou à conserver des données après le pipeline. Les rapports JUnit sont particulièrement utiles : GitLab les lit automatiquement et affiche un résumé des tests dans les Merge Requests, sans avoir à chercher dans les logs.
Situations réelles où ce lab vous aide :
- Un collègue demande « est-ce que les tests passent sur ma MR ? » — les rapports JUnit répondent directement dans l'interface
- Le pipeline prend 5 minutes pour lancer 30 secondes de tests à cause des
pip install - Vous voulez retrouver les logs d'un run passé — les artifacts conservent les fichiers 7 jours par défaut
- Le
docker pushécrase toujourslatestsans versionnement — impossible de savoir quelle image correspond à quel commit
Prérequis
Section intitulée « Prérequis »- Lab 03 complété — ou partir directement de la branche
starter/lab-04 - Avoir lu Artifacts et cache GitLab CI/CD (conseillé)
Point de départ
Section intitulée « Point de départ »-
Passez sur la branche de départ
Fenêtre de terminal cd pipeline-craftgit checkout starter/lab-04 -
Poussez pour déclencher le pipeline
Fenêtre de terminal git push origin starter/lab-04 -
Observez le pipeline dans Build > Pipelines
Le pipeline passe au vert — il hérite des corrections du Lab 03. Mais chaque job réinstalle ses dépendances depuis zéro, et le rapport de tests disparaît après le run.
L'exercice
Section intitulée « L'exercice »Comprendre : artifact vs cache
Section intitulée « Comprendre : artifact vs cache »Avant de coder, il faut clarifier deux concepts souvent confondus.
Un cache est conçu pour accélérer les builds. Il conserve des fichiers entre deux runs du même pipeline sur le même runner — typiquement un dossier de dépendances comme .pip-cache/. Si le cache n'existe pas encore, le job fonctionne quand même (il réinstalle juste tout). Le cache n'est pas garanti : si le runner change, si le cache expire, il sera vide.
Un artifact est un fichier que vous voulez conserver et partager. Il est téléversé sur GitLab après le job et reste accessible pendant la durée d'expiration configurée. Il peut être transmis d'un job à un autre dans le même pipeline (on l'utilise pour passer des binaires compilés d'un stage build à un stage deploy), ou conservé pour être consulté plus tard (rapports de tests, logs).
| Cache | Artifact | |
|---|---|---|
| Objectif | Accélérer le build | Conserver / transmettre des fichiers |
| Partagé entre jobs | Non (même runner) | Oui (pipeline entier) |
| Garanti ? | Non | Oui (jusqu'à expiration) |
| Visible dans GitLab UI | Non | Oui |
Étape 1 — À vous de configurer le cache pip global
Section intitulée « Étape 1 — À vous de configurer le cache pip global »En haut du .gitlab-ci.yml, avant les définitions de jobs, vous devez ajouter une variable globale qui configure où pip stocke ses paquets téléchargés. Cette variable sera ensuite utilisée par le job pytest.
À vous de proposer :
-
Quel est le nom de la variable que vous allez créer pour indiquer le chemin du cache pip ?
- 🔍 Indice : Les variables de cache pip dans les outils CI/CD suivent généralement la convention
PIP_*
- 🔍 Indice : Les variables de cache pip dans les outils CI/CD suivent généralement la convention
-
Quelle est la valeur que vous allez attribuer à cette variable ?
- 🔍 Indice : Utilisez une variable prédéfinie de GitLab qui représente le répertoire du projet. Elle commence par
$CI_...
- 🔍 Indice : Utilisez une variable prédéfinie de GitLab qui représente le répertoire du projet. Elle commence par
-
Comment allez-vous ensuite référencer cette variable dans la configuration du cache du job
pytest?- 🔍 Indice : La clé du cache devrait changer automatiquement quand les dépendances changent
👉 Vérifier votre solution
1️⃣ Configuration sur les deux jobs
Section intitulée « 1️⃣ Configuration sur les deux jobs »Ajoutez en haut du fichier (avant les définitions de jobs) :
variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"$CI_PROJECT_DIR est une variable prédéfinie de GitLab qui pointe vers le répertoire racine du projet sur le runner.
2️⃣ Cache sur le job pytest
Section intitulée « 2️⃣ Cache sur le job pytest »Modifiez le job pytest pour ajouter une configuration de cache :
pytest: stage: test image: python:3.12-slim cache: key: files: - requirements-dev.txt paths: - .pip-cache/ before_script: - pip install -r requirements-dev.txt script: - pytest -v --junitxml=report.xml artifacts: when: always paths: - report.xml reports: junit: report.xml expire_in: 7 daysPrécisions essentielles :
key.files: - requirements-dev.txt: la clé du cache est calculée à partir du hash de ce fichier. Si les dépendances changent, le cache est invalidé automatiquement.paths: - .pip-cache/: ce dossier sera sauvegardé et restauré entre les runs du même pipeline sur le même runner.
3️⃣ Cache en lecture seule sur le job ruff-lint
Section intitulée « 3️⃣ Cache en lecture seule sur le job ruff-lint »Le job ruff-lint n'installe qu'un seul paquet, mais il peut bénéficier du cache après pytest. Ajoutez la même configuration de cache avec policy: pull pour indiquer qu'il est en lecture seule :
ruff-lint: stage: lint image: python:3.12-slim cache: key: files: - requirements-dev.txt paths: - .pip-cache/ policy: pull before_script: - pip install ruff script: - ruff check app/ tests/La politique pull évite les conflits d'écriture simultanée dans le cache.
Étape 2 — À vous de publier le rapport JUnit
Section intitulée « Étape 2 — À vous de publier le rapport JUnit »Après avoir configuré le cache, vous allez faire en sorte que pytest génère un rapport au format JUnit, puis vous indiquerez à GitLab :
- Où trouver ce rapport (pour l'afficher dans l'UI)
- Qu'il doit être conservé comme artifact (même si le job échoue)
- Pendant combien de temps le conserver
À vous de proposer :
-
Quelle option pytest doivent vous ajouter pour générer un rapport XML ?
- 🔍 Indice : Elle commence par
--junit...
- 🔍 Indice : Elle commence par
-
Quelle clé YAML vous indique à GitLab que ce fichier doit être conservé après le job ?
- 🔍 Indice : Elle s'appelle
artifacts:
- 🔍 Indice : Elle s'appelle
-
Comment configurez-vous pour que l'artifact soit conservé même si le job échoue ?
- 🔍 Indice : Cherchez la clé
when:avec valeuralways
- 🔍 Indice : Cherchez la clé
-
Comment indiquez à GitLab que ce fichier est un rapport JUnit pour qu'il l'affiche dans les Merge Requests ?
- 🔍 Indice : Sous-clé
reports:avecjunit:
- 🔍 Indice : Sous-clé
👉 Vérifier votre solution
Générer et conserver le rapport
Section intitulée « Générer et conserver le rapport »Modifiez le job pytest pour générer le rapport et le déclarer comme artifact :
pytest: stage: test image: python:3.12-slim cache: key: files: - requirements-dev.txt paths: - .pip-cache/ before_script: - pip install -r requirements-dev.txt script: - pytest -v --junitxml=report.xml artifacts: when: always paths: - report.xml reports: junit: report.xml expire_in: 7 daysExplication ligne par ligne :
--junitxml=report.xml: pytest écrit les résultats en format JUnit dansreport.xmlwhen: always: conserve l'artifact même si le job échoue. C'est crucial pour les tests — c'est précisément quand les tests échouent qu'on veut le rapport !paths: - report.xml: le fichier sera téléchargeable depuis la page du jobreports.junit: report.xml: indique à GitLab que c'est un rapport JUnit. GitLab le parse et affiche un résumé dans les Merge Requestsexpire_in: 7 days: après 7 jours, GitLab supprime l'artifact pour économiser l'espace
Étape 3 — À vous de versionner l'image Docker
Section intitulée « Étape 3 — À vous de versionner l'image Docker »Pour l'instant, le job docker-build pousse l'image avec le tag latest, ce qui écrase à chaque fois l'image précédente. Vous ne savez plus quelle image correspond à quel commit.
Vous allez ajouter un second tag basé sur le SHA du commit, pour que chaque push crée une image unique et traçable.
À vous de proposer :
-
Où trouvez-vous le hash du commit pour l'utiliser comme tag Docker ?
- 🔍 Indice : GitLab injecte une variable
$CI_COMMIT_*que vous pouvez utiliser
- 🔍 Indice : GitLab injecte une variable
-
Quelles sont les deux lignes
docker buildque vous allez utiliser ?- 🔍 Indice : Une avec le SHA du commit court, une avec
latest
- 🔍 Indice : Une avec le SHA du commit court, une avec
-
Lequel des deux tags doit être créé et poussé en premier ?
- 🔍 Indice : Le tag versionnée (SHA) pour traçabilité
-
Comment référencez-vous le Container Registry de votre projet GitLab ?
- 🔍 Indice : Variable prédéfinie
$CI_REGISTRY*
- 🔍 Indice : Variable prédéfinie
👉 Vérifier votre solution
Versionner avec le SHA du commit
Section intitulée « Versionner avec le SHA du commit »Modifiez le job docker-build :
docker-build: stage: build image: docker:27 services: - docker:27-dind variables: DOCKER_TLS_CERTDIR: "/certs" 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:latestVariables prédéfinies en action :
$CI_REGISTRY_IMAGE: l'adresse du Container Registry de votre projet (ex :registry.gitlab.com/votre-pseudo/pipeline-craft)$CI_COMMIT_SHORT_SHA: les 8 premiers caractères du hash du commit (ex :a1b2c3d4). Chaque push produit un tag unique et traçable.$CI_REGISTRY_USERet$CI_REGISTRY_PASSWORD: identifiants pour le login — GitLab les injecte automatiquement
Avantage : maintenant, chaque version de votre code a sa propre image Docker dans le registry. Vous pouvez revenir à une version passée directement en re-pullant $CI_REGISTRY_IMAGE:a1b2c3d4, même 6 mois après.
Le fichier complet
Section intitulée « Le fichier complet »📄 Voir le fichier .gitlab-ci.yml complet
stages: - lint - test - build
variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"
ruff-lint: stage: lint image: python:3.12-slim cache: key: files: - requirements-dev.txt paths: - .pip-cache/ policy: pull before_script: - pip install ruff script: - ruff check app/ tests/
pytest: stage: test image: python:3.12-slim cache: key: files: - requirements-dev.txt paths: - .pip-cache/ before_script: - pip install -r requirements-dev.txt script: - pytest -v --junitxml=report.xml artifacts: when: always paths: - report.xml reports: junit: report.xml expire_in: 7 days
docker-build: stage: build image: docker:27 services: - docker:27-dind variables: DOCKER_TLS_CERTDIR: "/certs" 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:latestPousser et vérifier
Section intitulée « Pousser et vérifier »-
Committez et poussez
Fenêtre de terminal git add .gitlab-ci.ymlgit commit -m "ci: add pip cache, junit artifact and docker versioning"git push origin starter/lab-04 -
Observez le premier run
Le premier run ne bénéficie pas encore du cache (il n'existe pas encore). C'est normal. Regardez le job
pytest— à la fin, GitLab afficheUploading artifacts...puisreport.xml. -
Déclenchez un deuxième run (faites un commit vide par exemple)
Fenêtre de terminal git commit --allow-empty -m "ci: test cache hit"git push origin starter/lab-04Cette fois, les jobs doivent afficher
Checking cache for ...suivi deSuccessfully extracted cache. Lepip installva beaucoup plus vite. -
Vérifiez le rapport de tests
Après le run, cliquez sur le job
pytestdans GitLab. En bas de la page, vous verrez une section Artifacts avec un lien pour téléchargerreport.xml. Si vous créez une Merge Request sur ce projet, l'onglet Tests affichera automatiquement les résultats.
Vérification
Section intitulée « Vérification »- Le pipeline passe au vert
- Le deuxième run est plus rapide que le premier (cache hit)
- Le fichier
report.xmlest visible comme artifact dans le jobpytest - Le job
docker-buildutilise$CI_COMMIT_SHORT_SHApour taguer l'image
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Solution |
|---|---|---|
| Cache jamais utilisé | Clé de cache différente entre les runs | Vérifier que les clés (key:) sont identiques entre le job qui écrit et les jobs qui lisent |
report.xml absent des artifacts | --junitxml=report.xml absent du script pytest | Ajouter l'option à la commande pytest |
| Tests absents dans les MR | reports.junit: manquant dans artifacts: | Le chemin dans reports.junit: doit correspondre exactement au fichier généré |
| Push registry échoue | Container Registry désactivé dans le projet | Activer dans Settings > General > Visibility |
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Les variables prédéfinies de GitLab : GitLab injecte des dizaines de variables dans chaque job (
$CI_COMMIT_REF_NAME,$CI_PROJECT_PATH,$CI_PIPELINE_ID…). Ajoutezenv | grep CI_dans unscript:pour les voir toutes. Voir Artifacts et cache. - Comparez avec
solution/lab-04:git diff origin/starter/lab-04..origin/solution/lab-04montre les corrections complètes.
À retenir
Section intitulée « À retenir »- Un cache accélère les builds en réutilisant des fichiers entre les runs (pas garanti), un artifact conserve des fichiers après un job (garanti jusqu'à expiration)
- La clé de cache basée sur
key.filesinvalide automatiquement le cache quand les dépendances changent — c'est la bonne pratique when: alwayssur les artifacts de tests est essentiel : c'est quand les tests échouent qu'on a le plus besoin du rapport- Les variables prédéfinies (
$CI_REGISTRY_IMAGE,$CI_COMMIT_SHORT_SHA…) évitent de coder en dur des valeurs dans le pipeline