如何在GitHub Actions中缓存yarn包

54

我正在使用GitHub Actions来构建我的TypeScript项目。每次运行操作时,我需要等待3分钟才能安装所有依赖项。

有没有办法缓存yarn的依赖项,以便构建时间更快?

我尝试了这个:

     - name: Get yarn cache directory path
       id: yarn-cache-dir-path
       run: echo "::set-output name=dir::$(yarn cache dir)"

     - uses: actions/cache@v1
       id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
       with:
         path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
         key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
         restore-keys: |
           ${{ runner.os }}-yarn-

    - name: Install yarn
      run: npm install -g yarn

    - name: Install project dependencies
      run: yarn

但是构建时间仍然相同。

4个回答

102
  1. 使用 actions/setup-node@v2 或更新版本:

    - name: 设置 Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '16'
        cache: 'yarn'
    
    - name: 安装项目依赖
      run: yarn
    

    actions/setup-node@v2 或更新版本已内置缓存功能,因此您无需再设置 actions/cache

  2. 使用 actions/setup-node@v1 并使用 actions/cache 缓存 Yarn 全局缓存:

    - name: 设置 Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '16'
    
    - name: 获取 Yarn 缓存目录路径
      id: yarn-cache-dir-path
      run: echo "::set-output name=dir::$(yarn cache dir)"
    
    - uses: actions/cache@v3
      id: yarn-cache # 使用此项来检查是否为 `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
      with:
        path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
        key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
        restore-keys: |
        ${{ runner.os }}-yarn-
    
    - name: 安装项目依赖
      run: yarn --prefer-offline
    
