GitHub Actions で npm キャッシュを設定したら CI の待ち時間がかなり短くなった話

毎回 npm install を走らせていた CI ワークフローを actions/cache でキャッシュするようにしたところ、実行時間がかなり短縮されました。その設定と手順をまとめます。

🙌 結論から

GitHub Actions のワークフローで actions/cache を使って npm のキャッシュを設定したところ、毎回 2〜3 分かかっていた npm install が初回以外ほぼスキップされ、CI 全体の時間がかなり短縮されました✨

設定自体は数行追加するだけなので、ちょっとビルド系遅いな〜思ったらやってみる価値ありです!

package-lock.json が変わっていないプッシュなら、以降のインストールは 10〜20 秒程度で終わるようになりました(^o^)/

💡 なぜキャッシュが必要だったか

仕事で使っているプロジェクトでは、プッシュのたびに GitHub Actions でテストとビルドを回していました。

ワークフローのログを眺めていると、npm install だけで毎回 2〜3 分ほどかかっていることに気づきました(´・ω・`)

依存パッケージの内容がほとんど変わっていないのに、毎回フルでインストールするのは非効率…

package-lock.json が変わっていなければ、前回のインストール結果をそのまま使えるはず!

CI の待ち時間が長いと、プッシュのたびにブラウザで進捗を確認する手間が増えるのが割とストレスですよね…

👀 actions/cache の使い方

actions/cache を使うと、特定のディレクトリをキャッシュキーで保存・復元できます。

- name: Cache node_modules
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-

ポイントは keyhashFiles('**/package-lock.json') を使っていることです!

package-lock.json が変わらない限り同じキャッシュが使われ、変わった場合は新しいキャッシュが自動で作られます。

restore-keys は、完全一致するキャッシュがない場合の部分一致フォールバックです。

依存が少し変わった場合でも古いキャッシュをベースに差分だけインストールできるので、キャッシュ効果がより広い状況で出やすくなります。

✨ 適用してみた結果

キャッシュが効いた場合の npm install は、2〜3 分から 10〜20 秒程度まで短縮されました。

package-lock.json が変わっていないプッシュがほとんどなので、日常の CI はほぼキャッシュヒットする状態になっています。

一点、調べていて気づいたのですが、actions/setup-node アクション自体にも cache オプションが内蔵されています

- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

こちらでも同等のキャッシュが効くので、設定をシンプルにしたい場合はこちらの方が便利かもしれません。

私は最初 actions/cache を直接使っていましたが、後からこの setup-node のオプションに気づいて、今はこちらに切り替えています。

どちらの方法でも効果は変わらないので、プロジェクトの好みで選んで問題ないと思います。

👍 まとめ

actions/cache の設定は数行で完結します。

それだけで CI の待ち時間がかなり変わったので、設定コストに対して得られるリターンはかなり大きかったです!

setup-nodecache オプションを使えばさらにシンプルに書けるので、どちらか使いやすい方を試してみてください。

まだキャッシュを設定していないプロジェクトがあれば、ぜひ一度確認してみることをおすすめします(^^)