Matrix Strategy 基础
什么是矩阵构建?
矩阵构建(Matrix Build)允许你通过一个 Job 定义,自动生成多个并行运行的 Job 实例,每个实例使用不同的参数组合。例如,在 3 个操作系统 × 3 个 Node.js 版本上测试,只需写一次配置,GitHub Actions 会自动创建 9 个并行 Job。
矩阵构建的核心价值是:不用重复写相似的 Job 配置,且所有组合并行运行,不会增加总构建时间(但会消耗更多 Runner 资源)。
strategy.matrix
定义矩阵维度和各维度的值列表。多个维度之间取笛卡尔积,自动生成所有组合。每个组合生成一个独立的 Job 实例,可通过 ${{ matrix.xxx }} 访问当前实例的值。
include
两种用途:1) 添加笛卡尔积之外的额外组合;2) 为已有组合附加额外属性(精确匹配时只附加属性,不新建组合)。常用于为特定组合配置特殊参数。
exclude
从笛卡尔积中排除特定组合。常用场景:跳过已知有兼容性问题的组合、节省资源(如跳过耗时平台上的旧版本)。
fail-fast
默认 true:任何一个矩阵 Job 失败,立即取消所有其他正在运行的 Job,节省资源;设为 false:所有组合都跑完,收集全部结果,适合发布前的全面兼容性扫描。
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: ['18', '20', '22']
# 自动生成 3×3 = 9 个 Job 实例
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
- name: 在日志中标识当前矩阵
run: |
echo "OS: ${{ matrix.os }}"
echo "Node: ${{ matrix.node-version }}"
高级矩阵配置
include 和 exclude
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.10', '3.11', '3.12']
# include:添加额外的矩阵项或为现有项添加属性
include:
# 额外测试 Python 3.9(只在 Ubuntu 上)
- os: ubuntu-latest
python-version: '3.9'
experimental: true
# 为特定组合添加额外属性
- os: windows-latest
python-version: '3.12'
pip-cache-dir: 'C:\pip-cache'
# exclude:排除特定组合
exclude:
# Windows + Python 3.10 已知有兼容问题,暂时跳过
- os: windows-latest
python-version: '3.10'
fail-fast 与 max-parallel
strategy:
# fail-fast: true(默认)= 任何一个矩阵 Job 失败,取消所有其他 Job
# fail-fast: false = 所有 Job 都跑完,收集全部结果
fail-fast: false
# max-parallel: 限制同时运行的 Job 数量
# 避免耗尽 Runner 资源或超出 API 速率限制
max-parallel: 4
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [18, 20, 22]
何时使用 fail-fast: false?
当你需要了解所有平台的失败情况时(如发布前的全平台兼容性测试),设置 fail-fast: false 更合适。在日常 CI 中,fail-fast: true(默认)更高效——一旦发现失败就立即停止,节省资源。
动态矩阵(从文件生成)
jobs:
# 第一步:生成矩阵配置
prepare-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
run: |
# 从 services/ 目录自动发现需要测试的服务
SERVICES=$(ls services/ | jq -R -s -c 'split("\n")[:-1]')
echo "matrix={\"service\":$SERVICES}" >> $GITHUB_OUTPUT
# 第二步:使用动态矩阵
test-services:
needs: prepare-matrix
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.prepare-matrix.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- run: cd services/${{ matrix.service }} && npm test
实战:多语言兼容性测试矩阵
name: Cross-platform Compatibility Test
on: [push, pull_request]
jobs:
test:
name: Test (${{ matrix.os }}, Python ${{ matrix.python }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-20.04, windows-2022, macos-14]
python: ['3.10', '3.11', '3.12']
include:
# 在 ubuntu-22.04 + Python 3.12 上额外运行性能基准测试
- os: ubuntu-22.04
python: '3.12'
run-benchmarks: true
exclude:
# macOS 上跳过旧版本(节省资源)
- os: macos-14
python: '3.10'
steps:
- uses: actions/checkout@v4
- name: 配置 Python ${{ matrix.python }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
cache: 'pip'
- name: 安装依赖
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt
- name: 运行单元测试
run: pytest tests/ -v --tb=short
- name: 运行性能基准测试(仅特定矩阵项)
if: matrix.run-benchmarks == true
run: pytest benchmarks/ --benchmark-json=benchmark.json
- name: 上传基准测试结果
if: matrix.run-benchmarks == true
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: benchmark.json
矩阵构建结果聚合
矩阵 Job 各自独立运行,如何在后续 Job 中聚合所有结果?
jobs:
# 矩阵测试 Job
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
outputs:
# 每个矩阵实例都可以设置输出,但后续 Job 收到的是最后一个实例的值
# 因此单个输出值对矩阵聚合没有意义,通常用 Artifacts 传递结果
result: ${{ steps.test.outputs.result }}
steps:
- uses: actions/checkout@v4
- id: test
run: |
npm test
echo "result=passed" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.os }}
path: test-results/
# 聚合所有矩阵结果的 Job
aggregate:
needs: test
runs-on: ubuntu-latest
if: always() # 即使 test Job 失败也执行
steps:
# 下载所有矩阵实例的 Artifacts
- uses: actions/download-artifact@v4
with:
path: all-results/
# 检查 needs.test.result:'success'|'failure'|'cancelled'|'skipped'
- name: 检查矩阵整体结果
run: |
echo "矩阵测试结果: ${{ needs.test.result }}"
ls -la all-results/
# 可以合并所有测试报告或发送通知
- name: 生成汇总报告
if: needs.test.result == 'failure'
run: echo "有平台测试失败,详情见各 Artifacts"
并发控制(Concurrency)
矩阵构建会消耗大量并发 Runner。GitHub 免费账户有并发限制(公共仓库无限制,私有仓库根据套餐不同而有所限制)。此外,同一分支的多次推送可能触发多个 Workflow 同时运行,造成资源浪费。
name: CI
on: [push]
# 全局并发控制:同一 group 同时只有一个 Workflow 运行
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # 有新推送时取消旧的运行
jobs:
test:
runs-on: ubuntu-latest
strategy:
# 全局最多 2 个矩阵 Job 同时运行(避免消耗太多并发额度)
max-parallel: 2
matrix:
node: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci && npm test
矩阵构建的成本考量
矩阵构建增加了并发 Runner 的使用量。GitHub Actions 免费额度(公共仓库免费,私有仓库 2000 分钟/月)会被每个矩阵 Job 分别计算。例如 3×3=9 个 Job 每个运行 5 分钟,一次 Workflow 就消耗 45 分钟。对私有仓库,建议在 PR 时只运行关键平台(ubuntu),在合并到 main 时才运行全矩阵。
本章小结
本章核心要点
- 矩阵构建:一份 Job 配置 × 多维度参数 = 自动生成的多个并行 Job;适合兼容性测试(多 OS × 多语言版本);通过 ${{ matrix.xxx }} 访问当前实例的维度值。
- include/exclude:include 用于添加额外组合或为特定组合追加属性;exclude 用于从笛卡尔积中排除不需要的组合;两者配合可以精确控制测试矩阵。
- fail-fast vs max-parallel:fail-fast 控制一个失败是否取消其他(默认 true 节省资源,false 收集全部结果);max-parallel 限制同时运行的 Job 数量(避免消耗过多并发额度)。
- 动态矩阵:通过前置 Job 生成 JSON 矩阵配置,结合 fromJSON() + outputs 实现根据代码结构自动扩展测试范围(如自动发现所有微服务)。
- concurrency 控制:防止同一分支多次推送造成多个 Workflow 并行运行;cancel-in-progress: true 自动取消旧的运行,节省资源和避免冲突。