如何缓存 GitHub 操作中的步骤?

假设我有一个包含两个步骤的 GitHub 操作工作流。

  1. 下载并编译我的应用程序的依赖项。
  2. 编译并测试我的应用程序

我的依赖项很少更改,编译后的依赖项可以安全缓存,直到我下次更改指定其版本的锁文件。

是否可以保存第一步的结果,以便在将来的工作流中可以跳过该步骤?

45309 次浏览

我的依赖项很少更改,编译后的依赖项可以安全缓存,直到我下次更改指定其版本的锁文件。是否可以保存第一步的结果,以便在将来的工作流中可以跳过该步骤?

第一步是:

下载并编译我的应用程序的依赖项。

GitHub Actions 本身不会为您做这些。我能给您的唯一建议是,您应该遵循 Docker 最佳实践,以确保如果 Actions 确实使用了 Docker 缓存,您的映像可以被重用,而不是重建。请参见: < a href = “ https://docs.docker.com/development/development-images/dockerfile _ best-Practices/# ’’’’’”rel = “ nofollow norefrer”> https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache

构建映像时,Docker 按照指定的顺序逐步执行 Dockerfile 中的指令。在检查每条指令时,Docker 在其缓存中寻找可以重用的现有映像,而不是创建新的(重复的)映像。

这也意味着 GitHub Actions 的底层系统可以/将利用 Docker 缓存。

然而,像编译这样的事情,Docker 将不能使用缓存机制,所以我建议您好好考虑一下,如果这是您迫切需要的东西。另一种方法是从工件存储(Nexus、 NPM、 MavenCentral)下载已编译/处理的文件,以跳过该步骤。您确实需要权衡在此基础上构建所带来的好处和增加的复杂性。

大多数用例都包含在现有的操作中,例如:


通过 缓存动作支持自定义缓存。它可以在存储库中的作业和工作流之间工作。参见: GitHub 文档例子

考虑一下 下面的例子:

name: GitHub Actions Workflow with NPM cache


on: push


jobs:
build:


runs-on: ubuntu-latest


steps:
- uses: actions/checkout@v3


- name: Cache NPM dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: $\{\{ runner.OS }}-npm-cache-$\{\{ hashFiles('**/package-lock.json') }}
restore-keys: |
$\{\{ runner.OS }}-npm-cache-


- name: Install NPM dependencies
run: npm install

缓存是如何一步一步地工作的:

  • Cache NPM dependencies步骤中,操作将检查是否存在当前 key的缓存
  • 如果没有找到缓存,它将使用 restore-keys检查空间匹配。在这种情况下,如果 package-lock.json发生更改,它将回退到以前的缓存。使用 OS 和缓存名称作为键的前缀和还原键非常有用,因为它不应该为不同类型的缓存或 OS 加载文件。
  • 如果找到任何缓存,它将把文件加载到 path
  • CI 继续下一步,并且可以使用从缓存加载的文件。在这种情况下,npm install将使用 ~/.npm中的文件保存通过网络下载的文件(注意,对于 NPM,直接缓存 node_modules不推荐)。
  • 在 CI 运行结束时,执行一个事后操作,以便在 key更改时保存更新的缓存。这在工作流中没有明确定义,而是内置在缓存操作中,以同时负责加载和保存缓存。

您还可以使用 @ actions/cache构建自己的可重用缓存逻辑,例如:


老答案:

本机缓存目前不可能,预计于二零一九年十一月中实施

您可以使用构件(12)在作业之间移动目录(在1个工作流中) ,就像在 GH 社区委员会上建议的那样。然而,这是 不能跨工作流程工作

如果您在 WorkFlows 中使用 Docker,作为 @ Peterevans 回答道,GitHub 现在支持通过 缓存操作进行缓存,但它有其局限性。

因此,您可能会发现有用的 这次行动可以绕过 GitHub 的操作限制。

