跳至主要内容

缓存依赖项以加快工作流速度

为了使您的工作流程更快、更高效,您可以创建和使用缓存来存储依赖项和其他常用文件。

关于缓存工作流程依赖项

工作流程运行通常会重复使用来自一个运行到另一个运行的相同输出或下载的依赖项。例如,Maven、Gradle、npm 和 Yarn 等包和依赖项管理工具会保留下载依赖项的本地缓存。

GitHub 托管运行器上的作业从干净的运行器镜像开始,每次都必须下载依赖项,这会导致网络利用率增加、运行时间延长以及成本增加。为了帮助加快重新创建依赖项等文件所需的时间,GitHub 可以缓存您在工作流程中经常使用的文件。

要为作业缓存依赖项,您可以使用 GitHub 的 cache 操作。该操作会创建和恢复由唯一键标识的缓存。或者,如果您正在缓存下面列出的包管理器,使用它们各自的 setup-* 操作需要最少的配置,并将为您创建和恢复依赖项缓存。

包管理器用于缓存的 setup-* 操作
npm、Yarn、pnpmsetup-node
pip、pipenv、Poetrysetup-python
Gradle、Mavensetup-java
RubyGemssetup-ruby
Go go.sumsetup-go
.NET NuGetsetup-dotnet

警告:在使用 GitHub Actions 缓存时,请注意以下事项

  • 我们建议您不要在缓存中存储任何敏感信息。例如,敏感信息可能包括存储在缓存路径中文件的访问令牌或登录凭据。此外,docker login 等命令行界面 (CLI) 程序可能会在配置文件中保存访问凭据。任何具有读取权限的人都可以对存储库创建拉取请求并访问缓存的内容。存储库的分支也可以对基本分支创建拉取请求并访问基本分支上的缓存。
  • 当使用自托管运行器时,工作流运行的缓存存储在 GitHub 拥有的云存储中。仅 GitHub Enterprise Server 提供客户拥有的存储解决方案。

比较工件和依赖项缓存

工件和缓存类似,因为它们都提供了在 GitHub 上存储文件的能力,但每个功能都提供不同的用例,不能互换使用。

  • 当您想要重用在作业或工作流运行之间不会经常更改的文件时,例如来自包管理系统的构建依赖项,请使用缓存。
  • 当您想要保存作业生成的要在工作流运行结束后查看的文件时,例如构建的二进制文件或构建日志,请使用工件。

有关工作流运行工件的更多信息,请参阅 "将工作流数据存储为工件."

访问缓存的限制

访问限制通过在不同分支或标签之间创建逻辑边界来提供缓存隔离和安全性。工作流运行可以恢复在当前分支或默认分支(通常为 main)中创建的缓存。如果为拉取请求触发工作流运行,它也可以恢复在基分支中创建的缓存,包括分叉存储库的基分支。例如,如果分支 feature-b 的基分支为 feature-a,则在拉取请求上触发的工作流运行将可以访问在默认 main 分支、基 feature-a 分支和当前 feature-b 分支中创建的缓存。

工作流运行无法恢复为子分支或兄弟分支创建的缓存。例如,为子分支 feature-b 创建的缓存将无法被在父分支 main 上触发的工作流运行访问。类似地,为基分支为 mainfeature-a 分支创建的缓存将无法被其兄弟分支 feature-c(基分支为 main)访问。工作流运行也不能恢复为不同标签名称创建的缓存。例如,为基分支为 main 的标签 release-a 创建的缓存将无法被为基分支为 main 的标签 release-b 触发的工作流运行访问。

当工作流程运行在拉取请求上触发并创建缓存时,缓存将为合并引用(refs/pull/.../merge)创建。因此,缓存将具有有限的范围,只能通过重新运行拉取请求来恢复。它不能通过基分支或针对该基分支的其他拉取请求来恢复。

存储库中的多个工作流程运行可以共享缓存。在工作流程运行中为分支创建的缓存可以从同一存储库和分支的另一个工作流程运行中访问和恢复。

使用 cache 操作

