GitHub Actionsでactions/setup-nodeだけでnode_modulesをキャッシュできるのか試してみた

結論:actions/setup-nodeだけではできず、actions/cacheを使う必要がありました
2022.04.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 IoT事業部の若槻です。

GitHub Actionsでは、CI/CDでよく使われる処理がActionsとして公開されており、Workflow内で自由に使うことができます。

今回は、actions/setup-nodeを使用してWorkflowの実行時にnode_modulesをキャッシュできるのか試してみました。

actions/setup-nodeとは

actions/setup-nodeを使用すると、指定したバージョンのNode.js distributionのダウンロードおよびキャッシュをしたり、npm/yarn/pnpm dependencyをキャッシュしたりすることができるようです。

これを使えばWorkflow実行の高速化が期待できそうですね。

使い方

Usageを見ると使い方はこんな感じとのこと。

  • node-versionで指定したのNode.js distributionとsemverが一致するキャッシュが無ければダウンロードし、あればリストアして使用する。
  • cacheを指定することにより、ルートディレクトリ配下のpackage-lock.json(またはyarn.lock)により生成したハッシュ値が一致すれば、Dependencyのキャッシュをリストアする。
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
  with:
    node-version: 14
    cache: npm
- run: npm install

試してみる

それでは次のようなworkflowを使ってsetup-nodeのキャッシュの動作を試してみます。

.github/workflows/run.yaml

name: Caching with npm

on: push

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        id: setup_node_id
        with:
          node-version: 14
          cache: npm
      - run: echo '${{ toJSON(steps.setup_node_id.outputs) }}'
      - if: ${{ steps.setup_node_id.outputs.cache-hit != 'true' }}
        run: npm install
      - run: npm ls --depth=0

まず初回のworkflow実行です。

  • [Run actions/setup-node@v3]でnpm cache is not foundとあり、キャッシュミスしていることが分かります。
  • "cache-hit": "false"であるため、[Run npm install]でconditionがtrueとなり、npm installが実行されています。
  • [Run npm ls --depth=0]でnode_modulesにDependencyがインストールされていることが確認できます。
  • [Post Run actions/setup-node@v3]でCache saved successfullyとあり、キャッシュの保存が成功していることが分かります。

次に二回目のキャッシュを使用したworkflow実行です。

  • [Run actions/setup-node@v3]でCache restored successfullyとあり、キャッシュからのリストアが行われていることが分かります。
  • "cache-hit": "true"であるため、[Run npm install]でconditionがfalseとなり、npm installがスキップされています。
  • [Run npm ls --depth=0]でnode_modulesが見つからずエラーとなっています。

どういうことなのか?

ドキュメントのCaching packages dependenciesを改めて読むと、キャッシュからDependencyがnode_modulesが復元されるかのような記述となっています。

The action has a built-in functionality for caching and restoring dependencies. It uses actions/cache under the hood for caching dependencies but requires less configuration settings. Supported package managers are npm, yarn, pnpm (v6.10+). The cache input is optional, and caching is turned off by default.

The action defaults to search for the dependency file (package-lock.json or yarn.lock) in the repository root, and uses its hash as a part of the cache key. Use cache-dependency-path for cases when multiple dependency files are used, or they are located in different subdirectories.

一方Advanced usageCaching packages dependenciesには下記のようにあります。

The action follows actions/cache guidelines, and caches global cache on the machine instead of node_modules, so cache can be reused between different Node.js versions.

node_modulesではなくglobal cache on the machineがキャッシュされるとのことです。

ここで念の為確認。Workflowを次のように修正して実行。

.github/workflows/run.yaml

name: Caching with npm

on: push

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        id: setup_node_id
        with:
          node-version: 14
          cache: npm
      - run: echo '${{ toJSON(steps.setup_node_id.outputs) }}'
      - if: ${{ steps.setup_node_id.outputs.cache-hit != 'true' }}
        run: npm install
      - run: ls -a
      - run: npm ls -g --depth=0
      - run: npm ls --depth=0

ls -aでカレントディレクトリ内を見るとnode_modulesはやはり作成されていません。

もしやと思いnpm ls -g --depth=0でグローバルなnode_modulesの内容を見てみましたが、こちらにDependencyがリストアされているということはありませんでした。

global cache on the machineの意味するところがよく分かりませんが、ドキュメントの記述に情報が少なすぎると思いました。ちなみに今年2月に下記Issueがopenされており、同様のミスリーディングをした人が他にもいるようです。勘違いしちゃいますよね…。

node_modulesのキャッシュはactions/cacheを使おう

結論としては、node_modulesのキャッシュを行いたいならactions/cacheを使えば良さそうです。

actions/setup-nodeactions/cacheを組み合わせた次のWorkfrowを試してみます。

name: Caching with npm

on: push

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 14
      - uses: actions/cache@v3
        id: node_modules_cache_id
        env:
          cache-name: cache-node-modules
        with:
          path: '**/node_modules'
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
      - run: echo '${{ toJSON(steps.node_modules_cache_id.outputs) }}'
      - if: ${{ steps.node_modules_cache_id.outputs.cache-hit != 'true' }}
        run: npm install
      - run: npm ls --depth=0

まず初回のworkflow実行です。

  • [Run actions/setup-node@v3]でnpm cache is not foundとあり、キャッシュミスしていることが分かります。
  • "cache-hit": "false"であるため、[Run npm install]でconditionがtrueとなり、npm installが実行されています。
  • [Post Run actions/setup-node@v3]でCache saved successfullyとあり、キャッシュの保存が成功していることが分かります。

次に二回目のキャッシュを使用したworkflow実行です。

  • [Run actions/setup-node@v3]でCache restored successfullyとあり、キャッシュからのリストアが行われていることが分かります。
  • "cache-hit": "true"であるため、[Run npm install]でconditionがfalseとなり、npm installがスキップされています。
  • [Run npm ls --depth=0]で、キャッシュからnode_modulesへDependencyがリストアされていることが分かります。
  • [Post Run actions/setup-node@v3]でCache hit occurred on the primary key ..., not saving cache.とあり、キャッシュヒットしたためキャッシュの保存がスキップされていることが分かります。

おわりに

actions/setup-nodeを使用してWorkflowの実行時にnode_modulesをキャッシュできるのか試してみました。

node_modulesをキャッシュできる前提で検証に臨んでいたため、なぜ出来ないんだろうとデバッグに相当時間を要しましたが、そもそも出来ないようでした。今後の改善でactions/setup-nodeのみでnode_modulesにも対応できるようになると嬉しいですね。

参考

以上