
类型:代码托管平台
简介:只支持Git作为唯一的版本库格式进行托管,故名GitHub。
GitHub Actions 跑测试或构建时,最耗时间的环节往往不是脚本本身,而是反复下载依赖。Node 项目每次执行 npm ci,Python 项目每次安装 requirements.txt,如果没有缓存,哪怕代码只改了一行,也可能重新拉取大量包。GitHub Actions 缓存的价值,就是把依赖下载结果保存下来,在下一次工作流运行时复用,从而缩短 CI 等待时间。
一、先判断慢在哪里
给 Actions 加缓存前,先打开最近几次运行日志,看耗时集中在哪些步骤。如果 checkout 只花几秒,而安装依赖花了几分钟,缓存才有明显意义。若真正慢的是单元测试、Docker 镜像构建或外部接口等待,缓存依赖只能改善一部分体验。
Node 项目通常关注 node_modules 或包管理器缓存目录,Python 项目常见目标是 pip 下载缓存。不要一上来缓存整个工作目录,那样容易把构建产物、临时文件和不该复用的内容一起保存,反而造成难排查的问题。
二、缓存key要跟依赖文件绑定
缓存是否命中,关键看 key。一个实用原则是:依赖文件变化时缓存应自动失效,代码文件变化但依赖没变时缓存继续复用。Node 项目可以把 package-lock.json、pnpm-lock.yaml 或 yarn.lock 纳入 hash;Python 项目可以把 requirements.txt 或 poetry.lock 纳入 hash。
示例写法可以这样理解:
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
这个 key 同时包含运行系统和锁文件哈希。锁文件不变,缓存就有机会命中;锁文件变化,说明依赖版本变化,应重新生成缓存。
三、Node项目的常见写法
Node 项目如果使用 actions/setup-node,可以直接启用内置缓存。例如 npm 项目配置 cache: npm,setup-node 会缓存 npm 的全局下载缓存,而不是简单保存 node_modules。这样更稳定,也不容易受平台差异影响。
如果仓库是 monorepo,要确认 lock 文件位置。前端在 frontend 目录,就需要设置 cache-dependency-path 指向 frontend/package-lock.json。很多缓存不命中问题,都是因为工作目录和锁文件路径没有对应起来。
四、Python项目要区分下载缓存和虚拟环境
Python 项目可以缓存 pip 下载目录,但不建议盲目缓存整个虚拟环境。虚拟环境和 Python 小版本、系统路径、平台依赖关系很强,跨 runner 复用时更容易出问题。更稳妥的做法是使用 setup-python 配合 pip cache,或者缓存 ~/.cache/pip。
如果项目使用 Poetry 或 uv,也要按照对应工具的缓存目录配置。不同工具缓存位置不同,不要把 npm 的经验直接套到 Python 上。
五、restore-keys适合兜底但不能滥用
restore-keys 可以在完整 key 未命中时尝试使用相近缓存。例如依赖锁文件变化后,仍可复用同系统下旧缓存的一部分内容。但 restore-keys 过于宽泛时,可能拿到很旧的缓存,导致安装过程出现奇怪问题。
推荐只设置一到两级兜底,例如系统级前缀,不要让不同语言、不同项目目录共享同一个缓存前缀。缓存命中率和可预测性之间要平衡。
六、缓存不是越大越好
GitHub 对缓存大小和保留有配额限制。把 node_modules、构建目录、测试报告和临时文件全部缓存,会很快占满空间。缓存过大还会增加压缩和上传时间,可能抵消加速收益。
判断缓存是否值得保留,可以看两项:下载依赖节省的时间,是否明显大于缓存上传下载时间;缓存是否稳定命中,而不是频繁新建。若一个缓存每次都重新生成,说明 key 设计或依赖文件选择需要调整。
七、缓存失效怎么排查
如果日志里显示 Cache not found,先检查 key 是否每次都变化。常见原因包括 hashFiles 路径写错、lock 文件每次被脚本改动、不同分支使用不同依赖文件。若显示命中缓存但安装仍然很慢,要看缓存目录是否选错,或者包管理器本身仍在重新解析依赖。
对于 pnpm、yarn、Poetry 这类工具,优先查看官方推荐缓存目录。不要只凭目录名猜测。配置好后,连续跑两次同一分支的 workflow,第二次应明显更快,这才说明缓存发挥了作用。
八、适合站长项目的落地方式
如果仓库只是一个静态站点或文档项目,优先缓存包管理器依赖即可;如果还包含图片处理、搜索索引生成、Playwright 测试等耗时步骤,再考虑针对性缓存浏览器二进制或构建中间产物。每增加一个缓存,都要能说明它解决了哪段耗时。
GitHub Actions 缓存的核心不是堆配置,而是让 CI 更可预期。先定位慢步骤,再选择缓存目录,最后用日志确认命中情况,这样比复制一段复杂 YAML 更可靠。
九、不同包管理器的缓存思路不同
npm、pnpm、yarn 虽然都用于 Node 项目,但缓存目录和安装策略并不完全一样。npm 更常见的是缓存下载包,pnpm 依赖全局 store,yarn 也有自己的缓存机制。如果项目使用 pnpm,却只照搬 npm 的缓存写法,可能看似配置成功,实际命中效果很弱。配置前应先确认 lock 文件和包管理器命令,例如 npm ci、pnpm install –frozen-lockfile 或 yarn install –immutable。
在 monorepo 中,还要注意多个子项目是否共享同一个 lock 文件。根目录一个 pnpm-lock.yaml 通常可以服务多个包;如果每个子目录都有独立 lock 文件,缓存 key 就要分别设计。缓存策略越贴近项目真实依赖结构,越不容易出现“日志显示命中,但速度没有变化”的情况。
十、缓存与安全更新的关系
依赖缓存不会阻止安全更新,但可能让维护者误以为依赖一直是最新的。真正决定依赖版本的是 lock 文件和安装命令,缓存只是复用下载结果。Dependabot 或人工更新 lock 文件后,hash 改变,缓存会重新生成。
如果出现安全漏洞修复后 CI 仍使用旧依赖,先检查是否提交了新的 lock 文件,再看安装命令是否强制使用锁文件。不要通过手动删除缓存解决所有问题,根因往往在依赖文件没有更新或分支没有同步。
十一、一个更稳的调试顺序
调试缓存时,可以先固定在同一分支连续运行两次 workflow。第一次没有缓存很正常,第二次仍未命中才需要排查。接着检查 key 是否一致、hashFiles 是否找到文件、缓存目录是否存在、安装步骤是否真的读取了该目录。最后再考虑配额或缓存过期。
这样按顺序排查,能避免一边改 key、一边改安装命令,最后不知道是哪一步产生效果。对站长工具和静态站项目来说,缓存配置一旦稳定,后续维护成本很低,真正需要关注的是依赖文件变化和构建日志异常。

