Appearance
GitHub Actions矩阵构建
矩阵构建是GitHub Actions中的一项强大功能,允许您在单个工作流中为不同的配置组合运行相同的作业。这在测试应用程序在不同环境下的兼容性、构建不同架构的软件包或并行执行多个相关任务时非常有用。
矩阵构建基础
基本矩阵配置
yaml
# .github/workflows/matrix-example.yml
name: Matrix Build Example
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Report environment
run: |
echo "OS: ${{ runner.os }}"
echo "Node version: ${{ matrix.node-version }}"
矩阵构建的优势
矩阵构建的主要优势包括:
- 并行执行:多个配置组合同时运行,节省总执行时间
- 一致性:相同的步骤在不同环境中执行
- 全面覆盖:确保应用程序在各种环境下正常工作
- 易于维护:单一工作流配置管理多个环境
矩阵策略配置
基本策略选项
yaml
jobs:
matrix-test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [16, 18, 20]
fail-fast: true # 任一矩阵作业失败时停止所有作业(默认)
# fail-fast: false # 所有矩阵作业继续运行,即使有失败
max-parallel: 2 # 最大并行运行的矩阵作业数(默认为所有)
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
故障容忍策略
yaml
jobs:
fault-tolerant-matrix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
fail-fast: false # 即使一个作业失败,其他作业继续运行
max-parallel: 4 # 限制并行作业数以控制资源使用
steps:
- name: Run test with error handling
run: |
npm test || echo "Tests failed but continuing..."
复杂矩阵配置
包含和排除配置
yaml
jobs:
complex-matrix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
architecture: [x64, arm64]
# 包含特定的组合
include:
- os: ubuntu-latest
node-version: 16
architecture: x64
experimental: true
- os: windows-latest
node-version: 20
architecture: x64
experimental: false
# 排除特定的组合
exclude:
- os: windows-latest
node-version: 16
- os: macos-latest
architecture: arm64
- os: ubuntu-latest
node-version: 20
architecture: arm64
steps:
- name: Setup environment
run: |
echo "OS: ${{ matrix.os }}"
echo "Node: ${{ matrix.node-version }}"
echo "Architecture: ${{ matrix.architecture }}"
if [ "${{ matrix.experimental }}" = "true" ]; then
echo "Running experimental configuration"
fi
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
动态矩阵配置
yaml
jobs:
dynamic-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: |
# 根据某些条件动态设置矩阵
if [ "${{ github.event_name }}" = "push" ]; then
# 推送时运行所有配置
MATRIX='{"os":["ubuntu-latest","windows-latest","macos-latest"],"node":["16","18","20"]}'
else
# PR时只运行关键配置
MATRIX='{"os":["ubuntu-latest"],"node":["18"]}'
fi
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
run-tests:
needs: dynamic-matrix
runs-on: ${{ matrix.os }}
strategy:
matrix: ${{ fromJSON(needs.dynamic-matrix.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test
多维度矩阵
操作系统和运行时矩阵
yaml
jobs:
os-runtime-matrix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runtime:
- { name: 'node', version: '16' }
- { name: 'node', version: '18' }
- { name: 'python', version: '3.9' }
- { name: 'python', version: '3.11' }
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
if: matrix.runtime.name == 'node'
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.runtime.version }}
- name: Setup Python
if: matrix.runtime.name == 'python'
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.runtime.version }}
- name: Run tests
run: |
if [ "${{ matrix.runtime.name }}" = "node" ]; then
npm ci
npm test
elif [ "${{ matrix.runtime.name }}" = "python" ]; then
pip install -r requirements.txt
python -m pytest
fi
数据库兼容性矩阵
yaml
jobs:
database-matrix:
runs-on: ubuntu-latest
strategy:
matrix:
database:
- { name: 'postgres', version: '13', port: '5432' }
- { name: 'postgres', version: '14', port: '5433' }
- { name: 'mysql', version: '8.0', port: '3306' }
- { name: 'mysql', version: '5.7', port: '3307' }
services:
database:
image: ${{ matrix.database.name }}:${{ matrix.database.version }}
env:
POSTGRES_PASSWORD: postgres
MYSQL_ROOT_PASSWORD: root
options: >-
--health-cmd "pg_isready -U postgres || mysqladmin ping -u root -proot"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install and test with ${{ matrix.database.name }} ${{ matrix.database.version }}
run: |
npm ci
npm run test:db
env:
DB_HOST: localhost
DB_PORT: ${{ matrix.database.port }}
DB_NAME: test
DB_USER: postgres
DB_PASSWORD: postgres
矩阵与条件执行
矩阵条件执行
yaml
jobs:
conditional-matrix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
experimental: [false, true]
include:
- os: ubuntu-latest
node-version: 20
experimental: true
run-experimental: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Regular tests
run: npm test
- name: Experimental tests
if: matrix.run-experimental == true
run: npm run test:experimental
矩阵依赖作业
yaml
jobs:
setup:
runs-on: ubuntu-latest
outputs:
build-id: ${{ steps.build-info.outputs.id }}
steps:
- id: build-info
run: |
BUILD_ID=$(date +%s)
echo "id=$BUILD_ID" >> $GITHUB_OUTPUT
test:
needs: setup
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Run tests with build ID
run: |
npm ci
npm test
env:
BUILD_ID: ${{ needs.setup.outputs.build-id }}
deploy:
needs: [setup, test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy with matrix results
run: |
echo "Deploying build ${{ needs.setup.outputs.build-id }}"
echo "All matrix tests passed"
高级矩阵用法
自定义矩阵键名
yaml
jobs:
custom-matrix-keys:
runs-on: ${{ matrix.runner }}
strategy:
matrix:
runner: [ubuntu-latest, windows-latest]
language:
- { name: 'javascript', version: '18', command: 'npm test' }
- { name: 'python', version: '3.11', command: 'python -m pytest' }
- { name: 'go', version: '1.21', command: 'go test ./...' }
steps:
- uses: actions/checkout@v4
- name: Setup JavaScript
if: matrix.language.name == 'javascript'
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.language.version }}
- name: Setup Python
if: matrix.language.name == 'python'
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.language.version }}
- name: Setup Go
if: matrix.language.name == 'go'
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.language.version }}
- name: Run tests
run: ${{ matrix.language.command }}
矩阵与环境变量
yaml
jobs:
matrix-env-vars:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
env-type: [development, staging, production]
steps:
- name: Set environment-specific variables
run: |
if [ "${{ matrix.env-type }}" = "production" ]; then
echo "API_URL=https://api.example.com" >> $GITHUB_ENV
echo "LOG_LEVEL=error" >> $GITHUB_ENV
elif [ "${{ matrix.env-type }}" = "staging" ]; then
echo "API_URL=https://staging-api.example.com" >> $GITHUB_ENV
echo "LOG_LEVEL=warn" >> $GITHUB_ENV
else
echo "API_URL=https://dev-api.example.com" >> $GITHUB_ENV
echo "LOG_LEVEL=debug" >> $GITHUB_ENV
fi
shell: bash
- name: Run with environment
run: |
echo "Testing with API: $API_URL"
echo "Log level: $LOG_LEVEL"
# 实际测试命令
矩阵构建最佳实践
1. 合理控制矩阵大小
yaml
# 避免过大的矩阵
# 不好的做法:10x10x5 = 500个作业
# strategy:
# matrix:
# os: [all-possible-os]
# node-version: [all-possible-versions]
# database: [all-possible-databases]
# 好的做法:控制在可管理的范围内
strategy:
matrix:
os: [ubuntu-latest, windows-latest] # 限制操作系统数量
node-version: [18, 20] # 只测试关键版本
# 总作业数:2x2 = 4个作业
2. 使用并行限制
yaml
jobs:
controlled-parallelism:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
max-parallel: 3 # 限制同时运行的作业数
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
3. 优化矩阵配置
yaml
jobs:
optimized-matrix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
# 定义最常用的组合
- os: ubuntu-latest
node-version: '18'
test-suite: 'full'
- os: windows-latest
node-version: '18'
test-suite: 'core' # Windows上只运行核心测试
- os: macos-latest
node-version: '20'
test-suite: 'full'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Run ${{ matrix.test-suite }} tests
run: |
if [ "${{ matrix.test-suite }}" = "full" ]; then
npm test
else
npm run test:core
fi
实际应用示例
完整的跨平台测试矩阵
yaml
name: Cross-Platform Testing
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test-matrix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16.x, 18.x, 20.x]
# 排除某些不兼容的组合
exclude:
# Node 16在macOS上可能有问题
- os: macos-latest
node-version: 16.x
fail-fast: false
max-parallel: 6
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run unit tests
run: npm run test:unit
- name: Run integration tests
run: npm run test:integration
- name: Generate coverage report
run: npm run test:coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.os }}-node${{ matrix.node-version }}
path: coverage/
if: runner.os == 'Linux' # 只上传Linux的覆盖率报告
security-scan:
runs-on: ubuntu-latest
needs: test-matrix
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Security scan
run: npm audit --audit-level moderate
deploy:
runs-on: ubuntu-latest
needs: [test-matrix, security-scan]
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Deploy to production
run: echo "Deploying production build"
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
构建多个架构的发布包
yaml
name: Build and Release
on:
push:
tags: ['v*']
jobs:
build-matrix:
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- runner: ubuntu-latest
os: linux
arch: x64
target: x86_64-unknown-linux-musl
- runner: ubuntu-latest
os: linux
arch: arm64
target: aarch64-unknown-linux-musl
- runner: windows-latest
os: windows
arch: x64
target: x86_64-pc-windows-msvc
- runner: macos-latest
os: darwin
arch: x64
target: x86_64-apple-darwin
- runner: macos-latest
os: darwin
arch: arm64
target: aarch64-apple-darwin
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build for ${{ matrix.target }}
run: |
rustup target add ${{ matrix.target }}
cargo build --release --target ${{ matrix.target }}
- name: Package binary
run: |
BINARY_NAME="myapp-${{ matrix.os }}-${{ matrix.arch }}"
if [ "${{ matrix.os }}" = "windows" ]; then
BINARY_NAME="${BINARY_NAME}.exe"
cp "target/${{ matrix.target }}/release/myapp.exe" "$BINARY_NAME"
else
cp "target/${{ matrix.target }}/release/myapp" "$BINARY_NAME"
fi
chmod +x "$BINARY_NAME"
shell: bash
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: myapp-${{ matrix.os }}-${{ matrix.arch }}
path: myapp-*
release:
runs-on: ubuntu-latest
needs: build-matrix
steps:
- name: Download all binaries
uses: actions/download-artifact@v4
- name: Create release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
浏览器兼容性测试矩阵
yaml
name: Browser Compatibility Tests
on: [push, pull_request]
jobs:
e2e-test:
runs-on: ubuntu-latest
strategy:
matrix:
browser: [chrome, firefox, safari]
node-version: [18]
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Run E2E tests on ${{ matrix.browser }}
run: npm run test:e2e -- --browser=${{ matrix.browser }}
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.browser }}
path: test-results/
通过合理使用矩阵构建,可以高效地在多种配置下测试和构建应用程序,确保其在不同环境下的兼容性和稳定性。