cache 操作将尝试根据您提供的 key 恢复缓存。当操作找到与键完全匹配的缓存时,操作会将缓存的文件恢复到您配置的 path。您可以选择提供 restore-keys 列表,以防 key 与现有缓存不匹配。当您从另一个分支恢复缓存时,restore-keys 列表很有用,因为 restore-keys 可以部分匹配缓存键。有关匹配 restore-keys 的更多信息,请参阅“匹配缓存键”。

如果与提供的 key 完全匹配,则认为是缓存命中。如果没有任何缓存与提供的 key 完全匹配,则认为是缓存未命中。在缓存未命中时,如果作业成功完成,操作会自动创建一个新的缓存。新缓存将使用您提供的 key,并包含您在 path 中指定的 文件。有关如何处理此问题的更多信息,请参阅“缓存命中和未命中”。

您无法更改现有缓存的内容。相反,您可以使用新键创建一个新的缓存。

cache 操作的输入参数

  • key: 必需 保存缓存时创建的键,以及用于搜索缓存的键。它可以是变量、上下文值、静态字符串和函数的任何组合。键的最大长度为 512 个字符,超过最大长度的键会导致操作失败。

  • path: 必需 运行器上要缓存或恢复的路径。

    • 您可以指定单个路径,也可以在单独的行上添加多个路径。例如

      - name: Cache Gradle packages
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
      
    • 您可以指定目录或单个文件,并且支持通配符模式。

    • 您可以指定绝对路径或相对于工作区目录的路径。

  • restore-keys: 可选 包含备用恢复密钥的字符串,每个恢复密钥都放在新的一行。如果 key 没有命中缓存,则会按提供的顺序依次使用这些恢复密钥来查找和恢复缓存。例如

    restore-keys: |
      npm-feature-${{ hashFiles('package-lock.json') }}
      npm-feature-
      npm-
    
  • enableCrossOsArchive: 可选 布尔值,启用后,允许 Windows 运行器保存或恢复与创建缓存的操作系统无关的缓存。如果未设置此参数,则默认为 false。有关更多信息,请参阅 Actions 缓存文档中的 跨操作系统缓存

cache 操作的输出参数

  • cache-hit: 布尔值,指示是否找到了与键完全匹配的缓存。

缓存命中和未命中

key 与现有缓存完全匹配时,称为缓存命中,操作会将缓存的文件恢复到 path 目录。

key 与现有缓存不匹配时,称为缓存未命中,如果作业成功完成,则会自动创建一个新的缓存。

当发生缓存未命中时,操作还会搜索您指定的 restore-keys 以查找任何匹配项

  1. 如果您提供 restore-keyscache 操作会依次搜索与 restore-keys 列表匹配的任何缓存。
    • 当存在完全匹配时,操作会将缓存中的文件恢复到 path 目录。
    • 如果没有完全匹配,操作会搜索恢复密钥的部分匹配。当操作找到部分匹配时,会将最新的缓存恢复到 path 目录。
  2. cache 操作完成,作业中的下一步运行。
  3. 如果作业成功完成,操作会自动使用 path 目录的内容创建一个新的缓存。

有关缓存匹配过程的更详细说明,请参阅“匹配缓存键”。

使用 cache 操作的示例

此示例在 `package-lock.json` 文件中的包发生更改或运行器的操作系统发生更改时创建新的缓存。缓存键使用上下文和表达式来生成一个包含运行器操作系统和 `package-lock.json` 文件的 SHA-256 哈希的键。

YAML
name: Caching with npm
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Cache node modules
        id: cache-npm
        uses: actions/cache@v3
        env:
          cache-name: cache-node-modules
        with:
          # npm cache files are stored in `~/.npm` on Linux/macOS
          path: ~/.npm
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-

      - if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }}
        name: List the state of node modules
        continue-on-error: true
        run: npm list

      - name: Install dependencies
        run: npm install

      - name: Build
        run: npm run build

      - name: Test
        run: npm test

使用上下文创建缓存键

缓存键可以包含 GitHub Actions 支持的任何上下文、函数、字面量和运算符。有关更多信息,请参阅“上下文”和“表达式”。

