Skip to main content

Matrix builds


Table of contents

  1. The matrix concept
  2. Basic syntax
  3. Advanced configurations
  4. Include and exclude
  5. Fail-fast and max-parallel
  6. Practical exercises


1 - The matrix concept

Why use a matrix?

Use cases

ScenarioMatrix
Multi-versionNode 16, 18, 20
Multi-OSUbuntu, Windows, macOS
Multi-browserChrome, Firefox, Safari
Multi-databasePostgreSQL, MySQL

🔝 Back to table of contents



2 - Basic syntax

Simple matrix

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16, 18, 20]

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test

Multi-dimensions

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16, 18, 20]
os: [ubuntu-latest, windows-latest]

# Total: 3 versions × 2 OS = 6 jobs
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test

With objects

jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
environment:
- name: staging
url: https://staging.example.com
- name: production
url: https://example.com

steps:
- run: |
echo "Deploying to ${{ matrix.environment.name }}"
echo "URL: ${{ matrix.environment.url }}"

Accessing the values

# Variable simple
${{ matrix.node }}

# Variable dans un objet
${{ matrix.environment.name }}

# Dans le nom du job
name: Test Node ${{ matrix.node }} on ${{ matrix.os }}

🔝 Back to table of contents



3 - Advanced configurations

Multi-OS with adjustments

jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: windows-latest
shell: pwsh
- os: ubuntu-latest
shell: bash
- os: macos-latest
shell: bash

runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ matrix.shell }}

steps:
- uses: actions/checkout@v4
- run: echo "Running on ${{ matrix.os }}"

Dynamic matrix

jobs:
generate:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: |
echo "matrix={\"node\":[16,18,20]}" >> $GITHUB_OUTPUT

build:
needs: generate
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.generate.outputs.matrix) }}
steps:
- run: echo "Node version: ${{ matrix.node }}"

Complex combinations

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [16, 18, 20]
database: [postgres, mysql]

# Total: 2 OS × 3 Node × 2 DB = 12 jobs

runs-on: ${{ matrix.os }}
services:
db:
image: ${{ matrix.database }}

steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test

🔝 Back to table of contents



4 - Include and exclude

exclude

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [16, 18, 20]
exclude:
# Exclure Node 16 sur Windows
- os: windows-latest
node: 16

runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

include

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest]
node: [18]
include:
# Ajouter une configuration spécifique
- os: ubuntu-latest
node: 20
experimental: true

# Ajouter des variables à une combinaison existante
- os: ubuntu-latest
node: 18
coverage: true

runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

- run: npm test

- name: Coverage
if: matrix.coverage
run: npm run coverage

Combining include/exclude

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [16, 18, 20]
exclude:
# Pas de Node 16 sur macOS
- os: macos-latest
node: 16
include:
# Ajouter des variables spéciales
- os: ubuntu-latest
node: 20
latest: true

runs-on: ${{ matrix.os }}
name: Node ${{ matrix.node }} on ${{ matrix.os }}${{ matrix.latest && ' (latest)' || '' }}

🔝 Back to table of contents



5 - Fail-fast and max-parallel

fail-fast

jobs:
test:
strategy:
fail-fast: true # Défaut: true
matrix:
node: [16, 18, 20]

# Si un job échoue, tous les autres sont annulés
jobs:
test:
strategy:
fail-fast: false # Continuer même si un job échoue
matrix:
node: [16, 18, 20]

# Tous les jobs s'exécutent jusqu'au bout

max-parallel

jobs:
test:
strategy:
max-parallel: 2 # Maximum 2 jobs en parallèle
matrix:
node: [16, 18, 20]
os: [ubuntu-latest, windows-latest]

# 6 jobs mais seulement 2 à la fois

Combination

jobs:
test:
strategy:
fail-fast: false
max-parallel: 4
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [16, 18, 20]

runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test

Continue on error per job

jobs:
test:
strategy:
matrix:
include:
- node: 18
experimental: false
- node: 21
experimental: true

runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}

steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test

🔝 Back to table of contents



6 - Practical exercises

Exercise 1: Multi-version Node

Test on Node 16, 18 and 20:

Solution
name: Test Multi-Node

on: push

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16, 18, 20]

name: Node ${{ matrix.node }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci
- run: npm test

Exercise 2: Multi-OS and exclude

Test on Ubuntu and Windows, but exclude Node 16 on Windows:

Solution
name: Cross-Platform

on: push

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [16, 18, 20]
exclude:
- os: windows-latest
node: 16

runs-on: ${{ matrix.os }}
name: Node ${{ matrix.node }} on ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test

Quiz

Q1. How many jobs does a 3×2×2 matrix generate?

Answer

3 × 2 × 2 = 12 jobs

Q2. How do you exclude a specific combination?

Answer

With exclude in the strategy.

🔝 Back to table of contents



Key takeaways

  • Matrix = multi-configuration testing in parallel
  • Access the values: ${{ matrix.variable }}
  • exclude to remove combinations
  • include to add configurations or variables
  • fail-fast: false to continue despite failures
  • max-parallel to limit parallelization
  • Dynamic matrix possible with fromJSON()

🔝 Back to table of contents


← Previous chapter | Next chapter: Reusable workflows →