缓存(Cache)原理
为什么需要缓存?
每次 GitHub Actions Workflow 运行时,都是在全新的虚拟机上执行。这意味着每次都要重新下载 npm 包、pip 包、Maven 依赖等——这可能占据整个构建时间的 60-80%。通过缓存,可以将依赖下载从每次 2-5 分钟压缩到秒级。
缓存的工作流程
理解 actions/cache 的内部机制,有助于设计更好的缓存策略:
缓存查找(Restore)
在步骤执行前,GitHub 根据 key 精确匹配缓存。精确命中时直接恢复;未命中时按 restore-keys 中的前缀依次查找最近的匹配缓存(降级恢复)。降级恢复可以复用大部分文件,只需增量下载新依赖。
缓存保存(Save)
Job 结束时(成功情况下),actions/cache 会将 path 中的文件压缩并上传到 GitHub 缓存存储(每个仓库 10GB 限制)。如果 key 与已有缓存完全相同,则不会重复保存(避免重复占用配额)。
缓存范围
缓存默认仅在当前分支和默认分支(main/master)之间共享。Pull Request 可以读取 base 分支的缓存,但其新建的缓存对 base 不可见。这防止了 PR 对主分支缓存的污染。
缓存过期
缓存在 7 天内未被访问会自动删除。超过 10GB 上限时,GitHub 按 LRU 策略删除最旧的缓存。可在仓库 Actions → Caches 页面手动管理缓存。
Cache Key 设计原则
Cache Key 是决定是否命中缓存的关键。好的 Key 设计要在缓存命中率和缓存新鲜度之间取得平衡:
- uses: actions/cache@v4
with:
path: ~/.npm
# 完美 Key:OS + 锁文件哈希
# 锁文件变化 = 依赖变化 = 缓存失效(重新下载)
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
# restore-keys:降级匹配,即使不完全命中也能部分复用
restore-keys: |
${{ runner.os }}-npm-
${{ runner.os }}-
不同语言的缓存配置
# Node.js(npm)
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: ${{ runner.os }}-npm-
# Node.js(yarn)
- uses: actions/cache@v4
with:
path: ~/.yarn/cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
# Python(pip)
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
# Rust(Cargo)
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Go
- uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
# Maven(Java)
- uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
使用 setup-* Actions 的内置缓存
许多官方 setup Action(setup-node、setup-python、setup-go 等)都内置了缓存支持,只需添加 cache: 'npm' 参数即可,比手动配置 actions/cache 更简单。推荐优先使用内置缓存。
Artifacts 管理
Cache vs Artifact 的区别
Cache(缓存)
用于跨 Workflow 运行持久化中间产物(如依赖包)以加速构建。跨运行复用,保留时间较长(默认 7 天未使用后删除),大小限制 10GB。
Artifact(产物)
用于在同一 Workflow 运行内的不同 Job 之间传递文件,或供用户下载(如编译好的二进制、测试报告)。默认保留 90 天,大小限制 500MB(每次上传)。
在 Jobs 间传递构建产物
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
# 上传构建产物
- uses: actions/upload-artifact@v4
with:
name: dist-files # 产物名称
path: dist/ # 上传的路径
retention-days: 5 # 保留天数(1-90)
compression-level: 9 # 压缩级别(0-9)
if-no-files-found: error # warn/error/ignore
test-e2e:
needs: build
runs-on: ubuntu-latest
steps:
# 下载上一个 Job 的构建产物
- uses: actions/download-artifact@v4
with:
name: dist-files
path: ./dist # 下载到指定目录
- run: npx serve dist &
- run: npx playwright test
release:
needs: [build, test-e2e]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: dist-files
- name: 创建 Release 并上传产物
uses: softprops/action-gh-release@v2
with:
files: dist/*.tar.gz
上传测试报告
- name: 运行测试(即使失败也继续)
run: npm test -- --reporter=junit
continue-on-error: true
- name: 上传测试报告
uses: actions/upload-artifact@v4
if: always() # 无论成功失败都上传
with:
name: test-reports-${{ matrix.node }}
path: |
test-results/
coverage/
retention-days: 30
缓存优化实战:Node.js 项目完整示例
# 完整的 Node.js 项目缓存优化配置
name: CI with Cache
on: [push, pull_request]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 方式1:使用 setup-node 的内置缓存(推荐,更简洁)
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # 自动缓存 ~/.npm,key 基于 package-lock.json
# 方式2:手动配置 actions/cache(更灵活,可控制更多参数)
# - uses: actions/cache@v4
# with:
# path: ~/.npm
# key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
# restore-keys: ${{ runner.os }}-npm-
# ci 安装(比 install 更严格,保证 lock 文件一致性)
- run: npm ci
# 缓存 Next.js 构建缓存(下次构建只重新编译修改的部分)
- uses: actions/cache@v4
with:
path: .next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.{js,jsx,ts,tsx}') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
${{ runner.os }}-nextjs-
- run: npm run build
- run: npm test
# 上传覆盖率报告
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage/
retention-days: 14
多平台构建产物收集
# 并行构建多平台,收集所有产物发布
name: Multi-platform Release
jobs:
build:
# 矩阵构建:同时在三个平台构建
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build:desktop
# 每个平台上传各自的产物(名称包含 OS 信息)
- uses: actions/upload-artifact@v4
with:
name: app-${{ matrix.os }} # ubuntu-latest, windows-latest, macos-latest
path: |
dist/*.exe # Windows
dist/*.dmg # macOS
dist/*.AppImage # Linux
if-no-files-found: ignore # 跨平台文件不存在时忽略而非报错
# 收集所有平台的产物并发布
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write # 创建 Release 需要写权限
steps:
# 下载所有平台的产物(不指定 name 则下载全部)
- uses: actions/download-artifact@v4
with:
path: artifacts/ # 下载到子目录,按 artifact 名自动分目录
- name: 列出所有产物
run: ls -la artifacts/
# 创建 GitHub Release 并上传所有文件
- uses: softprops/action-gh-release@v2
with:
files: artifacts/**/*
缓存安全注意事项
缓存中不应存放敏感信息(密钥、凭证)。虽然缓存对仓库成员可见,但不会暴露给公开用户。Fork PR 的工作流程可以恢复父仓库的缓存(只读),但无法创建或覆盖父仓库的缓存,这防止了缓存投毒攻击。
本章小结
本章核心要点
- 缓存原理:GitHub Actions 每次在全新 VM 上运行,缓存将依赖下载时间从分钟压缩到秒级;缓存作用域限于当前分支和默认分支之间,防止 PR 污染主干缓存。
- Cache Key 设计:好的 Key 包含 runner.os + 锁文件哈希(package-lock.json/yarn.lock/Cargo.lock);restore-keys 实现降级匹配,即使锁文件变化也能复用大部分缓存。
- 内置缓存优先:setup-node/setup-python/setup-go 等 Action 内置 cache 参数,比手动配置 actions/cache 更简单;手动配置适合需要缓存自定义路径(如 .next/cache)的场景。
- Cache vs Artifact:Cache 跨 Workflow 运行复用依赖,最长保留 7 天未访问删除;Artifact 在同一 Workflow 内 Job 间传递文件,或供用户下载,最多保留 90 天。
- Artifact 的 if: always():对于测试报告等产物,应设置 if: always() 确保即使前面步骤失败也能上传,方便调试失败的构建。