62

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

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

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

是否有一种方法可以保存第一步的结果,以便将来的工作流程可以跳过该步骤?

4

6 回答 6

47

现在通过缓存操作原生支持缓存。它适用于存储库中的作业和工作流。另请参阅:https ://help.github.com/en/actions/automating-your-workflow-with-github-actions/caching-dependencies-to-speed-up-workflows 。

考虑以下示例

name: GitHub Actions Workflow with NPM cache

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1

    - name: Cache NPM dependencies
      uses: actions/cache@v1
      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

其中action的pathkey参数cache是用来标识缓存的。

可选项restore-keys用于可能回退到部分匹配(即,如果package-lock.json更改将使用先前的缓存)。

当使用回退并且有多个不同的缓存(例如,对于 JS 包和系统包)时,为键添加一些 id 前缀(npm-cache在此示例中)很有用。restore-keys否则,一个缓存可能会退回到另一个不相关的缓存。同样,在使用矩阵构建时操作系统前缀很有用,因此不同系统的缓存不会混淆。

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


老答案:

目前无法进行原生缓存,预计将于 2019 年 11 月中旬实施

您可以按照GH 社区委员会的建议,使用工件 ( 1 , 2 ) 在作业之间移动目录(在 1 个工作流程内)。但是,这不适用于工作流

于 2019-09-16T14:18:01.903 回答
6

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

例如,如果你使用一些虚构的东西package-installer(比如 Python 的pipor virtualenv,或 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
于 2020-06-29T13:35:35.810 回答
4

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

第一步是:

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

GitHub Actions 本身不会为您执行此操作。我能给你的唯一建议是你遵守 Docker 最佳实践,以确保如果 Actions 确实使用了 docker 缓存,你的图像可以被重用而不是重建。请参阅:https ://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache

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

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

但是编译之类的东西,Docker 将无法使用缓存机制,所以如果这是你迫切需要的东西,我建议你好好考虑一下。另一种方法是从工件存储(Nexus、NPM、MavenCentral)下载编译/处理的文件以跳过该步骤。您确实必须权衡好处与您在此基础上添加到构建中的复杂性。

于 2019-03-11T23:34:27.890 回答
3

如果您在工作流程中使用 Docker,正如@peterevans 回答的那样,GitHub 现在支持通过缓存操作进行缓存,但它有其局限性。

因此,您可能会发现此操作对绕过 GitHub 的操作限制很有用。

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

于 2019-11-07T16:20:53.887 回答
3

现在使用以下方式原生支持:https : //help.github.com/en/actions/automating-your-workflow-with-github-actions/caching-dependencies-to-speed-up-workflows

这是通过使用新的缓存操作来实现的:https ://github.com/actions/cache

于 2019-11-08T17:11:52.170 回答
1

我将总结这两个选项:

  1. 缓存
  2. 码头工人

缓存

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

在幕后,GitHub 正在将您的目录的 zip 文件上传到 github 自己的 AWS 存储。他们会清除一周以上或达到 2GB 限制的任何内容。

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

码头工人

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

您可以通过将该 Dockerfile 存储在与项目相同的 GitHub 存储库中来进一步自动化此操作,然后编写一个包含下载最新 docker 映像的步骤的作业,如有必要仅重建更改的步骤,然后上传到 dockerhub。然后是一项“需要”该工作并使用该图像的工作。这样,您的工作流程将在需要时更新 docker 映像并使用它。

缺点是您的 deps 将位于一个文件中,即 Dockerfile 和工作流中的测试,因此它们并没有放在一起。此外,如果下载图像的时间超过构建依赖项的时间,这是一个糟糕的选择。


我认为每个人都有优点和缺点。缓存只适用于非常简单的东西,比如编译成 .local。如果你需要更广泛的东西,Docker 是最强大的。

于 2019-12-21T03:32:12.193 回答