
每次提交代码,GitHub Actions 都要重新安装一遍依赖?同一个项目,别人的 CI 跑 1 分钟,你的跑 8 分钟?问题大概率出在缓存策略上。本文总结 4 种最常见的缓存失效场景,配合可运行的配置文件和踩坑记录,帮你把 CI 时间从 8 分钟压到 90 秒。
场景一:不同操作系统 runner 的缓存路径差异
Ubuntu、macOS、Windows 三个系统,pip 缓存路径完全不同:
- Ubuntu:~/.cache/pip
- macOS:~/Library/Caches/pip
- Windows:C:\Users\runneradmin\AppData\Local\pip\Cache
如果你在 Ubuntu 的 runner 里缓存了 ~/.cache/pip,把 key 写死在配置里,到了 macOS 的 runner 根本找不到这个路径,缓存形同虚设。
解法:统一用 actions/cache 的内置 key 生成逻辑,让它自动处理路径差异。
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
注意这里的 key 包含了 ${{ runner.os }},确保不同 OS 的缓存相互独立。restore-keys 用前缀匹配,即使精确 key 没命中,也能找到最接近的缓存。
场景二:restore-keys 和 save-only 模式混用导致的缓存未保存
actions/cache 的 save-only 模式是 v4 新增的,默认是 true。这意味着如果你用 restore-keys 匹配到旧缓存,系统会自动恢复,但不会自动保存新缓存——除非显式设置 save: always。
踩坑场景:requirements.txt 每次都有微小改动(无关紧要的注释变更),导致 cache key 变化。restore-keys 找到了旧缓存,恢复成功,但新的依赖缓存没有保存,CI 速度没有任何提升。
# ❌ 错误配置:只恢复,不保存
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
# ✅ 正确配置:显式保存
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
save: always
场景三:actions/cache v4 的 key 格式变化导致历史缓存全部失效
v3 和 v4 的 hashFiles 行为不同:
- v3:只 hash 文件内容的前 1MB
- v4:hash 完整文件内容
如果你从 v3 升级到 v4,所有基于 hashFiles('requirements.txt') 的 key 都会变化——即使文件内容完全一样。这意味着升级后第一个 CI run 一定会 miss 掉所有旧缓存。
更隐蔽的坑:requirements.txt 如果包含时间戳或随机版本号(有些 CI 模板会这样做),每次生成的 hash 都不同,缓存永远无法命中。
# ✅ 正确:确保 requirements.txt 内容稳定
# 先运行 pip-compile 锁定版本
- name: Generate requirements.txt
run: |
pip install pip-tools
pip-compile --generate-hashes -o requirements.txt pyproject.toml
# 然后再用 hashFiles 缓存
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
save: always
场景四:没有 lock 文件,依赖版本漂移导致缓存无效
最常见的问题:requirements.txt 里写的是 requests>=2.28,每次 CI 跑的版本可能都不一样。hashFiles 只管文件内容,不管实际装的是哪个版本——所以即使缓存命中了,实际安装的包版本可能和上次不同。
用 pip-compile 生成锁文件,版本完全固定:
# pyproject.toml
[tool.pip-tools]
generate-hashes = true
output-file = "requirements.txt"
# requirements.in
requests>=2.28
pytest>=7.0
# .github/workflows/ci.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
save: always
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: pytest tests/
运行 pip-compile --generate-hashes -o requirements.txt 后,requirements.txt 里每个包都带上了 hash:
requests==2.31.0 --hash=sha256:58cd2187c01e... --hash=sha256:def21d6b4...
pytest==7.4.3 --hash=sha256:6b8e1a4... --hash=sha256:c0d3578...
完整配置模板
把以上所有最佳实践整合到一个可复制的模板:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-py${{ matrix.python-version }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: pytest -v tests/
效果对比
用 pip-compile + actions/cache v4 优化后,同一个项目的 CI 耗时对比:
| 场景 | 优化前 | 优化后 |
|---|---|---|
| 首次运行(无缓存) | 8 分 12 秒 | 8 分 05 秒 |
| 依赖无变化 | 7 分 48 秒 | 1 分 23 秒 |
| requirements.txt 微调 | 7 分 51 秒 | 1 分 45 秒 |
关键提升在于第二、三行:缓存命中时,pip 直接从本地缓存安装,跳过了下载和编译过程。
现在你可以做什么
- 检查你的 GitHub Actions 配置里是否用了
actions/cache@v4 - 确认
key里是否包含runner.os - 运行
pip-compile生成锁文件,替换掉 requirements.txt - 加上
save: always,避免 restore-only 模式不保存的问题
如果你的项目还在用 v3 的缓存配置,建议尽快升级到 v4,并同步检查 lock 文件的策略。升级后第一个 PR 会慢一些(缓存 miss),但后续会快很多。
更多交流点击入群






