Appearance
GitHub Actions缓存策略
缓存是提高GitHub Actions工作流执行效率的关键技术。通过缓存依赖项、构建产物和其他文件,可以显著减少工作流的执行时间,节省计算资源。
缓存基础概念
什么是缓存
GitHub Actions缓存允许您在作业之间存储和重用文件。当缓存命中时,可以避免重新下载依赖项或重新构建资源,从而加快工作流执行速度。
yaml
# .github/workflows/caching-example.yml
name: Caching Example
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
run: npm ci # 由于缓存,这将很快执行
- name: Build project
run: npm run build
缓存组件
缓存主要由三个组件组成:
- 路径(Path):要缓存的文件或目录
- 键(Key):缓存的唯一标识符
- 恢复键(Restore keys):用于恢复相似缓存的键
actions/cache基本用法
简单缓存示例
yaml
jobs:
cache-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache dependencies
id: cache-dependencies
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies if cache miss
if: steps.cache-dependencies.outputs.cache-hit != 'true'
run: npm ci
- name: Run tests
run: npm test
多路径缓存
yaml
jobs:
multi-path-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache multiple paths
uses: actions/cache@v4
with:
path: |
~/.npm
~/.cache/Cypress
./dist
key: ${{ runner.os }}-multi-${{ hashFiles('**/package-lock.json') }}
- name: Use cached files
run: |
echo "NPM cache, Cypress cache, and dist directory are cached"
ls -la ~/.npm
ls -la ~/.cache/Cypress
ls -la ./dist
缓存键策略
基于文件哈希的键
yaml
jobs:
file-hash-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache with file hash
uses: actions/cache@v4
with:
path: node_modules
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
- name: Install dependencies
run: npm ci
复合键策略
yaml
jobs:
composite-key:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Cache node modules per OS and Node version
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-${{ matrix.node-version }}-
${{ runner.os }}-node-
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm' # 使用GitHub内置缓存
- name: Install and test
run: |
npm ci
npm test
语言特定缓存
Node.js缓存
yaml
jobs:
nodejs-caching:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 方法1: 使用actions/cache
- name: Cache node modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# 方法2: 使用actions/setup-node内置缓存
- name: Setup Node.js with cache
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm' # 或 'yarn' 或 'pnpm'
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Build and test
run: |
npm run build
npm test
Python缓存
yaml
jobs:
python-caching:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 使用actions/setup-python内置缓存
- name: Setup Python with cache
uses: actions/setup-python@v4
with:
python-version: '3.11'
cache: 'pip' # 缓存pip安装的包
cache-dependency-path: requirements.txt
# 或者使用actions/cache手动缓存
- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: python -m pytest
Java/Maven缓存
yaml
jobs:
java-caching:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 使用actions/setup-java内置缓存
- name: Setup Java with cache
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'maven' # 或 'gradle'
cache-dependency-path: pom.xml
# 或者手动缓存Maven仓库
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2-
- name: Build with Maven
run: mvn clean install
Go缓存
yaml
jobs:
go-caching:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 缓存Go模块
- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: true # 启用内置缓存
- name: Download dependencies
run: go mod download
- name: Build and test
run: |
go build ./...
go test ./...
高级缓存策略
条件缓存
yaml
jobs:
conditional-caching:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Determine if cache should be used
id: cache-check
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
echo "cache-key=${{ runner.os }}-pr-${{ hashFiles('**/package-lock.json') }}" >> $GITHUB_OUTPUT
else
echo "cache-key=${{ runner.os }}-main-${{ hashFiles('**/package-lock.json') }}" >> $GITHUB_OUTPUT
fi
- name: Cache dependencies
uses: actions/cache@v4
with:
path: node_modules
key: ${{ steps.cache-check.outputs.cache-key }}
restore-keys: |
${{ runner.os }}-main-
${{ runner.os }}-
- name: Install dependencies
run: npm ci
分层缓存
yaml
jobs:
layered-caching:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 缓存基础依赖
- name: Cache base dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-base-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-base-
# 缓存构建产物
- name: Cache build output
uses: actions/cache@v4
with:
path: dist
key: ${{ runner.os }}-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-${{ github.ref }}-
${{ runner.os }}-build-
- name: Install and build
run: |
npm ci
npm run build
缓存验证
yaml
jobs:
validated-caching:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache with validation
uses: actions/cache@v4
id: node-cache
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Validate cache integrity
if: steps.node-cache.outputs.cache-hit == 'true'
run: |
echo "Validating cached node_modules..."
if [ ! -f "node_modules/.package-lock.json" ]; then
echo "Cache appears corrupted, removing..."
rm -rf node_modules
else
echo "Cache validation passed"
fi
- name: Install dependencies if needed
if: steps.node-cache.outputs.cache-hit != 'true' || env.CACHE_CORRUPTED == 'true'
run: npm ci
- name: Run build
run: npm run build
构建产物缓存
缓存编译结果
yaml
jobs:
build-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 缓存编译产物
- name: Cache build artifacts
uses: actions/cache@v4
with:
path: |
build/
dist/
target/
.next/cache/
key: ${{ runner.os }}-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-${{ github.ref }}-
${{ runner.os }}-build-
- name: Build application
run: |
if [ -f "package.json" ]; then
npm run build
elif [ -f "pom.xml" ]; then
mvn package
fi
Docker层缓存
yaml
jobs:
docker-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: false
tags: myapp:${{ github.sha }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# 移动缓存文件以供下次使用
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
缓存监控和管理
缓存统计
yaml
jobs:
cache-stats:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache with statistics
uses: actions/cache@v4
id: cache
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- name: Print cache statistics
run: |
if [ "${{ steps.cache.outputs.cache-hit }}" == "true" ]; then
echo "Cache hit! Saved time on dependency installation."
# 可以在这里添加更多统计信息
else
echo "Cache miss. Dependencies need to be installed."
fi
# 检查缓存大小
if [ -d "node_modules" ]; then
du -sh node_modules
fi
缓存清理策略
yaml
jobs:
cleanup-cache:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action == 'closed'
steps:
- name: Cleanup PR cache
run: |
# 在PR关闭时清理相关缓存
# 注意:actions/cache不提供直接的删除接口
# 需要通过改变缓存键来间接清理
echo "Cleaning up cache for closed PR"
多作业缓存共享
作业间缓存共享
yaml
jobs:
setup:
runs-on: ubuntu-latest
outputs:
cache-key: ${{ steps.cache.outputs.cache-key }}
steps:
- uses: actions/checkout@v4
- name: Prepare cache
id: cache
run: |
CACHE_KEY="${{ runner.os }}-shared-${{ hashFiles('**/package-lock.json') }}"
echo "cache-key=$CACHE_KEY" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v4
with:
path: node_modules
key: ${{ steps.cache.outputs.cache-key }}
- name: Install dependencies
run: npm ci
build:
needs: setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Restore shared cache
uses: actions/cache@v4
with:
path: node_modules
key: ${{ needs.setup.outputs.cache-key }}
- name: Build project
run: npm run build
test:
needs: setup
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Restore shared cache
uses: actions/cache@v4
with:
path: node_modules
key: ${{ needs.setup.outputs.cache-key }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Run tests
run: npm test
缓存最佳实践
1. 合理选择缓存路径
yaml
# 好的做法:缓存正确的目录
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.npm
~/.cache # 但要确保只包含需要的子目录
key: ${{ runner.os }}-deps-${{ hashFiles('**/package-lock.json') }}
# 避免:缓存不必要的大目录
# path: ~/
2. 优化缓存键
yaml
# 好的做法:使用精确的文件哈希
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
# 更好:包含更多上下文
key: ${{ runner.os }}-node${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
3. 使用恢复键
yaml
- name: Cache with recovery
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node- # 回退到相同OS但不同依赖的缓存
${{ runner.os }}- # 回退到任何相同OS的缓存
node- # 回退到任何node缓存
4. 缓存大小管理
yaml
# 对于大型缓存,考虑分片
- name: Cache large assets
uses: actions/cache@v4
with:
path: |
large-assets-part1/
large-assets-part2/
key: ${{ runner.os }}-assets-${{ hashFiles('**/asset-manifest.json') }}
实际应用示例
完整的Node.js项目缓存策略
yaml
name: Node.js CI with Caching
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Cache node modules
id: npm-cache
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-${{ matrix.node-version }}-
${{ runner.os }}-node-
${{ runner.os }}-
- name: Install dependencies
if: steps.npm-cache.outputs.cache-hit != 'true'
run: npm ci
- name: Run linting
run: npm run lint
- name: Run tests
run: npm test -- --coverage
- name: Cache test results
uses: actions/cache@v4
with:
path: coverage/
key: ${{ runner.os }}-coverage-${{ github.sha }}
复杂项目的分层缓存
yaml
name: Complex Project with Layered Caching
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 缓存工具安装
- name: Cache tools
uses: actions/cache@v4
with:
path: ~/.local/bin
key: ${{ runner.os }}-tools-${{ hashFiles('.tool-versions') }}
# 缓存依赖项
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
node_modules
~/.m2/repository
~/.cache/pip
key: ${{ runner.os }}-deps-${{ hashFiles('**/package-lock.json', '**/pom.xml', '**/requirements.txt') }}
# 缓存构建产物
- name: Cache build artifacts
uses: actions/cache@v4
with:
path: |
dist/
target/
build/
key: ${{ runner.os }}-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-${{ github.ref }}-
# 缓存测试结果
- name: Cache test results
uses: actions/cache@v4
with:
path: test-results/
key: ${{ runner.os }}-test-results-${{ github.sha }}
- name: Install tools
run: |
# 安装项目特定工具
echo "Installing tools..."
- name: Install dependencies
run: |
npm ci
pip install -r requirements.txt
mvn dependency:resolve
- name: Build project
run: |
npm run build
mvn package
- name: Run tests
run: |
npm run test
mvn test
通过合理使用缓存策略,可以显著提高GitHub Actions工作流的执行效率,减少重复的下载和构建操作,从而节省时间和资源。