Skip to content
On this page

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

缓存组件

缓存主要由三个组件组成:

  1. 路径(Path):要缓存的文件或目录
  2. 键(Key):缓存的唯一标识符
  3. 恢复键(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工作流的执行效率,减少重复的下载和构建操作,从而节省时间和资源。