
解决什么问题
每天凌晨自动跑「Python 3.10 / 3.11 / 3.12 × Ubuntu / Windows / macOS」共 9 个组合的测试,或者每周一检查依赖有没有安全更新。GitHub Actions schedule 触发器配合矩阵策略,可以用最少的 YAML 实现这些定时任务。但 cron 表达式时区搞错、矩阵组合耗尽配额、依赖版本飘移导致漏报——这三个坑我全踩过,以下是完整排坑方案。
基础配置:cron + 矩阵的最简写法
下面是一个每天 UTC 14:00(北京时间 22:00)自动运行的测试矩阵:
name: Scheduled Multi-Version Test
on:
schedule:
- cron: '0 14 * * *' # 每天 UTC 14:00 = 北京时间 22:00
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['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/
9 个组合,每个组合独立运行,互不影响。GitHub Actions 会自动按字母序分配配额——免费账户每月 2000 分钟,超出后 job 会排队等待,不会直接失败。
踩坑一:cron 时区(最容易出错的地方)
schedule 的 cron 表达式 全部在 UTC 时间运行,不是本地时间。免费账户很难意识到这一点。
我第一次配置时,想设「北京时间每天 22:00 跑」,直接写:
# 错误!这是 UTC 22:00 = 北京时间次日 06:00
- cron: '0 22 * * *'
结果跑出来发现是凌晨 6 点,不是晚上 10 点。正确的换算:
- 北京时间 22:00 = UTC 14:00(UTC+8)
- 北京时间 14:00(下午)= UTC 06:00
- 不要凭感觉写,用
date -u先验证
# 验证当前 UTC 时间
$ date -u
Sun May 24 06:02:25 UTC 2026
# 验证对应北京时间
$ TZ='Asia/Shanghai' date
Sun May 24 14:02:25 CST 2026
踩坑二:每周只在周一跑,防止配额浪费
如果你的测试不需要每天跑(比如依赖安全检查),可以限定只在周一执行,减少配额消耗:
on:
schedule:
# 每周一 UTC 08:00 = 北京时间 16:00
# 表达式:分(0) 时(8) 日(*) 月(*) 周一(1)
- cron: '0 8 * * 1'
如果矩阵组合超过 10 个,建议加上 fail-fast: false,防止一个组合失败时 GitHub 取消其他正在运行的 job:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.10', '3.11', '3.12']
fail-fast: false # 一个失败不取消其他
踩坑三:依赖更新漏报——用 pip-compile 生成锁定版本
定时任务跑的是当前环境的最新依赖,但如果 requirements.txt 没有锁定版本,测试结果不稳定(今天 pass、明天 fail),无法定位是代码问题还是依赖问题。
正确做法是用 pip-compile 生成锁文件:
# requirements.in(只写包名,不写版本)
requests>=2.28
pytest>=7.0
httpx>=0.24
# 生成 requirements.txt(锁定版本)
$ pip install pip-tools
$ pip-compile requirements.in --generate-hashes
# requirements.txt 会被锁定到精确版本
# requests==2.31.0 --hash=sha256:...
# pytest==7.4.0 --hash=sha256:...
# httpx==0.25.0 --hash=sha256:...
在 CI 中验证锁文件和实际安装的版本一致:
- name: Verify pinned dependencies
run: |
pip install -r requirements.txt
# 导出实际安装的版本,对比 lock 文件
pip freeze | diff - requirements.txt || exit 1
踩坑四:矩阵组合过多导致配额提前耗尽
3 个 OS × 3 个 Python 版本 = 9 个组合,如果每个跑 5 分钟,就是 45 分钟。如果每天跑,一个月下来接近 1350 分钟——接近免费账户 2000 分钟的配额上限。
一个实际解决思路:不要测所有组合,用「锚点组合」覆盖主要风险:
# 只测关键组合,不是全矩阵
# 生产环境用 Ubuntu + 最新 Python,用这个做冒烟测试
# 其他 OS/Python 版本只在 release tag 时跑完整矩阵
strategy:
matrix:
include:
# 冒烟测试(每天跑)
- os: ubuntu-latest
python-version: '3.12'
# 回归测试(release 时跑)
- os: ubuntu-latest
python-version: ['3.10', '3.11', '3.12']
- os: windows-latest
python-version: ['3.11', '3.12']
- os: macos-latest
python-version: ['3.11', '3.12']
用 on.schedule 配合 on.push tags 做双触发:
on:
schedule:
- cron: '0 8 * * 1' # 每周一 UTC 8:00 冒烟测试
push:
tags:
- 'v*' # release tag 时跑完整测试
踩坑五:Windows 路径中的换行符导致缓存失效
在矩阵策略下使用 actions/cache 时,Windows runner 的路径分隔符是 \,Unix 是 /。如果缓存 key 里直接写 ~ 路径,不同 OS 的 key 不匹配:
# 错误:Windows 和 Unix 的缓存路径不同,无法复用
- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/Library/Caches/pip # macOS
# Windows: ~$LOCALAPPDATA$\pip\Cache
# Linux: ~/.cache/pip
key: pip-${{ matrix.os }}-${{ hashFiles('requirements.txt') }}
正确做法:用 runner.os 区分不同 OS 的缓存路径:
- name: Cache pip packages
uses: actions/cache@v4
with:
path: |
~/.cache/pip
~/Library/Caches/pip
~\AppData\Local\pip\Cache
key: |
pip-${{ runner.os }}-${{ hashFiles('requirements.txt') }}
restore-keys: |
pip-${{ runner.os }}-
现在你可以做什么
第一步:在你的项目里新建 .github/workflows/scheduled-test.yml,把上面的冒烟测试矩阵复制进去;
第二步:执行 pip-compile requirements.in --generate-hashes 生成锁文件,提交到仓库;
第三步:把 cron 表达式从 '0 8 * * 1' 改成你需要的北京时间,用 date -u 验证 UTC 时间是否对应正确;
第四步:如果 release 时需要跑完整矩阵,在 on.push.tags 里加上 paths: ['requirements.txt', 'requirements.in'],只在依赖文件变更时触发。
更多交流点击入群






