matrix-optimizer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Matrix Optimizer

矩阵优化器

Configure and optimize GitHub Actions matrix strategies for efficient multi-version and multi-platform testing.
配置并优化GitHub Actions矩阵策略,以实现高效的多版本和多平台测试。

Quick Start

快速开始

Basic matrix for testing multiple Node.js versions:
yaml
strategy:
  matrix:
    node-version: [16, 18, 20]
用于测试多个Node.js版本的基础矩阵:
yaml
strategy:
  matrix:
    node-version: [16, 18, 20]

Instructions

操作指南

Step 1: Identify Matrix Dimensions

步骤1:确定矩阵维度

Common matrix dimensions:
  • Language versions: Node.js, Python, Ruby, Go versions
  • Operating systems: ubuntu, macos, windows
  • Architectures: x64, arm64
  • Dependency versions: Database versions, framework versions
  • Feature flags: Different configuration options
Example dimensions:
yaml
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest, windows-latest]
    node-version: [16, 18, 20]
    # This creates 9 jobs (3 OS × 3 versions)
常见矩阵维度:
  • 语言版本:Node.js、Python、Ruby、Go版本
  • 操作系统:ubuntu、macos、windows
  • 架构:x64、arm64
  • 依赖版本:数据库版本、框架版本
  • 功能标志:不同配置选项
示例维度:
yaml
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest, windows-latest]
    node-version: [16, 18, 20]
    # 这会创建9个任务(3个操作系统 × 3个版本)

Step 2: Configure Matrix Strategy

步骤2:配置矩阵策略

Basic matrix:
yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
        node-version: [18, 20]
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm test
Matrix with include:
yaml
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest]
    node-version: [18, 20]
    include:
      # Add specific combination
      - os: windows-latest
        node-version: 20
      # Add extra variables for specific combination
      - os: ubuntu-latest
        node-version: 20
        experimental: true
Matrix with exclude:
yaml
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest, windows-latest]
    node-version: [16, 18, 20]
    exclude:
      # Skip Node 16 on Windows
      - os: windows-latest
        node-version: 16
      # Skip Node 16 on macOS
      - os: macos-latest
        node-version: 16
基础矩阵:
yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
        node-version: [18, 20]
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm test
包含额外配置的矩阵:
yaml
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest]
    node-version: [18, 20]
    include:
      # 添加特定组合
      - os: windows-latest
        node-version: 20
      # 为特定组合添加额外变量
      - os: ubuntu-latest
        node-version: 20
        experimental: true
排除指定组合的矩阵:
yaml
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest, windows-latest]
    node-version: [16, 18, 20]
    exclude:
      # 跳过Windows上的Node 16
      - os: windows-latest
        node-version: 16
      # 跳过macOS上的Node 16
      - os: macos-latest
        node-version: 16

Step 3: Optimize for Cost and Speed

步骤3:针对成本与速度进行优化

Fail-fast strategy:
yaml
strategy:
  fail-fast: false  # Continue all jobs even if one fails
  matrix:
    node-version: [16, 18, 20]
Max parallel jobs:
yaml
strategy:
  max-parallel: 2  # Limit concurrent jobs
  matrix:
    node-version: [16, 18, 20]
Conditional matrix:
yaml
strategy:
  matrix:
    os: [ubuntu-latest]
    # Add more OS only on main branch
    ${{ github.ref == 'refs/heads/main' && fromJSON('["macos-latest", "windows-latest"]') || fromJSON('[]') }}
失败不终止策略:
yaml
strategy:
  fail-fast: false  # 即使有任务失败,仍继续执行所有任务
  matrix:
    node-version: [16, 18, 20]
最大并行任务数:
yaml
strategy:
  max-parallel: 2  # 限制并发任务数
  matrix:
    node-version: [16, 18, 20]
条件化矩阵:
yaml
strategy:
  matrix:
    os: [ubuntu-latest]
    # 仅在主分支添加更多操作系统
    ${{ github.ref == 'refs/heads/main' && fromJSON('["macos-latest", "windows-latest"]') || fromJSON('[]') }}