使用表达式创建 `key` 允许您在依赖项更改时自动创建新的缓存。

例如,您可以使用计算 npm `package-lock.json` 文件哈希的表达式创建 `key`。因此,当构成 `package-lock.json` 文件的依赖项发生更改时,缓存键会发生更改,并且会自动创建新的缓存。

npm-${{ hashFiles('package-lock.json') }}

GitHub 会评估表达式 `hash "package-lock.json"` 以推导出最终的 `key`。

npm-d5ea0750

使用 `cache` 操作的输出

您可以使用 `cache` 操作的输出根据缓存命中或未命中执行某些操作。当为指定的 `key` 找到缓存的完全匹配项时,`cache-hit` 输出将设置为 `true`。

在上面的示例工作流程中,有一个步骤列出了如果发生缓存未命中,则 Node 模块的状态

- if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }}
  name: List the state of node modules
  continue-on-error: true
  run: npm list

匹配缓存键

`cache` 操作首先在包含工作流程运行的的分支中搜索 `key` 和缓存版本的缓存命中。如果没有命中,它会搜索 `restore-keys` 和版本。如果在当前分支中仍然没有命中,`cache` 操作会在默认分支上重试相同的步骤。请注意,范围限制适用于搜索。有关更多信息,请参阅“访问缓存的限制”。

缓存版本是一种使用 `path` 和创建缓存时使用的压缩工具的元数据来标记缓存的方法。这确保了使用工作流程运行可以唯一地匹配它可以实际解压缩和使用的缓存。有关更多信息,请参阅 Actions 缓存文档中的 缓存版本

`restore-keys` 允许您指定在 `key` 上发生缓存未命中时要使用的备用还原键列表。您可以创建多个还原键,按从最具体到最不具体的顺序排列。`cache` 操作按顺序搜索 `restore-keys`。当键不直接匹配时,操作会搜索以还原键为前缀的键。如果有多个还原键的局部匹配,操作将返回最近创建的缓存。

使用多个恢复密钥的示例

restore-keys: |
  npm-feature-${{ hashFiles('package-lock.json') }}
  npm-feature-
  npm-

运行器会评估表达式,这些表达式解析为以下 restore-keys

restore-keys: |
  npm-feature-d5ea0750
  npm-feature-
  npm-

恢复密钥 npm-feature- 与以字符串 npm-feature- 开头的任何密钥匹配。例如,密钥 npm-feature-fd3052denpm-feature-a9b253ff 都与恢复密钥匹配。将使用创建日期最新的缓存。此示例中的密钥按以下顺序搜索

  1. npm-feature-d5ea0750 与特定哈希匹配。
  2. npm-feature- 与以 npm-feature- 为前缀的缓存密钥匹配。
  3. npm- 与以 npm- 为前缀的任何密钥匹配。

搜索优先级的示例

key:
  npm-feature-d5ea0750
restore-keys: |
  npm-feature-
  npm-

例如,如果拉取请求包含 feature 分支并指向默认分支 (main),则操作将按以下顺序搜索 keyrestore-keys

  1. feature 分支中的密钥 npm-feature-d5ea0750
  2. feature 分支中的密钥 npm-feature-
  3. feature 分支中的密钥 npm-
  4. main 分支中的密钥 npm-feature-d5ea0750
  5. main 分支中的密钥 npm-feature-
  6. main 分支中的密钥 npm-

使用限制和驱逐策略

GitHub 将删除所有超过 7 天未访问的缓存条目。您可以存储的缓存数量没有限制,但存储库中所有缓存的总大小限制为 10 GB。存储库达到最大缓存存储空间后,缓存驱逐策略将通过删除存储库中最旧的缓存来创建空间。

如果超过限制,GitHub 将保存新缓存,但将开始驱逐缓存,直到总大小小于存储库限制。缓存驱逐过程可能会导致缓存抖动,即缓存以高频率创建和删除。为了减少这种情况,您可以查看存储库的缓存并采取纠正措施,例如从特定工作流中删除缓存。有关更多信息,请参阅“管理缓存”。

管理缓存

