GitHub Actions Matrix 矩阵构建:多平台多版本并行测试实战指南
去年有个开源项目找到我,说他们的 CI 配置文件已经膨胀到 800 多行了。我打开一看,密密麻麻全是重复的 job 定义——Node 16 在 Ubuntu 上跑、Node 16 在 Windows 上跑、Node 16 在 macOS 上跑……然后 Node 18 再来一遍,Node 20 又来一遍。改个测试命令?得改 12 处。加个新版本?复制粘贴十几分钟。
那一刻我突然意识到,很多人还在用手写的方式维护多版本多平台的测试。
GitHub Actions 的 Matrix 功能,说白了就是帮你自动展开这些重复配置。你定义几个操作系统、几个运行时版本,它自动帮你把所有组合跑一遍。听起来挺简单,但真正用起来,坑还不少——组合数爆炸导致账单爆炸、一个任务失败全链路挂掉、想排除特定组合不知道怎么写……这些问题我都踩过。
这篇文章会从最基础的 Matrix 语法讲起,一步步带你掌握 exclude/include 精准控制、fail-fast 策略选择、max-parallel 并发限制、以及动态 Matrix 生成这些高级技巧。最后给你 5 个可以直接复制到项目里的生产级 workflow 模板,覆盖从个人项目到企业级应用的各种场景。
2. Matrix 核心概念:一键展开多任务
Matrix 的核心逻辑很简单:你定义几个维度,GitHub Actions 自动帮你做笛卡尔积展开。
比如说,你的项目需要在 Ubuntu、Windows、macOS 三个系统上测试,同时要兼容 Node.js 18、20、22 三个版本。传统写法你得手写 9 个 job,每个 job 都要重复定义运行环境、安装步骤、测试命令。用 Matrix 的话,只需要这样:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [18, 20, 22]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test
这 10 行配置,GitHub Actions 会自动展开成 3 x 3 = 9 个并行任务。每个任务都会拿到不同的 matrix.os 和 matrix.node 值,跑完所有组合。
我之前那个 800 行配置文件的项目,用 Matrix 重构之后缩减到了 120 行左右——减少了 60%+ 的代码量。维护成本也下来了,加个新版本只需要在数组里加一个数字,不用再复制粘贴一堆 job 定义。
Matrix 能帮你做到的事:
- 一键生成多平台多版本的测试组合
- 自动展开所有配置,避免手写重复代码
- 通过 exclude 排除已知问题的组合
- 通过 include 添加特殊配置的用例
- 控制并发数量,平衡速度和成本
但它解决不了的问题:
- 测试本身写得烂,Matrix 也救不了
- 组合太多导致账单爆炸——这个得靠你自己控制维度数量
- 依赖安装慢——得配合缓存策略
说实话,Matrix 本身不难理解,难的是怎么用它解决实际工程问题。接下来我们从基础语法开始,一步步拆解。
3. 基础语法:os x version 组合原理
Matrix 的组合规则其实就是数学里的笛卡尔积。你定义的每个维度,都会和其他维度做全排列组合。
一个维度,N 个值 -> N 个任务
两个维度,M x N 个值 -> M x N 个任务
三个维度,A x B x C 个值 -> A x B x C 个任务
来个具体的例子。假设你有一个 Python 项目,需要在 Linux 和 Windows 上测试 Python 3.9、3.10、3.11、3.12 四个版本,同时要测试 PostgreSQL 和 MySQL 两种数据库:
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12']
database: [postgresql, mysql]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Setup ${{ matrix.database }}
run: |
# 启动对应的数据库服务
if [ "${{ matrix.database }}" = "postgresql" ]; then
docker run -d -p 5432:5432 postgres
else
docker run -d -p 3306:3306 mysql
fi
shell: bash
- run: pip install -r requirements.txt
- run: pytest
这个配置会生成 2 x 4 x 2 = 16 个任务。每个任务都是独立的运行环境,互不干扰。
访问 Matrix 变量的方式:
${{ matrix.os }}— 获取当前任务的操作系统${{ matrix.python-version }}— 获取当前任务的 Python 版本${{ matrix.database }}— 获取当前任务的数据库类型
这些变量可以在 runs-on、steps、env 等地方使用,帮你动态调整每个任务的行为。
一个常见的坑:很多人以为 Matrix 会自动处理依赖安装的问题。实际上每个任务都是独立的环境,依赖安装会重复执行。这就导致了一个问题——如果你的依赖安装需要 2 分钟,16 个任务就是 32 分钟的等待时间(假设它们串行执行)。
解决方法有两个:
- 使用缓存 — 缓存
pip或npm的依赖目录,跳过重复下载 - 减少组合数 — 通过 exclude 排除不必要的测试组合
缓存策略我之前在 [GitHub Actions 缓存策略:加速 CI/CD 流水线 5 倍] 这篇文章里详细讲过,这里就不展开了。接下来我们重点讲讲怎么用 exclude/include 精准控制测试组合。
4. exclude/include:精准控制测试组合
Matrix 默认会把所有维度做全排列组合,但实际项目中,你经常会遇到「某些组合不需要测试」或者「某些组合需要特殊处理」的情况。
4.1 exclude:排除无效组合
我之前维护一个 Python 项目时遇到过这个问题:Windows + Python 3.9 的组合总是失败,因为某个依赖库在 Windows 上的 3.9 版本有兼容性问题。但这个项目主要面向 Linux 服务器部署,Windows 只是顺带支持,不值得花时间去修这个特定的 bug。
这时候 exclude 就派上用场了:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', '3.10', '3.11', '3.12']
exclude:
- os: windows-latest
python-version: '3.9'
- os: macos-latest
python-version: '3.9'
这个配置会排除 Windows 和 macOS 上的 Python 3.9 测试。原本 3 x 4 = 12 个任务,排除 2 个后变成 10 个任务。
exclude 的典型使用场景:
- 已知兼容性问题 — 某个版本在特定系统上跑不起来
- 资源限制 — 自托管 runner 数量有限,需要减少组合数
- 边缘场景 — 某些组合用户几乎不会用,不值得花 CI 时间
4.2 include:添加特殊配置
include 的作用相反——它帮你添加额外的测试组合,或者给特定组合补充额外变量。
比如说,你想在 Python 3.12 的测试中额外启用覆盖率报告,其他版本则不需要:
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
include:
- python-version: '3.12'
coverage: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -r requirements.txt
- name: Run tests
run: |
if [ "${{ matrix.coverage }}" = "true" ]; then
pytest --cov=src --cov-report=xml
else
pytest
fi
shell: bash
这里 include 做了两件事:
- 添加一个新组合 — Python 3.12 的测试
- 给这个组合补充额外变量 —
coverage: true
include 的典型使用场景:
- 实验性版本测试 — 比如 Python 3.13 预览版,只在某个系统上测试
- 特殊配置 — 某些组合需要额外的环境变量或参数
- 边缘场景覆盖 — 低频使用的组合,单独添加而不是通过全排列生成
4.3 exclude 和 include 可以一起用
实际项目中,你经常需要同时使用 exclude 和 include。比如:排除所有 Python 3.9 的组合,但单独添加一个 Ubuntu + Python 3.9 的最小化测试:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12']
exclude:
- python-version: '3.9'
include:
- os: ubuntu-latest
python-version: '3.9'
minimal: true
这个配置的执行顺序是:先生成所有组合 -> 应用 exclude 排除 -> 应用 include 添加。最终结果:Ubuntu 上跑 4 个版本,Windows 上跑 3 个版本(排除 3.9)。
5. fail-fast 策略:快速失败 vs 完整调试
Matrix 任务默认有一个行为:只要有一个任务失败,其他正在运行的任务会被取消。这个行为叫 fail-fast,默认是开启的。
strategy:
fail-fast: true # 默认值,可以省略
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20, 22]
5.1 什么时候用 fail-fast: true(默认)
PR 测试场景 — 开发者提了一个 PR,你想快速知道测试有没有问题。一个任务失败了,其他任务大概率也会失败(同样的代码问题),没必要继续浪费时间。
成本敏感场景 — GitHub Actions 的免费额度有限,自托管 runner 的资源也有限。快速失败能帮你省下不少钱。
我个人的习惯是:PR 测试用 fail-fast: true,main 分支的完整测试用 fail-fast: false。
5.2 什么时候用 fail-fast: false
调试阶段 — 你的 Matrix 任务经常失败,但你想知道具体是哪些组合失败、失败的原因各是什么。如果 fail-fast: true,你只能看到第一个失败的任务,其他任务被取消了。
兼容性测试 — 你在做多版本多平台的兼容性测试,想知道每个组合的测试结果。即使某个版本有问题,也不影响你获取其他版本的信息。
完整报告场景 — 你需要在 CI 结束后生成一份完整的测试报告,包含所有组合的通过/失败状态。
strategy:
fail-fast: false # 让所有任务跑完
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [18, 20, 22]
5.3 一个真实的案例
去年我帮一个项目排查 CI 问题,他们的测试在 Ubuntu + Node 18 上总是失败,但其他组合都通过。因为默认开启了 fail-fast,每次只能看到 Ubuntu + Node 18 失败,然后就取消了其他任务。后来他们想知道 Windows + Node 18 是不是也有问题,就改成了 fail-fast: false,结果发现 Windows 上没问题,只有 Ubuntu 有问题——最后定位到是文件路径大小写的兼容性问题。
所以我的建议是:开发调试阶段用 fail-fast: false,看清楚所有问题;稳定运行阶段用 fail-fast: true,省钱省时间。
6. max-parallel:并发控制与成本优化
Matrix 任务默认是并行执行的,GitHub 会尽可能多地同时启动任务。对于公开仓库,GitHub 托管 runner 的并发限制是 20 个;对于私有仓库,免费账户的并发限制是 2 个。
但有时候你需要手动控制并发数,这就是 max-parallel 的作用。
6.1 什么时候需要限制并发
自托管 runner 资源有限 — 你的 runner 服务器只有 4 核 8G,同时跑 8 个任务会把机器拖垮。
外部服务限流 — 你的测试需要调用第三方 API,对方有 QPS 限制,并发太高会被封。
数据库连接池限制 — 你的测试需要连接数据库,连接池只有 10 个连接,任务太多会耗尽连接。
strategy:
max-parallel: 4 # 最多同时跑 4 个任务
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20, 22]
这个配置会生成 6 个任务,但最多只有 4 个同时运行。跑完一个,再启动下一个。
6.2 成本计算的一个例子
假设你有一个项目,每次 CI 运行需要测试 3 个系统 x 4 个 Node 版本 = 12 个任务。每个任务平均运行 10 分钟。
不限制并发(假设 runner 足够):
- 12 个任务同时运行
- 总耗时约 10 分钟
- 总计算时间 = 12 x 10 = 120 分钟
限制 max-parallel: 4:
- 12 个任务分 3 批运行
- 总耗时约 30 分钟
- 总计算时间 = 12 x 10 = 120 分钟(不变)
看到了吗?max-parallel 不会减少总计算时间,只会延长总耗时。那为什么还要用?
因为 并发峰值成本 和 资源限制。
GitHub Actions 的计费单位是「分钟数」,但如果你用自托管 runner,或者你的云服务商按峰值计费,并发控制就很重要。比如说,12 个任务同时跑,你的数据库需要 12 个连接;分批跑,只需要 4 个连接。
我的实践经验:
- 公开仓库,GitHub 托管 runner:不用管
max-parallel,让它自己调度 - 私有仓库,免费额度:限制
max-parallel: 2,慢慢跑,不超额度 - 自托管 runner:根据服务器配置限制
max-parallel,4 核建议 2-4 个并发
7. 动态 matrix:fromJSON 进阶技术
前面讲的 Matrix 都是静态配置——你在 YAML 里写死要测试的版本。但有些场景下,你需要根据代码变化动态生成测试组合。
比如说,你有一个 monorepo,里面有多个服务,每个服务都有自己的测试配置。你想做到:只测试这次提交涉及到的服务,而不是所有服务都跑一遍。
7.1 两步 workflow 实现动态 matrix
GitHub Actions 没有直接支持「动态 matrix」的语法,但你可以用一个 job 生成 matrix 配置,然后传给另一个 job 使用。关键是 fromJSON() 函数。
jobs:
# 第一步:检测变化的服务,生成 matrix 配置
detect:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # 需要获取上一次提交
- name: Detect changed services
id: set-matrix
run: |
# 获取这次提交修改的文件
CHANGED_FILES=$(git diff --name-only HEAD^ HEAD)
# 判断哪些服务被修改了
SERVICES="[]"
if echo "$CHANGED_FILES" | grep -q "services/auth/"; then
SERVICES=$(echo $SERVICES | jq '. + ["auth"]')
fi
if echo "$CHANGED_FILES" | grep -q "services/api/"; then
SERVICES=$(echo $SERVICES | jq '. + ["api"]')
fi
if echo "$CHANGED_FILES" | grep -q "services/web/"; then
SERVICES=$(echo $SERVICES | jq '. + ["web"]')
fi
# 如果没有服务被修改,默认测试所有服务
if [ "$SERVICES" = "[]" ]; then
SERVICES='["auth", "api", "web"]'
fi
echo "matrix={\"service\":$(echo $SERVICES)}" >> $GITHUB_OUTPUT
# 第二步:使用动态生成的 matrix
test:
needs: detect
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.detect.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- name: Test ${{ matrix.service }}
run: |
cd services/${{ matrix.service }}
npm install
npm test
这个 workflow 的工作原理:
detectjob 检测这次提交修改了哪些目录- 根据修改的目录,动态生成一个 JSON 格式的 matrix 配置
testjob 使用fromJSON()解析这个配置,生成对应的任务
7.2 动态 matrix 的典型应用场景
Monorepo 场景 — 只测试变化的服务,节省 CI 时间
按需部署 — 检测 Dockerfile 变化,只构建和部署有更新的镜像
矩阵测试优化 — 根据文件类型决定测试组合(比如只有 package.json 变化才跑完整的多版本测试)
我踩过的坑:
fromJSON()只能用在strategy.matrix的值上,不能用在其他地方- 生成的 JSON 必须是合法的 matrix 格式,比如
{"service": ["auth", "api"]} - 如果生成的 matrix 为空,workflow 会直接报错——记得加默认值处理
8. 实战模板库:5 个生产级 workflow 示例
接下来给你 5 个可以直接复制使用的 workflow 模板,覆盖从个人项目到企业级应用的各种场景。
8.1 模板 1:Node.js 多版本测试(基础)
适用场景:Node.js 库或应用,需要兼容多个 Node 版本
name: Node.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, 20, 22, 23]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run build --if-present
- run: npm test
- name: Upload coverage
if: matrix.node-version == 22
uses: codecov/codecov-action@v4
关键点:
- 使用
npm ci而不是npm install,确保依赖版本锁定 - 只在 Node 22 上传覆盖率报告,避免重复上传
cache: 'npm'启用 npm 缓存,加速依赖安装
8.2 模板 2:Python 多平台多版本测试(中级)
适用场景:Python 项目,需要跨平台跨版本测试
name: Python CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.10', '3.11', '3.12']
exclude:
- os: windows-latest
python-version: '3.10' # 已知兼容性问题
steps:
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: pytest -v
- name: Lint check
run: |
pip install ruff
ruff check .
关键点:
- 使用
exclude排除已知问题的组合 cache: 'pip'加速 pip 依赖安装- 集成代码检查工具 ruff
8.3 模板 3:exclude/include 精准控制(高级)
适用场景:需要精细控制测试组合,排除特定组合,添加特殊测试
name: Advanced Matrix
on:
push:
branches: [main]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.10', '3.11', '3.12']
exclude:
# 排除 Windows + Python 3.10(已知问题)
- os: windows-latest
python-version: '3.10'
include:
# 添加一个实验性测试:Ubuntu + Python 3.13 预览版
- os: ubuntu-latest
python-version: '3.13-dev'
experimental: true
# 给 Python 3.12 添加覆盖率报告
- python-version: '3.12'
coverage: true
continue-on-error: ${{ matrix.experimental == true }}
steps:
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- run: pip install -r requirements.txt
- name: Run tests
run: |
if [ "${{ matrix.coverage }}" = "true" ]; then
pytest --cov=src --cov-report=xml
else
pytest
fi
shell: bash
关键点:
continue-on-error让实验性测试失败不影响整体状态include可以同时添加新组合和补充变量- 使用
shell: bash确保 Windows 和 Linux 命令一致
8.4 模板 4:动态 matrix + caching(进阶)
适用场景:Monorepo,根据文件变化动态生成测试组合
name: Dynamic Matrix CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
detect:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Detect changed packages
id: set-matrix
run: |
CHANGED_FILES=$(git diff --name-only HEAD^ HEAD)
PACKAGES="[]"
for dir in packages/*/; do
pkg=$(basename $dir)
if echo "$CHANGED_FILES" | grep -q "^packages/$pkg/"; then
PACKAGES=$(echo $PACKAGES | jq ". + [\"$pkg\"]")
fi
done
# 无变化时测试所有包
if [ "$PACKAGES" = "[]" ]; then
PACKAGES='["core", "utils", "cli"]'
fi
echo "matrix={\"package\":$(echo $PACKAGES)}" >> $GITHUB_OUTPUT
test:
needs: detect
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.detect.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build --if-present
- name: Test ${{ matrix.package }}
run: |
cd packages/${{ matrix.package }}
npm test
关键点:
fetch-depth: 2获取上一次提交用于比较- 使用
jq命令操作 JSON 数组 - 无变化时提供默认值,避免空 matrix 报错
8.5 模板 5:自托管 runner + max-parallel(企业级)
适用场景:自托管 runner,需要严格控制并发和资源
name: Enterprise CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: [self-hosted, linux, x64]
strategy:
fail-fast: true
max-parallel: 4
matrix:
java-version: [11, 17, 21]
database: [postgresql, mysql]
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
mysql:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup Java ${{ matrix.java-version }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java-version }}
distribution: 'temurin'
cache: 'maven'
- name: Run tests with ${{ matrix.database }}
env:
DB_TYPE: ${{ matrix.database }}
DB_HOST: localhost
DB_PORT: ${{ matrix.database == 'postgresql' && 5432 || 3306 }}
run: mvn test -Dspring.profiles.active=${{ matrix.database }}
- name: Archive test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.java-version }}-${{ matrix.database }}
path: target/surefire-reports
关键点:
runs-on: [self-hosted, linux, x64]指定自托管 runner 标签max-parallel: 4限制并发,保护 runner 服务器- 使用
services启动测试数据库容器 if: always()确保测试结果始终上传,即使测试失败
9. 常见陷阱与最佳实践
用 Matrix 这么久,我踩过不少坑。总结几个最常见的。
9.1 陷阱 1:组合数爆炸
我见过最夸张的配置:4 个操作系统 x 5 个运行时版本 x 3 个数据库 x 2 个缓存方案 = 120 个任务。每次 CI 跑完要 45 分钟,账单直接爆了。
解决方法:
- 只在 main 分支跑完整 Matrix,PR 只跑关键组合
- 使用
exclude排除边缘场景 - 评估每个维度的必要性——真的需要测 4 个操作系统吗?
# PR 只测关键组合
on:
pull_request:
branches: [main]
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest] # PR 只测 Ubuntu
node: [20] # PR 只测 Node 20
9.2 陷阱 2:fail-fast 阻碍调试
默认 fail-fast: true 在调试阶段很烦——一个任务失败,其他任务全被取消,你看不到完整的失败报告。
解决方法:调试阶段手动改成 fail-fast: false,调试完再改回来。
或者用环境变量控制:
strategy:
fail-fast: ${{ github.event_name == 'pull_request' }}
9.3 陷阱 3:缺少 caching
Matrix 会重复执行同一个 job 多次,如果每次都重新安装依赖,时间成本很高。我有次测试 12 个组合,每个安装依赖要 2 分钟,光安装就要 24 分钟。
解决方法:使用 GitHub Actions 缓存,或者用专门的缓存 action。
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm' # 关键:启用 npm 缓存
9.4 最佳实践总结
实践 1:PR 小型 Matrix + main 完整 Matrix
jobs:
test:
strategy:
matrix:
# PR 只测关键组合
${{ github.event_name == 'pull_request' && fromJSON('{"os":["ubuntu-latest"],"node":[20]}') || fromJSON('{"os":["ubuntu-latest","windows-latest","macos-latest"],"node":[18,20,22]}') }}
实践 2:exclude 排除已知问题组合
遇到特定组合的兼容性问题,先用 exclude 跳过,记个 TODO,后面再修。
实践 3:结合 caching 减少安装时间
每个 job 的依赖安装是最耗时的环节之一,用好缓存能把时间从分钟级降到秒级。
实践 4:给组合添加有意义的 name
默认的任务名称是 test (ubuntu-latest, 20) 这种格式,你可以用 name 自定义:
jobs:
test:
name: Test (${{ matrix.os }}, Node ${{ matrix.node }})
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20, 22]
这样在 GitHub Actions 界面更容易识别每个任务。
10. 结论
GitHub Actions Matrix 是处理多平台多版本测试的利器。核心就几件事:定义维度、控制组合、管理并发、动态生成。
我见过太多项目还在用手写的方式维护重复的 CI 配置,改一个命令要改十几处。Matrix 能帮你把几百行的配置压缩到几十行,维护成本直接下来。
关键点回顾:
- 基础语法:
matrix.os和matrix.node做笛卡尔积展开 - 精准控制:
exclude排除无效组合,include添加特殊配置 - 策略选择:
fail-fast根据场景选择,调试用 false,生产用 true - 并发限制:
max-parallel保护自托管 runner,控制成本 - 动态生成:
fromJSON()实现按需测试,节省 CI 资源
文章里的 5 个模板可以直接复制到你的项目里用,从最简单的 Node.js 多版本测试到企业级的自托管 runner 配置都有覆盖。
如果你刚开始用 Matrix,建议从模板 1 开始,跑通之后再加 exclude 和 include,最后再尝试动态生成。一步步来,别一上来就整复杂的。
有问题可以在评论区留言,或者去 GitHub Actions 官方文档看看更多细节。如果你有 Matrix 使用上的心得,也欢迎分享。
配置 GitHub Actions Matrix 多平台多版本测试
从零开始配置 Matrix 策略,实现跨平台跨版本的自动化测试
⏱️ 预计耗时: 30 分钟
- 1
步骤1: 定义 Matrix 维度
在 workflow 的 job 中添加 strategy.matrix 配置:
```yaml
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20, 22]
```
这会生成 2 x 3 = 6 个并行任务。 - 2
步骤2: 使用 Matrix 变量
在 runs-on 和 steps 中引用 matrix 变量:
```yaml
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
```
每个任务会自动获取对应的 os 和 node 值。 - 3
步骤3: 排除特定组合(可选)
使用 exclude 排除已知问题的组合:
```yaml
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20, 22]
exclude:
- os: windows-latest
node: 18
```
排除 Windows + Node 18 组合,最终生成 5 个任务。 - 4
步骤4: 配置失败策略
根据场景选择 fail-fast 策略:
- PR 测试:fail-fast: true(快速失败,省钱)
- 调试阶段:fail-fast: false(看全所有失败)
- main 分支:fail-fast: false(完整报告)
```yaml
strategy:
fail-fast: false
matrix:
# ...
``` - 5
步骤5: 限制并发数(可选)
自托管 runner 或资源有限时,限制并发:
```yaml
strategy:
max-parallel: 4
matrix:
# ...
```
最多同时运行 4 个任务,避免 runner 过载。
常见问题
Matrix 组合数有上限吗?
fail-fast 默认值是什么?
exclude 和 include 的执行顺序是什么?
动态 matrix 的 fromJSON() 能用在哪些地方?
max-parallel 会减少总计算时间吗?
Matrix 任务之间能共享缓存吗?
如何给 Matrix 任务添加自定义名称?
• name: Test (${{ matrix.os }}, Node ${{ matrix.node }})
这样在 GitHub Actions 界面会显示友好的名称如 "Test (ubuntu-latest, Node 20)",方便识别每个任务。
18 分钟阅读 · 发布于: 2026年4月28日 · 修改于: 2026年4月29日
相关文章
GitHub Actions Matrix 矩阵构建:多版本并行测试实战
GitHub Actions Matrix 矩阵构建:多版本并行测试实战
GitHub Actions 入门:YAML 工作流基础与触发器配置
GitHub Actions 入门:YAML 工作流基础与触发器配置
GitHub Actions 入门:YAML 工作流基础与触发器配置

评论
使用 GitHub 账号登录后即可评论