# 依赖项缓存参考

查找工作流中依赖项缓存功能的相关信息。

## `cache` 操作使用情况

还原缓存时，[`cache` 操作](https://github.com/actions/cache)将尝试采用以下顺序：

1. 首先，它会搜索所提供的 `key` 的精确匹配项。
2. 如果未找到精确匹配项，将搜索 `key` 的部分匹配项。
3. 如果仍未找到匹配项，并且你已提供 `restore-keys`，则会按顺序检查这些键是否存在部分匹配项。 有关详细信息，请参阅[缓存键匹配](#cache-key-matching)。

如果与提供的 `key` 完全匹配，则这被视为缓存命中。 如果没有缓存与提供的 `key` 完全匹配，则这被视为缓存失误。 在缓存失误情况下，该操作会在作业成功完成时自动创建一个新缓存。 新缓存将使用你提供的 `key`，并包含你在 `path` 中指定的文件。 有关如何处理此问题的详细信息，请参阅[缓存命中和缓存失误](#cache-hits-and-misses)。

不能更改现有缓存的内容。 相反，可以使用新键创建新缓存。

### `cache` 操作的输入参数

* `key`：**必要**。保存缓存时创建的密钥和用于搜索缓存的密钥。 它可以是变量、上下文值、静态字符串和函数的任何组合。 密钥最大长度为 512 个字符，密钥长度超过最大长度将导致操作失败。

* `path`：**必要**。运行程序上用于缓存或还原的路径。
  * 可以指定单个路径，也可以在单独的行上添加多个路径。 例如：

    ```yaml
    - name: Cache Gradle packages
      uses: actions/cache@v4
      with:
        path: |
          ~/.gradle/caches
          ~/.gradle/wrapper
    ```

  * 可以指定目录或单个文件，并且支持 glob 模式。

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

* `restore-keys`：**可选**。包含备用还原键的字符串，每个还原键均放置在一个新行上。 如果 `key` 没有发生缓存命中，则按照提供的顺序依次使用这些还原键来查找和还原缓存。 例如：

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

* `enableCrossOsArchive`：**Optional**，一个布尔值，当启用时，允许Windows运行程序保存或还原与缓存创建所用操作系统无关的缓存。 如果未设置此参数，则默认为 `false`。 有关详细信息，请参阅操作缓存文档中的[交叉操作系统缓存](https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cross-os-cache)。

> \[!NOTE]
> 建议不要在缓存路径中的文件中存储任何敏感信息，例如访问令牌或登录凭据。 具有读取访问权限的任何人都可以在存储库上创建拉取请求并访问缓存的内容。 此外，存储库的分支可以在基础分支上创建拉取请求，并在基础分支上访问缓存。

### `cache` 操作的输出参数

* `cache-hit`：一个表示找到了键的精确匹配项的布尔值。

### 缓存命中和缓存失误

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

当 `key` 与现有缓存不匹配时，称为\_缓存失误\_，在作业成功完成时会自动创建一个新缓存。

发生缓存失误时，该操作还会搜索指定的 `restore-keys` 以查找任何匹配项：

1. 如果提供 `restore-keys`，`cache` 操作将按顺序搜索与 `restore-keys` 列表匹配的任何缓存。
   * 当存在精确匹配时，该操作会将缓存中的文件还原到 `path` 目录。
   * 如果没有精确匹配，操作将会搜索恢复键值的部分匹配。 当操作找到部分匹配时，最近的缓存将还原到 `path` 目录。
2. `cache` 操作完成，作业中的下一个步骤运行。
3. 如果作业成功完成，则操作将自动创建一个包含 `path` 目录内容的新缓存。

有关缓存匹配过程的更详细说明，请参阅[缓存键匹配](#cache-key-matching)。

### 使用 `cache` 操作的示例

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

```yaml copy
name: Caching with npm
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Cache node modules
        id: cache-npm
        uses: actions/cache@v4
        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支持。 有关详细信息，请参阅 [上下文参考](/zh/enterprise-server@3.22/actions/reference/workflows-and-actions/contexts) 和 [对工作流和操作中的表达式求值](/zh/enterprise-server@3.22/actions/reference/workflows-and-actions/expressions)。

使用表达式创建 `key` 使你能够在依赖项更改时自动创建新缓存。

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

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

GitHub 计算表达式 `hash "package-lock.json"` 以派生最终 `key`结果。

```yaml
npm-d5ea0750
```

### 使用 `cache` 操作的输出

可以使用 `cache` 操作的输出，以根据发生的是缓存命中还是缓存失误来执行某些操作。 找到指定 `key` 的缓存的精确匹配时，`cache-hit` 输出设置为 `true`。

在上面的示例工作流中，有一个步骤会列出发生缓存失误时节点模块的状态：

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

## 缓存键匹配

`cache` 操作首先在包含工作流运行的分支中搜索 `key` 和缓存\_版本\_的缓存命中。 如果没有命中，它将搜索 `key` 的前缀匹配项，如果仍然没有命中，则会搜索 `restore-keys` 和\_版本\_。 如果当前分支中仍然没有命中，`cache` 操作会在默认分支上重试相同步骤。 请注意，范围限制在搜索期间适用。 有关详细信息，请参阅[访问缓存的限制](#restrictions-for-accessing-a-cache)。

缓存版本是使用 `path` 元数据以及创建缓存时使用的压缩工具标记缓存的一种方法。 这可确保使用的工作流运行与它实际可以解压缩和使用的缓存唯一匹配。 有关详细信息，请参阅操作缓存文档中的[缓存版本](https://github.com/actions/cache#cache-version)。

通过 `restore-keys`，你可以指定当 `key` 中发生缓存失误时要使用的备用还原键列表。 您可以创建从最具体到最不具体的多个恢复键。
`cache` 操作按顺序搜索 `restore-keys`。 当键不直接匹配时，操作将搜索以恢复键为前缀的键。 如果恢复键值有多个部分匹配项，操作将返回最近创建的缓存。

### 使用多个恢复键值的示例

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

运行器将计算表达式，这些表达式解析为以下 `restore-keys`：

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

还原键 `npm-feature-` 与以字符串 `npm-feature-` 开头的任何键匹配。 例如，`npm-feature-fd3052de` 和 `npm-feature-a9b253ff` 这两个键都与还原键匹配。 将使用创建日期最新的缓存。 此示例中的键值按以下顺序搜索：

1. \*\*
   `npm-feature-d5ea0750`
   \*\* 匹配特定哈希。
2. \*\*
   `npm-feature-`
   \*\* 匹配前缀为 `npm-feature-` 的缓存键。
3. \*\*
   `npm-`
   \*\* 匹配前缀为 `npm-` 的任何键。

#### 搜索优先级示例

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

例如，如果拉取请求包含 `feature` 分支，并以默认分支 (`main`) 为目标，则该操作将按以下顺序搜索 `key` 和 `restore-keys`：

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

## 针对特定包管理器的 `setup-*` 操作

如果要缓存下列包管理器，则使用其各自的 setup-\* 操作需要最小配置，并且将为你创建和还原依赖项缓存。

| 包管理器                                                                                              | 用于缓存的 setup-\* 操作 |
| ------------------------------------------------------------------------------------------------- | ----------------- |
| npm、Yarn、pnpm                                                                                     |                   |
| [setup-node](https://github.com/actions/setup-node#caching-global-packages-data)                  |                   |
| pip、pipenv、Poetry                                                                                 |                   |
| [setup-python](https://github.com/actions/setup-python#caching-packages-dependencies)             |                   |
| Gradle、Maven                                                                                      |                   |
| [setup-java](https://github.com/actions/setup-java#caching-packages-dependencies)                 |                   |
| RubyGems                                                                                          |                   |
| [setup-ruby](https://github.com/ruby/setup-ruby#caching-bundle-install-automatically)             |                   |
| 去 `go.sum`                                                                                        |                   |
| [setup-go](https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs)        |                   |
| .NET NuGet                                                                                        |                   |
| [setup-dotnet](https://github.com/actions/setup-dotnet?tab=readme-ov-file#caching-nuget-packages) |                   |

## 访问缓存的限制

访问限制通过在不同分支或标记之间创建逻辑边界来提供缓存隔离和安全性。
工作流运行可以还原在当前分支或默认分支（通常是 `main`）中创建的缓存。 如果为拉取请求触发了工作流运行，则它还可以还原在基分支中创建的缓存，包括分支存储库的基分支。 例如，如果分支 `feature-b` 具有基分支 `feature-a`，则对拉取请求触发的工作流运行将有权访问在默认 `main` 分支、`feature-a` 基分支和当前 `feature-b` 分支中创建的缓存。

工作流运行无法还原为子分支或同级分支创建的缓存。 例如，在父分支 `feature-b` 上触发的工作流运行无法访问为子分支 `main` 创建的缓存。 例如，为 `feature-a` 分支（基分支为 `main`）创建的缓存无法供其同级 `feature-c` 分支（基分支为 `main`）访问。 工作流运行也不能还原为不同标记名称创建的缓存。 例如，针对标记 `release-a`（基分支为 `main`）触发的工作流无法访问为标记 `release-b`（基分支为 `main`）创建的缓存。

当缓存由对拉取请求触发的工作流运行创建时，会为合并引用 (`refs/pull/.../merge`) 创建缓存。 因此，缓存的范围有限，只能通过重新运行拉取请求来还原。 它不能由基分支或针对该基分支的其他拉取请求还原。

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

> \[!NOTE]
> 由于对象是由运行器直接从缓存中检索或存入缓存的，因此 Actions 运行器必须能够直接连接到在 GitHub Enterprise Server 中配置的 Actions 对象存储，例如 AWS S3 或 Azure Blob 存储。 自托管运行器使用由 GitHub Enterprise Server 实例提供的访问 URL 向 Blob 存储提供程序进行身份验证。 此 URL 为 Blob 存储提供程序提供有效的临时身份验证凭据。 此过程由实例自身启动，会协调针对对象存储的所有请求。
>
> 这意味着 `actions/cache` 需要与 Blob 存储建立 HTTPS 连接才能正常工作。
>
> 所有元数据均由制品缓存服务管理，该服务是 GitHub Actions 内部的一个微服务。
>
> 有关缓存存储的详细信息，请参阅[外部存储要求](/zh/enterprise-server@3.22/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server#external-storage-requirements)。

## 低信任工作流触发器的缓存访问

某些工作流在响应事件时运行，这些事件可由对存储库没有写入访问权限的人员启动，例如分支拉取请求或问题注释。 当这些事件在默认分支的上下文中运行时，它们可能被用来写入恶意缓存，而后续权限更高的工作流会恢复并信任该缓存。 此类攻击称为 *缓存中毒*。

为了降低此风险，只有这些工作流触发器才能在默认分支的作用域中创建或覆盖缓存：

* `push`
* `workflow_dispatch`
* `repository_dispatch`
* `delete`
* `registry_package`
* `page_build`
* `schedule`

由任何其他最终解析到默认分支的事件触发的运行，会被授予对默认分支作用域内缓存的只读访问权限。 这些运行可以还原现有缓存，但无法创建或覆盖它们。 这包括其有效负载或发起组件可以受存储库外部的人员（例如 `pull_request_target`， `issue_comment`和 `workflow_run`）的影响的触发器。

`pull_request` 事件不受影响。 由 `pull_request` 运行创建的缓存，其作用域已限定为合并 ref（`refs/pull/.../merge`），无法写入默认分支的作用域。 有关详细信息，请参阅[访问缓存的限制](#restrictions-for-accessing-a-cache)。

当具有只读缓存访问权限的运行尝试保存缓存时，缓存保存会失败，但该步骤和作业都不会失败。 工作流将继续执行，该失败会在工作流日志中记录为警告。 在这种情况下，请考虑以下事项：

* 若要保留默认分支作用域内缓存带来的性能优势，请确保有一个受信任的工作流来持续更新缓存，例如由针对默认分支的 `push` 触发的 CI 构建。 然后，这些缓存条目可由低信任事件（例如 `pull_request_target`）触发的工作流还原。
* 在低信任工作流中，切换到仅还原缓存操作，例如 `actions/cache/restore` 使预期缓存使用情况清晰，并避免工作流运行日志中的警告。

## 安全使用缓存的最佳做法

缓存内容未签名或验证，任何可以读取缓存的工作流运行都可以提取其内容。 提取的缓存可能会修改随后在工作流运行中执行的文件，从而导致恶意代码执行。 遵循以下做法，降低使用缓存的安全风险。

* **不要将敏感信息存储在缓存中。** 可以针对存储库打开拉取请求的任何人都可以读取基分支中的缓存内容。 不要将机密、令牌或凭据写入缓存路径。 而是将敏感值存储为机密。 请参阅“[机密](/zh/enterprise-server@3.22/actions/concepts/security/secrets)”。
* **保存来自受信任触发器的缓存。** 将缓存写入限制为仅适用于由受信任主体触发的工作流（通常是具有仓库写入权限的主体）。 有关强制实施的默认限制，请参阅 [低信任工作流触发器的缓存访问](#cache-access-for-low-trust-workflow-triggers) ，以限制哪些工作流触发器可以写入缓存。 此外，请考虑使用具有部署保护规则的环境进一步限制可修改缓存的工作流。 请参阅“[管理部署环境](/zh/enterprise-server@3.22/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments)”。
* **遵循工作流安全最佳实践来加固工作流：** 仅将缓存写入权限授予那些已针对工作流漏洞进行加固的工作流。 按照 [安全使用指南](/zh/enterprise-server@3.22/actions/reference/security/secure-use#writing-workflows) 中的指导，防止工作流中的漏洞可能导致代码执行和引入恶意缓存条目。

有关保护工作流的更广泛指南，请参阅 [安全使用指南](/zh/enterprise-server@3.22/actions/reference/security/secure-use)。

## 使用限制和收回政策

GitHub 对缓存存储和保留应用限制来管理存储成本并防止滥用。 了解这些限制有助于优化缓存使用情况。

### 默认限制

GitHub 将删除在 7 天内未访问的任何缓存条目。 可以存储的缓存数没有限制，但存储库中所有缓存的总大小有限。 默认情况下，每个存储库的限制为 10 GB，但企业所有者、组织所有者或存储库管理员可以增加此限制。
仓库达到其最大缓存存储后，缓存逐出策略将按照上次访问时间的顺序，从最早到最近，依次删除缓存以释放空间。

如果超过此限制，GitHub 将保存新缓存，但会开始收回缓存，直到总大小小于存储库限制。 缓存淘汰过程可能会导致缓存抖动，即缓存以很高的频率被创建和删除。 为了减少这种情况，可以查看存储库的缓存并采取纠正措施，例如从特定工作流选择启用该功能的用户。 请参阅 [管理缓存](/zh/enterprise-server@3.22/actions/how-tos/manage-workflow-runs/manage-caches)。 还可以增加存储库的缓存大小限制。 有关详细信息，请参阅“[管理存储库的GitHub Actions设置](/zh/enterprise-server@3.22/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#configuring-cache-storage-for-a-repository)”。

## 后续步骤

若要管理依赖项缓存，请参阅 [管理缓存](/zh/enterprise-server@3.22/actions/how-tos/manage-workflow-runs/manage-caches)。