GitHub Actions 矩阵构建
yaml jobs: test: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] node-version: [16, 18, 20] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm test
上面的配置会产生 **3(操作系统)× 3(Node.js 版本)= 9** 个并行作业。每个作业都可以通过 `${{ matrix.os }}` 和 `${{ matrix.node-version }}` 访问当前组合的具体值。
### 变量命名与引用
矩阵中的变量名可以自定义,例如 `python-version`、`browser` 等。在作业的其他部分(如 `runs-on`、`steps`、`env`)都可以通过上下文 `${{ matrix.<variable> }}` 来引用。注意,变量名只能包含字母、数字、`-` 和 `_`,且不能以 `github` 开头。
## 扩展矩阵功能
除了直接生成笛卡尔积,GitHub Actions 还提供了更精细的控制选项。
### 使用 `include` 添加特定组合
有时你需要添加一些不便于用乘积表达的组合,或者为某些组合附加额外配置。`include` 允许你向矩阵中手动追加作业,并且可以引入**不属于原始矩阵的新变量**。
```yaml
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [18]
include:
- os: ubuntu-latest
node-version: 16
experimental: true
- os: macos-latest
node-version: 20
experimental: false
上面的配置会生成以下作业:
(ubuntu-latest, 18)-- 来自原有乘积(windows-latest, 18)-- 来自原有乘积(ubuntu-latest, 16)-- 由 include 追加,并带有一个额外的experimental变量(macos-latest, 20)-- 由 include 追加
你可以在后续步骤中通过 ${{ matrix.experimental }} 访问这个额外变量(在未定义该变量的作业中,此值为空)。
使用 exclude 移除不需要的组合
如果某些组合没有意义或不需要测试,可以用 exclude 从矩阵中删除。
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
browser: [chrome, firefox, edge]
exclude:
- os: windows-latest
browser: firefox
- os: ubuntu-latest
browser: edge
原先生成 2 × 3 = 6 个组合,排除后只剩下 4 个作业。
控制并行与失败行为
你可以在 strategy 下设置更多属性来控制整个矩阵作业组的行为。
max-parallel:限制同时运行的作业数量。例如,当你的账户并发作业数有限,或想避免对目标服务的瞬间压力时,可以设置max-parallel: 2。fail-fast:默认值为true。一旦任何一个矩阵作业失败,GitHub 会取消所有其他仍在运行或排队的作业。如果你想即使有作业失败也让其他作业继续跑完(以收集完整的失败信息),可以设置为false。
strategy:
fail-fast: false
max-parallel: 4
matrix:
...
实战示例:多平台多语言测试
下面的例子展示了一个实际项目中常见的场景:一个 Python 库需要在不同操作系统和 Python 版本下运行测试,同时使用 include 添加一个特殊的“最新开发环境”组合,并用 exclude 排除不必要的旧版本 Python 在 Windows 上的测试。
name: CI
on: [push, pull_request]
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.8", "3.9", "3.10", "3.11"]
exclude:
# 不在 Windows 上测试 Python 3.8
- os: windows-latest
python-version: "3.8"
include:
# 额外加入一个 ubuntu 下的 Python 3.12-dev 环境
- os: ubuntu-latest
python-version: "3.12-dev"
allow-failure: true
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.allow-failure == true }}
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -r requirements-dev.txt
- run: pytest
这里用到了 continue-on-error 上下文,依赖 include 中额外定义的 allow-failure 变量,使得实验性 Python 版本的失败不会标记整个工作流为失败。
动态矩阵与条件矩阵
有时候,你需要根据分支、标签或其他上下文动态生成矩阵变量。虽然标准 YAML 矩阵是静态的,但你可以通过 fromJSON() 函数结合前序步骤来动态定义矩阵。
一种常见模式是使用一个输出 JSON 的步骤,再通过作业输出传递给下游的矩阵作业。
jobs:
prepare-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo '{"node": [16, 18, 20]}' > matrix.json
else
echo '{"node": [20]}' > matrix.json
fi
echo "matrix=$(cat matrix.json)" >> $GITHUB_OUTPUT
test:
needs: prepare-matrix
strategy:
matrix: ${{ fromJSON(needs.prepare-matrix.outputs.matrix) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test