上面的缓存代码只缓存和恢复Yarn的全局缓存目录,而不是缓存node_modules目录本身。
为了提高安装速度,您需要告诉Yarn在安装过程中尽可能使用缓存的下载文件(在上述缓存目录中),而不是从服务器下载:
- name: Install project dependencies
  run: yarn --prefer-offline
  • 使用actions/cache缓存node_modules目录(不推荐

    您还可以直接缓存node_modules目录,并在缓存可用时跳过运行yarn

    但是不推荐这样做,因为:

    • yarn很擅长利用全局缓存。如果依赖项已经在全局缓存中可用,yarn可以在不到1秒的时间内完成运行(参见@mvlabat的评论)。
    • node_modules可能会损坏。每次重新运行yarn并让yarn决定是否从缓存中获取文件更安全(因为yarn在使用缓存之前会尝试验证缓存)。
    - name: 设置 Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '16'
    
    - name: 获取 yarn 缓存目录路径
        id: yarn-cache-dir-path
        run: echo "::set-output name=dir::$(yarn cache dir)"
    
    - name: 缓存 yarn 缓存
        uses: actions/cache@v3
        id: cache-yarn-cache
        with:
        path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
        key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
        restore-keys: |
            ${{ runner.os }}-yarn-
    
    - name: 缓存 node_modules
        id: cache-node-modules
        uses: actions/cache@v3
        with:
        path: node_modules
        key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
        restore-keys: |
            ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-
    
    - run: yarn
        if: |
        steps.cache-yarn-cache.outputs.cache-hit != 'true' ||
        steps.cache-node-modules.outputs.cache-hit != 'true'
    

  • 2
    此外,yarn 本身就能够很好地检测出是否需要更新依赖项。例如,在本地第二次运行 yarn 安装时,它会在不到1秒的时间内完成。我认为在任何情况下都不应该跳过 yarn 步骤。 - mvlabat
    1
    谢谢大家的评论!我完全同意。我已经更新了答案,使其更清晰明了。 - Quang Lam
    2
    有其他人能澄清一下吗?既然在最后一个代码示例中我们还检查了缓存的yarn.lock,那么“不推荐使用”的选项现在是否适用?我已经测试过添加/删除包,似乎可以可靠地捕获这些差异。谢谢。 - sledgeweight
    2
    为什么不推荐缓存 node_modules? - Penguin
    8
    您可以将 node_modules 缓存起来,这样也可以正常工作。但是 node_modules 可能会损坏。每次重新运行 yarn 并让 yarn 决定是否从缓存中获取文件更加安全(假设 yarn 在使用缓存之前会尝试验证缓存)。 - Quang Lam
    显示剩余10条评论

    42

    正如Github软件包readme所述:

    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v2
      with:
        node-version: '14'
        cache: 'npm' # or yarn
    - run: npm install
    - run: npm test
    

    编辑:

    事实证明,文档编写方式非常具有误导性,他们进行了更新以澄清只缓存全局缓存目录,而不是node_modules文件夹,如此问题中所述。

    正如Mrchief在评论中提到的那样:

    ......您仍将遭受npm i time,但可以节省因从互联网下载(如果模块在npm缓存中)而产生的下载时间

    因此,您应该仍然使用此方法来节省从互联网下载软件包的时间,但是如果您想缓存node_modules文件夹,请检查其他答案,它使用actions/cache

    您还应该查看Quang Lam的答案及其评论,了解为什么您不应该缓存node_modules文件夹。


    4
    其他答案要么没有使用setup-node,要么使用的是v1版本。 缓存功能是在v2中添加的。 现在应该接受这个答案。 - Scott Vandehey
    2
    该方法不会缓存 node_modules,而是全局缓存(在 npm 的情况下为 .npm)。因此,您仍然需要花费时间进行 npm i,只是从互联网下载时节省了时间(如果模块在 npm 缓存中)。 - Mrchief
    1
    正如@Mrchief所述,这实际上并没有将您的软件包缓存到node_modules中。他们描述的方式非常误导人,但在一些投诉之后,他们更新了自己的自述文件。更多信息请参见:https://github.com/actions/setup-node/issues/416 - Vasco
    1
    @Vasco我更新了我的答案,所以现在清楚地知道实际上正在缓存什么,感谢你的评论。 - Vencovsky

    14

    如同在缓存步骤中 id 字段旁的注释中提到的:

    使用此选项来检查是否有 cache-hitsteps.yarn-cache.outputs.cache-hit != 'true'

    您需要添加一个条件性的 if 属性来决定是否应该运行此步骤:

    - name: Install yarn
      run: npm install -g yarn
    
    - name: Install project dependencies
      if: steps.yarn-cache.outputs.cache-hit != 'true' # Over here!
      run: yarn
    

    顺便提一下,您应该使用设置NodeJS GitHub操作,它还会为您设置Yarn:

    - uses: actions/setup-node@v1
      with:
        node-version: '10.x' # The version spec of the version to use.
    

    请查看action.yml文件,以获取有效输入的完整列表。


    编辑:事实证明,Yarn已包含在GitHub托管的Ubuntu 18.04.4 LTS (ubuntu-latest/ubuntu-18.04)运行器中 安装软件的列表,因此无需包含全局安装Yarn的步骤。


    这是我的配置文件:https://pastebin.com/CCgFjEW0 我已经在使用 actions/setup-node@v1,所以我不需要安装 yarn 吗? - Baterka
    遗憾的是,“if”条件语句没有起到帮助作用。 - Baterka
    1
    关于第一个评论,请查看我对答案所做的编辑。至于第二个评论,您能指出条件“if”属性如何无法帮助吗? - Edric
    3
    谢谢,那个方法有效。但在运行jest时出错了。jest --config=jest.config.js /bin/sh: 1: jest: not found error Command failed with exit code 127 此外,禁用缓存后操作成功了。 - Vivek

    11

    actions/setup-node 支持自定义缓存选项,从 v2 版本开始,可用于缓存包和数据

    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '16'
        cache: 'yarn'
    
    - name: Install JS dependencies
      run: yarn install
    

    缓存应按照推荐的方式执行,只缓存yarn cache dir而不是node_modules。 不建议缓存node_modules,因为它可能会导致问题,例如当节点版本更改时。


    Old answer:

    这是专门针对Yarn的一行缓存:https://github.com/c-hive/gha-yarn-cache

    它按照GitHub的建议进行缓存。支持Yarn v1和v2。

    同样适用于NPM:https://github.com/c-hive/gha-npm-cache


    对于那些给这个帖子点踩的人,请添加一些评论说明为什么要这样做 :) 我猜这现在已经内置了:https://github.com/actions/cache/blob/main/examples.md#node---yarn - Hibbem
    2
    这仍然是一个1行代码,而示例中则需要10行。 - thisismydesign
    1
    是的,我也不认为这应该被踩。问题是“如何在GitHub Actions中缓存yarn包”,并且回答了这个问题。我相信其他答案提供了更深入的解释,但对于寻找快速解决方案的人来说,这真的很有帮助。出于好奇,您测试过这个一行代码的性能吗?它比较长的代码更快、更慢还是一样快? - Bielik
    2
    它在幕后执行相同的命令。 - thisismydesign

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接