Step 4: Use Matrix Variables

步骤4:使用矩阵变量

In job steps:
yaml
steps:
  - name: Display matrix values
    run: |
      echo "OS: ${{ matrix.os }}"
      echo "Version: ${{ matrix.node-version }}"
      echo "Experimental: ${{ matrix.experimental }}"
In job configuration:
yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    continue-on-error: ${{ matrix.experimental == true }}
    strategy:
      matrix:
        os: [ubuntu-latest]
        node-version: [18, 20]
        include:
          - node-version: 21
            experimental: true
在任务步骤中使用:
yaml
steps:
  - name: Display matrix values
    run: |
      echo "OS: ${{ matrix.os }}"
      echo "Version: ${{ matrix.node-version }}"
      echo "Experimental: ${{ matrix.experimental }}"
在任务配置中使用:
yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    continue-on-error: ${{ matrix.experimental == true }}
    strategy:
      matrix:
        os: [ubuntu-latest]
        node-version: [18, 20]
        include:
          - node-version: 21
            experimental: true

Step 5: Name Jobs Clearly

步骤5:清晰命名任务

yaml
jobs:
  test:
    name: Test on ${{ matrix.os }} with Node ${{ matrix.node-version }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
        node-version: [18, 20]
yaml
jobs:
  test:
    name: Test on ${{ matrix.os }} with Node ${{ matrix.node-version }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
        node-version: [18, 20]

Common Patterns

常见模式

Language Version Matrix

语言版本矩阵

Node.js:
yaml
strategy:
  matrix:
    node-version: [16, 18, 20]
steps:
  - uses: actions/setup-node@v3
    with:
      node-version: ${{ matrix.node-version }}
Python:
yaml
strategy:
  matrix:
    python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
  - uses: actions/setup-python@v4
    with:
      python-version: ${{ matrix.python-version }}
Go:
yaml
strategy:
  matrix:
    go-version: ['1.20', '1.21', '1.22']
steps:
  - uses: actions/setup-go@v4
    with:
      go-version: ${{ matrix.go-version }}
Node.js:
yaml
strategy:
  matrix:
    node-version: [16, 18, 20]
steps:
  - uses: actions/setup-node@v3
    with:
      node-version: ${{ matrix.node-version }}
Python:
yaml
strategy:
  matrix:
    python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
  - uses: actions/setup-python@v4
    with:
      python-version: ${{ matrix.python-version }}
Go:
yaml
strategy:
  matrix:
    go-version: ['1.20', '1.21', '1.22']
steps:
  - uses: actions/setup-go@v4
    with:
      go-version: ${{ matrix.go-version }}

Cross-Platform Matrix

跨平台矩阵

yaml
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest, windows-latest]
    include:
      # Platform-specific configurations
      - os: ubuntu-latest
        install-cmd: sudo apt-get install
      - os: macos-latest
        install-cmd: brew install
      - os: windows-latest
        install-cmd: choco install

steps:
  - name: Install dependencies
    run: ${{ matrix.install-cmd }} package-name
yaml
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest, windows-latest]
    include:
      # 平台特定配置
      - os: ubuntu-latest
        install-cmd: sudo apt-get install
      - os: macos-latest
        install-cmd: brew install
      - os: windows-latest
        install-cmd: choco install

steps:
  - name: Install dependencies
    run: ${{ matrix.install-cmd }} package-name

Database Version Matrix

数据库版本矩阵

yaml
strategy:
  matrix:
    postgres-version: [12, 13, 14, 15]

services:
  postgres:
    image: postgres:${{ matrix.postgres-version }}
    env:
      POSTGRES_PASSWORD: postgres
    options: >-
      --health-cmd pg_isready
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5
yaml
strategy:
  matrix:
    postgres-version: [12, 13, 14, 15]

services:
  postgres:
    image: postgres:${{ matrix.postgres-version }}
    env:
      POSTGRES_PASSWORD: postgres
    options: >-
      --health-cmd pg_isready
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5

Feature Flag Matrix

功能标志矩阵

