跳至主要内容

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

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

关于缓存工作流依赖项

工作流运行通常会重用一个运行到另一个运行的相同输出或下载的依赖项。例如,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操作的输出参数

  • cache-hit:布尔值,指示是否为密钥找到了完全匹配项。

缓存命中和未命中

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

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

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

  1. 如果您提供restore-keys,则cache操作会依次搜索与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 Cache 文档中的缓存版本

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 Web 界面,如下所示。

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

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

    注意

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

查看缓存条目

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

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

  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 上,导航到存储库的主页。

  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 分支的缓存以高频率创建和删除。

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

以下示例工作流使用 gh 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: |
          echo "Fetching list of cache key"
          cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id')

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

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