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 ?
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
❌ Sans matrix : duplication massive
jobs:
test-ubuntu-18:
runs-on:ubuntu-latest
steps:
-uses:actions/setup-node@v4
with:
node-version:18
-run:npm test
test-ubuntu-20:
runs-on:ubuntu-latest
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-latest, 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 test
Votre première matrix en 3 étapes
Définir les axes : quelles variables voulez-vous combiner ?
strategy:
matrix:
os: [ubuntu-latest, 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@v5
with:
python-version:${{ matrix.python }}
Lancer le workflow : GitHub génère automatiquement 2 × 3 = 6 jobs
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 }}
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 jobs
Comment 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 valeurs
node: [18, 20, 22] 3 valeurs
database: [postgres, mysql] 2 valeurs
─────────
2 × 3 × 2 = 12 jobs
Traduction 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
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
Définir une matrix
jobs:
build:
strategy:
matrix:
# Chaque clé devient une variable ${{ matrix.xxx }}
os: [ubuntu-latest, 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
Les valeurs sont accessibles via le context matrix :
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
Imaginons que vous voulez tester Node 22, mais uniquement sur Ubuntu
(car c’est expérimental sur les autres OS) :
strategy:
matrix:
os: [ubuntu-latest, 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-latest
node:22
experimental:true# Variable bonus pour cette combinaison
Sans include : 2 × 2 = 4 combinaisons
Avec include : 4 + 1 = 5 combinaisons
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-latest, 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-latest
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
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ées
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 ?
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
strategy:
matrix:
os: [ubuntu-latest, 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:18
Calcul 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
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
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 total
Pourquoi 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
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.