yaml
strategy:
  matrix:
    feature:
      - name: baseline
        flags: ''
      - name: new-parser
        flags: '--enable-new-parser'
      - name: experimental
        flags: '--enable-experimental'

steps:
  - name: Run tests
    run: npm test ${{ matrix.feature.flags }}
yaml
strategy:
  matrix:
    feature:
      - name: baseline
        flags: ''
      - name: new-parser
        flags: '--enable-new-parser'
      - name: experimental
        flags: '--enable-experimental'

steps:
  - name: Run tests
    run: npm test ${{ matrix.feature.flags }}

Optimization Strategies

优化策略

Reduce Matrix Size

缩小矩阵规模

Before (12 jobs):
yaml
matrix:
  os: [ubuntu-latest, macos-latest, windows-latest]
  node-version: [16, 18, 20, 21]
After (7 jobs):
yaml
matrix:
  # Test all versions on Linux only
  os: [ubuntu-latest]
  node-version: [16, 18, 20, 21]
  include:
    # Test latest version on other platforms
    - os: macos-latest
      node-version: 21
    - os: windows-latest
      node-version: 21
优化前(12个任务):
yaml
matrix:
  os: [ubuntu-latest, macos-latest, windows-latest]
  node-version: [16, 18, 20, 21]
优化后(7个任务):
yaml
matrix:
  # 仅在Linux上测试所有版本
  os: [ubuntu-latest]
  node-version: [16, 18, 20, 21]
  include:
    # 在其他平台上仅测试最新版本
    - os: macos-latest
      node-version: 21
    - os: windows-latest
      node-version: 21

Conditional Matrix Expansion

条件化矩阵扩展

yaml
strategy:
  matrix:
    # Always test on Linux
    os: [ubuntu-latest]
    node-version: [18, 20]
    include:
      # Full matrix only on main branch or release tags
      - ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}:
        os: [macos-latest, windows-latest]
yaml
strategy:
  matrix:
    # 始终在Linux上测试
    os: [ubuntu-latest]
    node-version: [18, 20]
    include:
      # 仅在主分支或发布标签下使用完整矩阵
      - ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}:
        os: [macos-latest, windows-latest]

Parallel vs Sequential

并行与串行对比

High parallelism (faster, more expensive):
yaml
strategy:
  matrix:
    shard: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
  - run: npm test -- --shard=${{ matrix.shard }}/8
Limited parallelism (slower, cheaper):
yaml
strategy:
  max-parallel: 2
  matrix:
    shard: [1, 2, 3, 4, 5, 6, 7, 8]
高并行度(更快,成本更高):
yaml
strategy:
  matrix:
    shard: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
  - run: npm test -- --shard=${{ matrix.shard }}/8
有限并行度(更慢,成本更低):
yaml
strategy:
  max-parallel: 2
  matrix:
    shard: [1, 2, 3, 4, 5, 6, 7, 8]

Caching Across Matrix

跨矩阵缓存

yaml
steps:
  - uses: actions/cache@v3
    with:
      path: ~/.npm
      key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
      restore-keys: |
        ${{ runner.os }}-node-${{ matrix.node-version }}-
        ${{ runner.os }}-node-
yaml
steps:
  - uses: actions/cache@v3
    with:
      path: ~/.npm
      key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
      restore-keys: |
        ${{ runner.os }}-node-${{ matrix.node-version }}-
        ${{ runner.os }}-node-

Advanced Patterns

高级模式

Dynamic Matrix from JSON

从JSON生成动态矩阵

yaml
jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - id: set-matrix
        run: |
          # Generate matrix dynamically
          MATRIX='{"include":[{"os":"ubuntu-latest","version":"18"},{"os":"macos-latest","version":"20"}]}'
          echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
  
  test:
    needs: setup
    strategy:
      matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
    runs-on: ${{ matrix.os }}
    steps:
      - run: echo "Testing on ${{ matrix.os }} with version ${{ matrix.version }}"
yaml
jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - id: set-matrix
        run: |
          # 动态生成矩阵
          MATRIX='{"include":[{"os":"ubuntu-latest","version":"18"},{"os":"macos-latest","version":"20"}]}'
          echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
  
  test:
    needs: setup
    strategy:
      matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
    runs-on: ${{ matrix.os }}
    steps:
      - run: echo "Testing on ${{ matrix.os }} with version ${{ matrix.version }}"

