

解决什么问题
GitHub Actions 的 matrix 策略让你可以用一条 workflow 跑遍 Python 3.9/3.10/3.11/3.12 四个版本 + macOS/Linux/Windows 三个系统。但真正用过的人都知道:matrix 跑起来很爽,调试起来很痛。本文记录我在生产环境中遇到的真实问题:环境变量传不进、依赖缓存命中率只有 30%、超时时间设置无效、删除矩阵维度后 CI 反而更慢。
一、基础矩阵配置
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, macos-14, windows-2022]
python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -r requirements.txt
- run: pytest tests/
fail-fast: false 是关键——默认 true 导致一个版本失败就取消所有 job,调试时极不友好。
二、问题 1:矩阵 job 收不到环境变量
在 env 全局定义的变量,矩阵 job 里读不到:
env:
NODE_ENV: production
API_URL: https://api.example.com
jobs:
test:
runs-on: ubuntu-22.04
# ❌ 这里 ${{ env.API_URL }} 是空字符串
steps:
- run: echo ${{ env.API_URL }}
正确做法是在 job 或 step 级别单独声明:
jobs:
test:
runs-on: ubuntu-22.04
env:
API_URL: https://api.example.com
steps:
- run: echo ${{ env.API_URL }}
# ✅ 输出 https://api.example.com
踩坑经历:项目从 Travis CI 迁移过来,习惯在全局写 env,GitHub Actions 的 job 级别作用域完全不一样。
三、问题 2:依赖缓存命中率只有 30%
用了 actions/cache 但命中率惨淡,看日志发现每次都 generate 新 key:
steps:
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${{ runner.os }}-${{ hashFiles('requirements.txt') }}
问题是 hashFiles 匹配的是 checkout 之后的文件,但 setup-python 默认不指定 architecture:
# 修复:加入 python 版本到 key
key: pip-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}
restore-keys: |
pip-${{ runner.os }}-py${{ matrix.python-version }}-
这样 4 个 Python 版本各自独立缓存,不再互相覆盖。实测从 30% 命中率提升到 85%+。
四、问题 3:macOS _runner 持续时间莫名很长
macOS runner 的启动时间比 Ubuntu 长 3-5 倍,加上 Homebrew 每次更新 brew update,job 时间从 2 分钟变成 12 分钟。
jobs:
test:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Skip brew update on macOS
run: |
# 避免每次更新 Homebrew 索引,节省 3-5 分钟
mkdir -p $HOME/.cache/homebrew
touch $HOME/.cache/homebrew/initial_install_complete
if: matrix.os == 'macos-14'
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: requirements.txt
if: matrix.os != 'macos-14'
对于 macOS,直接用 setup-python 的内置 pip 缓存,省去 actions/cache 的配置复杂度,命中率反而更高。
五、问题 4:删除某个矩阵维度后 CI 更慢了
删掉了 ["3.9", "3.10"] 两个旧版本,job 数量从 12 个减少到 6 个,但总耗时反而增加了 40%。
根因是 GitHub Actions 的免费套餐对并发 job 有队列限制(上限 20 个并行),job 数量减少后没有更快入队,反而因为剩余 job 独占 runner 资源导致调度延迟。
# 查看当前并发状态
# Settings → Actions → Parallelism 确认队列配置
# 如果要控制总并发数,用 concurrency 组
jobs:
test:
runs-on: ${{ matrix.os }}
concurrency:
group: ${{ github.workflow }}-${{ matrix.os }}-${{ matrix.python-version }}
cancel-in-progress: true
用 concurrency 限制最大并发 job 数,比单纯删矩阵维度更可控。
六、问题 5:Windows 路径分隔符导致 shell 脚本失效
steps:
- name: Run install script
run: ./scripts/install.sh
shell: bash
# ❌ Windows 上 bash 不是默认 shell,需要手动指定
# 默认是 PowerShell,./ 路径在 PowerShell 里行为不同
Windows runner 的默认 shell 是 PowerShell 或 cmd,跨平台脚本必须显式指定:
steps:
- name: Run install script
run: ./scripts/install.sh
shell: bash
# ✅ 所有平台统一用 bash,避免路径问题
实战经验:Windows 上跑 Python 项目,路径最好用 pathlib 或 os.path.join,而不是硬编码 /。
七、完整示例:Python 多版本测试工作流
name: Python Matrix Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, macos-14, windows-2022]
python-version: ["3.9", "3.10", "3.11", "3.12"]
exclude:
# Python 3.9 不在 macOS ARM 上测试(无此版本)
- os: macos-14
python-version: "3.9"
env:
# job 级别环境变量,矩阵内所有 job 都能读到
PYTHONUNBUFFERED: "1"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: requirements.txt
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run tests
run: pytest tests/ -v --cov=src
- name: Upload coverage
uses: codecov/codecov-action@v4
if: matrix.os == 'ubuntu-22.04' # 只在 Linux 上传一次
with:
file: ./coverage.xml
exclude 用来跳过不兼容的版本组合,比在 step 里加 if 判断更干净。
现在你可以做什么
第一步:打开你的 .github/workflows/*.yml,检查 env 是不是写在 job 级别而不是全局级别。
第二步:在 setup-python 的 cache 配置里加入 python-version 到 key,确保不同版本缓存独立:key: pip-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}
第三步:给 Windows 平台的 steps 加上 shell: bash,避免跨平台脚本失效。
第四步:如果 job 总数超过 20 个,用 concurrency 控制最大并发数,而不是直接删矩阵维度。
更多交流点击入群