免责声明: 在 GitHub 正式启动之前,我就创建了支持缓存的操作,由于它的简单性和灵活性,我现在仍在使用它。

我将总结两种选择:

  1. 缓存
  2. 多克

缓存

可以在工作流中添加命令以缓存目录。当到达该步骤时,它将检查您指定的目录是否以前保存过。如果是这样,它会抓住它。如果没有,就不会。然后在进一步的步骤中,您编写检查来查看是否存在缓存的数据。例如,假设您正在编译某个依赖项,这个依赖项很大,而且不会发生太大变化。您可以在工作流的开始添加一个缓存步骤,然后添加一个步骤来构建目录的内容(如果目录中没有内容)。第一次运行它时不会找到文件,但随后它会找到,并且您的工作流将运行得更快。

在幕后,GitHub 将目录的 zip 上传到 GitHub 自己的 AWS 存储中。他们会清除超过一周或者超过2GB 的内存。

这种技术的一些缺点是它只保存目录。因此,如果您安装到/usr/bin 中,就必须缓存它!那就尴尬了。您应该将其安装到 $home/中。并使用 echo set-env 将其添加到路径中。

多克

Docker 更复杂一些,它意味着您现在必须拥有一个 dockerhub 帐户并管理两件事情。但它的力量更强大。与只保存一个目录不同,您将保存整台计算机!您要做的是创建一个 Dockerfile,其中包含所有依赖项,比如 apt-get 和 python 管道线,甚至长编译。然后您将构建这个 docker 映像并在 dockerhub 上发布它。最后,您将把测试设置为在新的 docker 映像上运行,而不是在 ubuntu-last 上运行。从现在开始,它将不再安装依赖项,而只是下载映像。

您可以通过将 Dockerfile 存储在与项目相同的 GitHub 回购中来进一步实现自动化,然后编写一个包含步骤的作业,这些步骤将下载最新的 docker 映像,如果需要,只重新构建更改后的步骤,然后上传到 dockerhub。然后一个工作“需要”这一点,并使用的形象。这样,如果需要,您的工作流程既可以更新 Docker 映像,也可以使用它。

缺点是,您的 dep 将放在一个文件中,即 Dockerfile 和工作流中的测试,所以它们并不是全部放在一起。另外,如果下载映像的时间超过了构建依赖项的时间,那么这是一个糟糕的选择。


我认为每一个都有好的一面和坏的一面。缓存只适用于非常简单的事情,比如编译到。本地。如果你需要更广泛的东西,多克是最强大的。

cache操作只能缓存文件夹的内容。因此,如果有这样一个文件夹,您可以通过缓存它来赢得一些时间。

例如,如果你使用一些虚构的 package-installer(比如 Python 的 pipvirtualenv,或者 NodeJS 的 npm,或者其他任何将文件放入文件夹的东西) ,你可以通过这样做赢得一些时间:

    - uses: actions/cache@v2
id: cache-packages  # give it a name for checking the cache hit-or-not
with:
path: ./packages/  # what we cache: the folder
key: $\{\{ runner.os }}-packages-$\{\{ hashFiles('**/packages*.txt') }}
restore-keys: |
$\{\{ runner.os }}-packages-
- run: package-installer packages.txt
if: steps.cache-packages.outputs.cache-hit != 'true'

所以重要的是:

  1. 我们给这个步骤命名为 cache-packages
  2. 稍后,我们将这个名称用于条件执行: ifsteps.cache-packages.outputs.cache-hit != 'true'
  3. 为缓存操作指定要缓存的文件夹的路径: ./packages/
  4. 缓存键: 依赖于输入文件的哈希值的东西。也就是说,如果任何 packages.txt文件发生更改,将重新构建缓存。
  5. 第二步,包安装程序,只有在没有缓存的情况下才会运行

对于 virtualenv的用户: 如果需要激活某个 shell 环境,则必须在每个步骤中激活。像这样:

- run: . ./environment/activate && command