Matrix with Outputs

带输出的矩阵

yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
    outputs:
      result-${{ matrix.os }}: ${{ steps.test.outputs.result }}
    steps:
      - id: test
        run: echo "result=passed" >> $GITHUB_OUTPUT
yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
    outputs:
      result-${{ matrix.os }}: ${{ steps.test.outputs.result }}
    steps:
      - id: test
        run: echo "result=passed" >> $GITHUB_OUTPUT

Reusable Matrix Workflow

可复用矩阵工作流

yaml
undefined
yaml
undefined

.github/workflows/reusable-matrix.yml

.github/workflows/reusable-matrix.yml

on: workflow_call: inputs: versions: required: true type: string
jobs: test: strategy: matrix: version: ${{ fromJSON(inputs.versions) }} runs-on: ubuntu-latest steps: - run: echo "Testing version ${{ matrix.version }}"
on: workflow_call: inputs: versions: required: true type: string
jobs: test: strategy: matrix: version: ${{ fromJSON(inputs.versions) }} runs-on: ubuntu-latest steps: - run: echo "Testing version ${{ matrix.version }}"

Caller workflow

调用方工作流

jobs: test: uses: ./.github/workflows/reusable-matrix.yml with: versions: '["18", "20", "21"]'
undefined
jobs: test: uses: ./.github/workflows/reusable-matrix.yml with: versions: '["18", "20", "21"]'
undefined

Troubleshooting

故障排除

Too many jobs:
  • Use
    exclude
    to remove unnecessary combinations
  • Test all versions on one OS, latest version on others
  • Use conditional matrix expansion for PRs vs main
Jobs failing inconsistently:
  • Set
    fail-fast: false
    to see all failures
  • Check for race conditions or timing issues
  • Verify platform-specific dependencies
Slow matrix execution:
  • Increase
    max-parallel
    if budget allows
  • Optimize caching strategy
  • Consider test sharding within jobs
Matrix not expanding:
  • Verify JSON syntax in
    fromJSON()
  • Check that matrix variables are properly referenced
  • Ensure
    include
    and
    exclude
    syntax is correct
任务数量过多:
  • 使用
    exclude
    移除不必要的组合
  • 在一个操作系统上测试所有版本,在其他操作系统上仅测试最新版本
  • 针对PR和主分支使用不同的条件化矩阵扩展
任务失败情况不一致:
  • 设置
    fail-fast: false
    以查看所有失败情况
  • 检查竞争条件或时序问题
  • 验证平台特定依赖
矩阵执行缓慢:
  • 如果预算允许,增加
    max-parallel
  • 优化缓存策略
  • 考虑在任务内进行测试分片
矩阵未展开:
  • 验证
    fromJSON()
    中的JSON语法
  • 检查矩阵变量是否被正确引用
  • 确保
    include
    exclude
    语法正确

Best Practices

最佳实践

  1. Start small: Begin with minimal matrix, expand as needed
  2. Test locally first: Verify one configuration works before expanding
  3. Use fail-fast: false: See all failures, not just first
  4. Name jobs clearly: Include matrix values in job names
  5. Cache effectively: Use matrix values in cache keys
  6. Optimize for PRs: Smaller matrix for PRs, full matrix for main
  7. Document matrix: Explain why each dimension is needed
  8. Monitor costs: Track runner minutes usage
  1. 从小规模开始:先使用最小矩阵,再根据需要扩展
  2. 先本地测试:在扩展前先验证单个配置可正常工作
  3. 使用fail-fast: false:查看所有失败情况,而非仅第一个
  4. 清晰命名任务:在任务名称中包含矩阵值
  5. 有效缓存:在缓存键中使用矩阵值
  6. 针对PR优化:PR使用较小矩阵,主分支使用完整矩阵
  7. 文档化矩阵:说明每个维度的必要性
  8. 监控成本:跟踪运行器分钟数使用情况