Imaginez que vous devez tester votre application sur 3 systèmes d’exploitation et 3 versions de Node.js. Cela fait 9 combinaisons à tester. Sans outil adapté, vous devriez copier-coller le même code 9 fois. La matrix strategy résout ce problème élégamment.
C’est quoi une matrix, concrètement ?
Section intitulée « C’est quoi une matrix, concrètement ? »L’analogie du tableau de bord
Section intitulée « L’analogie du tableau de bord »Pensez à un tableau à double entrée comme ceux qu’on utilisait à l’école :
Chaque cellule du tableau représente un job GitHub Actions. La matrix génère automatiquement toutes ces combinaisons à partir de deux listes :
- Liste des OS :
[ubuntu, windows, macos] - Liste des versions :
[18, 20, 22]
3 × 3 = 9 jobs, sans écrire 9 fois le même code !
Avant/après : la puissance de la matrix
Section intitulée « Avant/après : la puissance de la matrix »❌ Sans matrix : duplication massive
jobs: test-ubuntu-18: runs-on: ubuntu-24.04 steps: - uses: actions/setup-node@v4 with: node-version: 18 - run: npm test
test-ubuntu-20: runs-on: ubuntu-24.04 steps: - uses: actions/setup-node@v4 with: node-version: 20 - run: npm test
test-ubuntu-22: # ... encore et encore test-windows-18: # ... 6 autres jobs identiques✅ Avec matrix : un seul bloc
jobs: test: strategy: matrix: os: [ubuntu-24.04, windows-latest, macos-latest] node: [18, 20, 22] runs-on: ${{ matrix.os }} steps: - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - run: npm testVotre première matrix en 3 étapes
Section intitulée « Votre première matrix en 3 étapes »-
Définir les axes : quelles variables voulez-vous combiner ?
strategy:matrix:os: [ubuntu-24.04, windows-latest]python: ['3.10', '3.11', '3.12'] -
Utiliser les variables : accédez aux valeurs avec
${{ matrix.xxx }}runs-on: ${{ matrix.os }}steps:- uses: actions/setup-python@v5with:python-version: ${{ matrix.python }} -
Lancer le workflow : GitHub génère automatiquement 2 × 3 = 6 jobs
Exemple complet commenté
Section intitulée « Exemple complet commenté »name: Tests multi-configurations
on: [push, pull_request]
jobs: test: # Le nom du job affiche les valeurs de la matrix name: Test Python ${{ matrix.python }} sur ${{ matrix.os }}
strategy: matrix: # Axe 1 : les systèmes d'exploitation os: [ubuntu-24.04, windows-latest] # Axe 2 : les versions de Python python: ['3.10', '3.11', '3.12']
# Cette ligne UTILISE la valeur de matrix.os runs-on: ${{ matrix.os }}
steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Cette action UTILISE la valeur de matrix.python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python }}
- run: pip install -e ".[test]" - run: pytestRésultat dans l’interface GitHub :
✓ Test Python 3.10 sur ubuntu-24.04 (2m 15s)✓ Test Python 3.11 sur ubuntu-24.04 (2m 08s)✓ Test Python 3.12 sur ubuntu-24.04 (2m 12s)✓ Test Python 3.10 sur windows-latest (3m 45s)✓ Test Python 3.11 sur windows-latest (3m 38s)✓ Test Python 3.12 sur windows-latest (3m 42s)Comment ça marche techniquement ?
Section intitulée « Comment ça marche techniquement ? »Le produit cartésien
Section intitulée « Le produit cartésien »GitHub calcule le produit cartésien de tous les axes. Ne vous laissez pas intimider par ce terme mathématique : c’est simplement toutes les combinaisons possibles entre vos listes de valeurs.
Imaginez un menu de restaurant : si vous avez 2 entrées et 3 plats, vous pouvez composer 2 × 3 = 6 menus différents. Le produit cartésien, c’est exactement ça !
Avec une matrix à deux axes :
Axe 1: [A, B] ┐ ├──→ Combinaisons: [A,X], [A,Y], [B,X], [B,Y]Axe 2: [X, Y] ┘
2 × 2 = 4 jobsComment lire ce schéma ? GitHub prend chaque valeur du premier axe (A, puis B) et l’associe à chaque valeur du second axe (X, puis Y). Résultat : 4 combinaisons, donc 4 jobs parallèles.
Avec trois axes, le principe reste le même — on multiplie simplement les possibilités :
os: [ubuntu, windows] 2 valeursnode: [18, 20, 22] 3 valeursdatabase: [postgres, mysql] 2 valeurs ───────── 2 × 3 × 2 = 12 jobsTraduction concrète : chaque combinaison OS + version Node + base de données sera testée. Ubuntu avec Node 18 et Postgres, Ubuntu avec Node 18 et MySQL, Ubuntu avec Node 20 et Postgres… et ainsi de suite jusqu’aux 12 combinaisons.
Les variables de la matrix
Section intitulée « Les variables de la matrix »Chaque valeur définie dans la matrix devient accessible via le context
matrix :
strategy: matrix: fruit: [pomme, banane] couleur: [rouge, jaune]
# Dans les steps, vous pouvez utiliser :# ${{ matrix.fruit }} → "pomme" ou "banane"# ${{ matrix.couleur }} → "rouge" ou "jaune"Syntaxe détaillée
Section intitulée « Syntaxe détaillée »Définir une matrix
Section intitulée « Définir une matrix »jobs: build: strategy: matrix: # Chaque clé devient une variable ${{ matrix.xxx }} os: [ubuntu-24.04, windows-latest] version: [1.0, 2.0, 3.0] arch: [x64, arm64] runs-on: ${{ matrix.os }} steps: - run: | echo "OS: ${{ matrix.os }}" echo "Version: ${{ matrix.version }}" echo "Arch: ${{ matrix.arch }}"Cet exemple génère 2 × 3 × 2 = 12 combinaisons.
Utiliser les valeurs de la matrix
Section intitulée « Utiliser les valeurs de la matrix »Les valeurs sont accessibles via le context matrix :
jobs: test: strategy: matrix: python: ['3.9', '3.10', '3.11', '3.12'] runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python }}
- run: python --versionInclude : ajouter ou enrichir des combinaisons
Section intitulée « Include : ajouter ou enrichir des combinaisons »Le mot-clé include est comme un bonus pour votre matrix. Il permet deux
choses :
- Ajouter des combinaisons qui n’existent pas dans le produit cartésien
- Enrichir des combinaisons existantes avec des variables supplémentaires
Cas 1 : Ajouter une combinaison spéciale
Section intitulée « Cas 1 : Ajouter une combinaison spéciale »Imaginons que vous voulez tester Node 22, mais uniquement sur Ubuntu (car c’est expérimental sur les autres OS) :
strategy: matrix: os: [ubuntu-24.04, windows-latest] node: [18, 20] include: # Cette combinaison N'EXISTE PAS dans le produit cartésien # (node 22 n'est pas dans la liste originale) - os: ubuntu-24.04 node: 22 experimental: true # Variable bonus pour cette combinaisonSans include : 2 × 2 = 4 combinaisons
Avec include : 4 + 1 = 5 combinaisons
Cas 2 : Enrichir une combinaison existante
Section intitulée « Cas 2 : Enrichir une combinaison existante »Parfois, vous avez besoin de variables différentes selon la combinaison. Par exemple, le shell par défaut varie selon l’OS :
strategy: matrix: os: [ubuntu-24.04, windows-latest, macos-latest] include: # Ces lignes ENRICHISSENT les combinaisons existantes # en ajoutant une variable "shell" - os: windows-latest shell: pwsh # PowerShell pour Windows - os: ubuntu-24.04 shell: bash # Bash pour Ubuntu - os: macos-latest shell: bash # Bash pour macOS
runs-on: ${{ matrix.os }}defaults: run: shell: ${{ matrix.shell }} # Utilise le shell appropriéCas 3 : Matrix basée uniquement sur include
Section intitulée « Cas 3 : Matrix basée uniquement sur include »Vous pouvez créer une matrix sans axes, uniquement avec include. Utile
pour des configurations très différentes :
strategy: matrix: include: - name: 'Production EU' region: 'eu-west-1' env: 'prod' replicas: 3 - name: 'Production US' region: 'us-east-1' env: 'prod' replicas: 3 - name: 'Staging' region: 'eu-west-1' env: 'staging' replicas: 1
# 3 jobs avec des configurations complètement personnaliséesExclude : retirer des combinaisons indésirables
Section intitulée « Exclude : retirer des combinaisons indésirables »exclude fait l’inverse de include : il retire des combinaisons du
produit cartésien. C’est comme dire “je veux tout, sauf ça”.
Pourquoi exclure des combinaisons ?
Section intitulée « Pourquoi exclure des combinaisons ? »Quelques raisons courantes :
- Une version n’est pas supportée sur un OS particulier
- Une combinaison est redondante ou inutile
- Économiser des minutes de CI sur des tests non pertinents
Exemple concret
Section intitulée « Exemple concret »strategy: matrix: os: [ubuntu-24.04, windows-latest, macos-latest] node: [18, 20, 22] exclude: # Node 22 a des problèmes connus sur Windows - os: windows-latest node: 22 # On ne teste pas toutes les versions sur macOS (coûteux) - os: macos-latest node: 18Calcul des jobs :
- Produit cartésien : 3 × 3 = 9 combinaisons
- Exclusions : -2 combinaisons
- Total : 7 jobs
Contrôler l’exécution : fail-fast et max-parallel
Section intitulée « Contrôler l’exécution : fail-fast et max-parallel »fail-fast : tout arrêter ou continuer ?
Section intitulée « fail-fast : tout arrêter ou continuer ? »Par défaut, fail-fast est à true. Cela signifie que si un seul job
de la matrix échoue, GitHub annule tous les autres immédiatement.
Quand garder fail-fast: true (défaut) :
- Vous voulez un feedback rapide
- Un échec rend les autres résultats inutiles
- Vous économisez des minutes de CI
Quand utiliser fail-fast: false :
- Vous voulez voir tous les résultats
- Vous debuggez et cherchez quelles combinaisons échouent
- Les jobs sont indépendants
strategy: fail-fast: false # Continue même si un job échoue matrix: node: [18, 20, 22]max-parallel : limiter les jobs simultanés
Section intitulée « max-parallel : limiter les jobs simultanés »Par défaut, GitHub lance tous les jobs en parallèle. Avec max-parallel,
vous pouvez limiter ce nombre :
strategy: max-parallel: 2 # Maximum 2 jobs en même temps matrix: node: [18, 20, 22] # 3 jobs au totalPourquoi limiter le parallélisme ?
| Situation | Raison |
|---|---|
| Tests avec base de données partagée | Éviter les conflits de données |
| API externe avec rate limiting | Ne pas dépasser les quotas |
| Runners self-hosted limités | Éviter la saturation |
| Économiser les minutes | Réduire les coûts (repos privés) |
Techniques avancées
Section intitulée « Techniques avancées »Matrix dynamique avec fromJSON
Section intitulée « Matrix dynamique avec fromJSON »Parfois, vous ne connaissez pas les valeurs de la matrix à l’avance. Par exemple, vous voulez tester uniquement les modules modifiés. La solution : générer la matrix dynamiquement dans un premier job.
jobs: # Job 1 : Déterminer quoi tester setup: runs-on: ubuntu-24.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - id: set-matrix run: | # Génère un JSON qui sera la matrix echo 'matrix={"version":["1.0","2.0","3.0"]}' >> $GITHUB_OUTPUT
# Job 2 : Utilise la matrix générée build: needs: setup strategy: # fromJSON convertit la string en objet matrix: ${{ fromJSON(needs.setup.outputs.matrix) }} runs-on: ubuntu-24.04 steps: - run: echo "Building version ${{ matrix.version }}"Matrix depuis un fichier de configuration
Section intitulée « Matrix depuis un fichier de configuration »Pour une meilleure maintenabilité, stockez la matrix dans un fichier :
Fichier .github/matrix.json :
{ "include": [ { "name": "app-frontend", "path": "./apps/frontend" }, { "name": "app-backend", "path": "./apps/backend" }, { "name": "app-api", "path": "./apps/api" } ]}Workflow :
jobs: setup: runs-on: ubuntu-24.04 outputs: matrix: ${{ steps.read.outputs.matrix }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - id: read run: | # jq -c compacte le JSON sur une ligne echo "matrix=$(cat .github/matrix.json | jq -c .)" >> $GITHUB_OUTPUT
test: needs: setup strategy: matrix: ${{ fromJSON(needs.setup.outputs.matrix) }} runs-on: ubuntu-24.04 steps: - run: echo "Testing ${{ matrix.name }} at ${{ matrix.path }}"Voir le guide jq pour maîtriser le traitement
JSON en ligne de commande.
Exemples prêts à l’emploi
Section intitulée « Exemples prêts à l’emploi »Test multi-versions Python
Section intitulée « Test multi-versions Python »Un classique : tester sur plusieurs versions de Python et plusieurs OS.
name: Tests Python
on: [push, pull_request]
permissions: contents: read
jobs: test: name: Python ${{ matrix.python }} / ${{ matrix.os }} strategy: fail-fast: false # Voir tous les résultats matrix: os: [ubuntu-24.04, windows-latest, macos-latest] python: ['3.10', '3.11', '3.12'] exclude: # macOS coûte cher, on limite les versions testées - os: macos-latest python: '3.10'
runs-on: ${{ matrix.os }}
steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python }}
- run: pip install -e ".[test]" - run: pytest --verboseBuild multi-architecture Docker
Section intitulée « Build multi-architecture Docker »Pour créer des images Docker ARM64 et AMD64 :
name: Build Multi-Arch
on: push: branches: [main]
jobs: build: name: Build ${{ matrix.platform }} strategy: matrix: include: - platform: linux/amd64 runner: ubuntu-24.04 - platform: linux/arm64 runner: ubuntu-24.04-arm # Runner ARM natif
runs-on: ${{ matrix.runner }}
steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Build image run: | docker build \ --platform ${{ matrix.platform }} \ -t myapp:${{ github.sha }}-${{ matrix.platform }} .Déploiement multi-environnements
Section intitulée « Déploiement multi-environnements »Déployer sur staging ou production selon le choix de l’utilisateur :
name: Deploy
on: workflow_dispatch: inputs: target: description: 'Où déployer ?' type: choice options: [staging, production] default: staging
jobs: deploy: name: Deploy to ${{ matrix.env }} strategy: matrix: include: - env: staging url: https://staging.example.com replicas: 1 - env: production url: https://example.com replicas: 3 exclude: # Astuce : exclure l'environnement non choisi - env: ${{ github.event.inputs.target == 'staging' && 'production' || 'staging' }}
environment: ${{ matrix.env }} runs-on: ubuntu-24.04
steps: - run: | echo "🚀 Deploying to ${{ matrix.env }}" echo "URL: ${{ matrix.url }}" echo "Replicas: ${{ matrix.replicas }}"Les 5 règles d’or des matrix
Section intitulée « Les 5 règles d’or des matrix »1. Nommez vos jobs explicitement
Section intitulée « 1. Nommez vos jobs explicitement »Sans nom personnalisé, GitHub affiche “test (1)”, “test (2)”… Peu utile !
jobs: test: # ✅ Nom explicite avec les valeurs de la matrix name: Test ${{ matrix.os }} / Node ${{ matrix.node }} strategy: matrix: os: [ubuntu-24.04, windows-latest] node: [18, 20]Résultat dans l’interface :
✓ Test ubuntu-24.04 / Node 18 (2m 15s)✓ Test ubuntu-24.04 / Node 20 (2m 08s)✓ Test windows-latest / Node 18 (3m 45s)✓ Test windows-latest / Node 20 (3m 38s)2. Utilisez fail-fast: false pour débugger
Section intitulée « 2. Utilisez fail-fast: false pour débugger »Quand vous cherchez quelles combinaisons échouent, vous voulez tous les résultats :
strategy: fail-fast: false # Ne pas annuler les autres jobs en cas d'échec matrix: node: [18, 20, 22]3. Limitez la taille de votre matrix
Section intitulée « 3. Limitez la taille de votre matrix »# ❌ ÉVITER : 3 × 4 × 3 = 36 combinaisons !matrix: os: [ubuntu, windows, macos] node: [16, 18, 20, 22] database: [postgres, mysql, sqlite]
# ✅ PRÉFÉRER : combinaisons cibléesmatrix: include: # Test complet sur Ubuntu (référence) - os: ubuntu-24.04 node: 20 database: postgres # Validation Windows - os: windows-latest node: 20 database: postgres # Test rétro-compatibilité - os: ubuntu-24.04 node: 18 database: mysql4. Gérez les spécificités de chaque OS
Section intitulée « 4. Gérez les spécificités de chaque OS »Windows et Linux n’utilisent pas les mêmes commandes. Utilisez include
pour personnaliser :
strategy: matrix: os: [ubuntu-24.04, windows-latest] include: - os: ubuntu-24.04 script: ./scripts/test.sh shell: bash - os: windows-latest script: .\scripts\test.ps1 shell: pwsh
steps: - run: ${{ matrix.script }} shell: ${{ matrix.shell }}5. Documentez vos exclusions
Section intitulée « 5. Documentez vos exclusions »Les exclusions peuvent être mystérieuses pour les contributeurs. Ajoutez toujours un commentaire :
exclude: # Python 3.9 est en fin de vie (EOL octobre 2025) # On ne le supporte plus que sur Ubuntu pour les systèmes legacy - python: '3.9' os: macos-latest - python: '3.9' os: windows-latest
# Node 22 a un bug connu avec Windows Server 2022 # Voir https://github.com/nodejs/node/issues/XXXXX - node: 22 os: windows-latestÀ retenir
Section intitulée « À retenir »| Concept | Syntaxe | Usage |
|---|---|---|
| Axes de la matrix | matrix: { os: [...], node: [...] } | Définir les combinaisons |
| Accès aux valeurs | ${{ matrix.os }} | Utiliser dans les steps |
| Ajouter/enrichir | include: [...] | Cas spéciaux |
| Exclure | exclude: [...] | Retirer des combinaisons |
| Continuer sur échec | fail-fast: false | Debug |
| Limiter le parallélisme | max-parallel: N | Ressources limitées |
| Matrix dynamique | fromJSON(...) | Valeurs calculées |
Liens utiles
Section intitulée « Liens utiles »- Workflows GitHub Actions — structure et organisation
- Syntaxe des workflows — rappels sur le format
- Documentation officielle : Matrix — référence complète