要管理从工作流创建的缓存,您可以

  • 查看存储库的所有缓存条目的列表。
  • 使用特定元数据(例如缓存大小、创建时间或上次访问时间)过滤和排序缓存列表。
  • 从存储库中删除缓存条目。
  • 监控存储库和组织的聚合缓存使用情况。

有多种方法可以管理存储库的缓存

  • 使用 GitHub 网页界面,如下所示。

  • 使用 REST API。有关更多信息,请参阅“GitHub Actions 缓存的 REST API 端点”。

  • 安装 `gh cache` 子命令来从命令行管理您的缓存。有关更多信息,请参阅 GitHub CLI 文档

    注意:如果您手动执行此操作,请确保已安装 2.32.0 或更高版本的 CLI。

查看缓存条目

您可以使用 Web 界面查看存储库的缓存条目列表。在缓存列表中,您可以查看每个缓存占用了多少磁盘空间、缓存创建时间以及缓存上次使用时间。

  1. 在 GitHub.com 上,导航到存储库的主页。

  2. 在您的存储库名称下,单击 操作.

    Screenshot of the tabs for the "github/docs" repository. The "Actions" tab is highlighted with an orange outline.

  3. 在左侧边栏的“管理”部分下,单击 缓存.

  4. 查看存储库的缓存条目列表。

    • 要搜索用于特定分支的缓存条目,请单击 **分支** 下拉菜单并选择一个分支。缓存列表将显示所有用于所选分支的缓存。
    • 要搜索具有特定缓存键的缓存条目,请在 **筛选缓存** 字段中使用语法 `key: key-name`。缓存列表将显示所有使用该键的分支的缓存。

    Screenshot of the list of cache entries.

删除缓存条目

具有存储库 `写入` 权限的用户可以使用 GitHub Web 界面删除缓存条目。

  1. 在 GitHub.com 上,导航到存储库的主页。

  2. 在您的存储库名称下,单击 操作.

    Screenshot of the tabs for the "github/docs" repository. The "Actions" tab is highlighted with an orange outline.

  3. 在左侧边栏的“管理”部分下,单击 缓存.

  4. 在要删除的缓存条目的右侧,单击 .

    Screenshot of the list of cache entries. A trash can icon, used to delete a cache, is highlighted with a dark orange outline.

强制删除缓存条目

缓存具有分支范围限制,这意味着某些缓存的使用选项有限。有关缓存范围限制的更多信息,请参阅“缓存依赖项以加快工作流程”。如果限制在特定分支的缓存使用大量存储配额,可能会导致 `default` 分支的缓存频繁创建和删除。

例如,一个仓库可能会有很多新的拉取请求打开,每个请求都有自己的缓存,这些缓存仅限于该分支。这些缓存可能会占用该仓库的大部分缓存存储空间。一旦仓库达到其最大缓存存储空间,缓存驱逐策略将通过删除仓库中最旧的缓存来腾出空间。为了防止这种情况发生时出现缓存抖动,您可以设置工作流,以比缓存驱逐策略更快的频率删除缓存。您可以使用 gh-actions-cache CLI 扩展来删除特定分支的缓存。

以下示例工作流使用 gh-actions-cache 在拉取请求关闭后删除分支创建的最多 100 个缓存。

要在跨仓库拉取请求或来自 fork 的拉取请求上运行以下示例,您可以使用 pull_request_target 事件触发工作流。如果您确实使用 pull_request_target 触发工作流,则需要考虑安全问题。有关更多信息,请参阅“触发工作流的事件”。

name: cleanup caches by a branch
on:
  pull_request:
    types:
      - closed

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - name: Cleanup
        run: |
          gh extension install actions/gh-actions-cache

          echo "Fetching list of cache key"
          cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )

          ## Setting this to not fail the workflow while deleting cache keys.
          set +e
          echo "Deleting caches..."
          for cacheKey in $cacheKeysForPR
          do
              gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
          done
          echo "Done"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          REPO: ${{ github.repository }}
          BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge

或者,您可以使用 API 以自己的频率自动列出或删除所有缓存。有关更多信息,请参阅“GitHub Actions 缓存的 REST API 端点”。