
Un test qui passe sur une seule version Python n'est pas une garantie de compatibilité réelle. Dans ce lab, vous mettez en place une matrice pour exécuter plusieurs combinaisons en parallèle, tout en gardant un flux de build propre avec needs.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Créer une matrice avec
parallel: matrix - Paramétrer l'image via une variable (
python:$PYTHON_VERSION) - Segmenter les tests en suites complémentaires
- Garder un enchaînement propre vers le build avec
needs
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Quand une équipe migre de Python 3.11 vers 3.12, les incompatibilités apparaissent souvent tard, parfois en production. La matrice CI permet de détecter ces écarts plus tôt, sans doubler manuellement les jobs.
Vous utiliserez ce pattern pour :
- valider une compatibilité multi-versions ;
- réduire le temps d'exécution via parallélisme ;
- préparer des stratégies de test plus fines par type de suite.
Prérequis
Section intitulée « Prérequis »- Lab 14, Externaliser les templates CI terminé
- Avoir lu Matrices
Point de départ
Section intitulée « Point de départ »Le run baseline de la troisième étape n'est pas une formalité : c'est lui qui donne le temps de référence de la phase de test avant la matrice. Notez cette durée, vous la comparerez à celle du pipeline matriciel à la fin du lab. La branche starter/lab-15 contient déjà un job pytest mono-version, c'est le point de départ que vous allez transformer.
-
Basculez sur la branche du lab
Fenêtre de terminal cd pipeline-craftgit checkout starter/lab-15 -
Ouvrez
ci/test.ymlVous devez y voir un job de test mono-version.
-
Lancez un run baseline
Fenêtre de terminal git push origin starter/lab-15
Le problème
Section intitulée « Le problème »Le pipeline valide un seul environnement Python. Il ne détecte donc pas les divergences d'interpréteur, et la phase de test reste plus longue qu'elle ne devrait.
L'exercice
Section intitulée « L'exercice »Étape 1, Déclarer la matrice de test
Section intitulée « Étape 1, Déclarer la matrice de test »parallel: matrix prend une liste de dictionnaires, et GitLab génère le produit cartésien des valeurs de chaque entrée. Deux versions Python croisées avec deux suites de tests donnent donc quatre jobs, pas deux. Le point sensible est l'indentation YAML : PYTHON_VERSION et TEST_SUITE appartiennent au même élément de la liste, ils doivent être alignés sur la même colonne. Un espace de trop sur la seconde clé et GitLab rejette le fichier avec une erreur de parsing peu explicite.
-
Modifiez le job
pytesten job matricielIndice : renommez le job pour refléter son rôle matriciel.
-
Paramétrez l'image avec la variable
Indice : l'image Python doit dépendre de la dimension
PYTHON_VERSION. -
Gardez un script qui route selon
TEST_SUITEL'objectif est d'éviter la duplication de jobs tout en conservant des suites ciblées.
👉 Vérifier votre solution (Étape 1)
1️⃣ Job matriciel dans ci/test.yml
Section intitulée « 1️⃣ Job matriciel dans ci/test.yml »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 daysLa matrice produit 4 combinaisons : 2 versions Python x 2 suites de tests.
Étape 2, Aligner le build sur la fin des tests
Section intitulée « Étape 2, Aligner le build sur la fin des tests »Quand un job est généré par une matrice, needs accepte son nom de base et couvre alors toutes les combinaisons produites. Écrire needs: [pytest-matrix] suffit donc, sans énumérer les quatre variantes. Attention à la conséquence : needs fait passer le pipeline en mode DAG, le build n'attend plus la fin de l'étape test entière mais uniquement les jobs listés. Si vous ajoutez plus tard un autre job de test hors matrice, il faudra l'ajouter explicitement, sinon le build démarrera sans lui.
-
Mettez à jour le job de build
Indice : le build doit dépendre explicitement du job matriciel.
-
Vérifiez que le build attend bien le succès global de la matrice
Aucun build ne doit démarrer si une combinaison échoue.
👉 Vérifier votre solution (Étape 2)
1️⃣ Dépendance du build dans ci/build.yml
Section intitulée « 1️⃣ Dépendance du build dans ci/build.yml »docker-build: stage: build needs: - pytest-matrix image: docker:27@sha256:aa3df78ecf320f5fafdce71c659f1629e96e9de0968305fe1de670e0ca9176ce 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Étape 3, Valider puis pousser
Section intitulée « Étape 3, Valider puis pousser »glab ci lint envoie le fichier à l'API de validation du projet GitLab, ce qui détecte les erreurs de syntaxe YAML et les mots-clés inconnus avant de consommer des minutes de runner. L'option --include-jobs fait remonter en plus la liste des jobs retenus par la validation, utile pour contrôler que le job matriciel est bien pris en compte. Vérifiez ensuite dans l'interface que les quatre jobs portent bien leurs variables de matrice dans leur nom, c'est le signe que le produit cartésien a été construit comme prévu.
-
Validez la config
Fenêtre de terminal glab ci lint .gitlab-ci.yml -
Committez les changements
Fenêtre de terminal git add ci/test.yml ci/build.ymlgit commit -m "ci: add matrix testing across python versions"git push origin starter/lab-15 -
Vérifiez l'UI pipeline
Vous devez voir 4 jobs de test issus de la matrice.
👉 Vérifier votre solution (Étape 3)
1️⃣ Contrôles attendus dans le pipeline
Section intitulée « 1️⃣ Contrôles attendus dans le pipeline »- 4 jobs de test sont créés automatiquement (
2 x 2) ; - chaque job affiche ses variables de matrice (
PYTHON_VERSION,TEST_SUITE) ; docker-builddémarre seulement après succès de toutes les combinaisons.
Le fichier complet
Section intitulée « Le fichier complet »Les trois blocs repliés ci-dessous donnent l'état final des fichiers après l'exercice. Ouvrez-les pour comparer avec votre version, pas pour les copier avant d'avoir essayé : l'intérêt du lab tient dans les erreurs d'indentation que vous corrigez vous-même. Notez que .gitlab-ci.yml ne contient plus aucun job, il se limite aux stages, au workflow et aux include locaux, toute la logique vivant dans ci/.
📄 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/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/build.yml complet
.docker-vars: &docker_vars DOCKER_TLS_CERTDIR: "/certs"
docker-build: stage: build needs: - pytest-matrix image: docker:27@sha256:aa3df78ecf320f5fafdce71c659f1629e96e9de0968305fe1de670e0ca9176ce 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:latestVérification
Section intitulée « Vérification »Le dernier point de la liste est le plus instructif. Quatre jobs de test au lieu d'un ne quadruplent la durée du pipeline que si vos runners ne peuvent en exécuter qu'un à la fois : avec assez de concurrence côté runner, la phase de test reste proche de la durée d'un seul job. Si vous constatez que le temps total a explosé, regardez la concurrence configurée avant d'incriminer la matrice.
- 4 jobs de test sont visibles (2 versions x 2 suites)
- Les jobs de test s'exécutent en parallèle
- Le build dépend du succès de la matrice
- Le temps global est comparable à un run mono-suite
Pièges fréquents
Section intitulée « Pièges fréquents »La matrice peut exploser rapidement en nombre de combinaisons. Ajoutez une dimension seulement si elle apporte une information de qualité utile.
Pensez aussi à nommer clairement vos variables de matrice pour garder des logs lisibles.
À retenir
Section intitulée « À retenir »parallel: matrixpermet de scaler les tests sans dupliquer les jobs- Un build fiable dépend du succès global des combinaisons
- Le parallélisme réduit la latence de feedback
- La matrice est un outil de confiance, pas un objectif en soi
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Pipelines parent-enfant GitLab CI/CD : Isoler une matrice qui a grossi dans un pipeline enfant dédié.
- Lab 16, Pipelines parent-enfant dynamiques : L'exercice qui génère un pipeline enfant selon le contexte du dépôt.
- Pipelines dynamiques GitLab CI/CD : Produire les combinaisons en YAML quand la matrice statique ne suffit plus.