キャッシュとアーティファクト — CIを高速化し、成果物を受け渡す
この章の目次開く
第5章で学んだ通り、ランナーは毎回まっさらです。つまり何もしなければ、CIのたびに依存パッケージを全部ダウンロードし直すことになります。この「毎回ゼロから」を補うのがキャッシュとアーティファクトです。
2つは似ているようで役割がまったく違います。先に対比を頭に入れてしまいましょう。
| キャッシュ | アーティファクト | |
|---|---|---|
| 目的 | 高速化(作り直せるものを取っておく) | 受け渡し・保存(成果物そのもの) |
| 例 | npmのダウンロードキャッシュ、Next.jsのビルドキャッシュ | ビルド成果物、テストレポート、スクリーンショット |
| 消えたら | 遅くなるだけ(また作れる) | 困る(その実行の成果が失われる) |
| 主な行き先 | 次回以降の実行 | 同じ実行内の別ジョブ、または人間がダウンロード |
学習者なるほど、キャッシュは「速くするための使い回し」で、アーティファクトは「持ち出したい荷物」なんだ。
その理解で完璧です。それぞれ見ていきます。
キャッシュ①: setup-nodeの組み込みキャッシュ
実は前章のワークフローには、すでにキャッシュが入っていました。
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm # ← これcache: npm の1行で、npmのダウンロードキャッシュ(~/.npm)が実行をまたいで保存・復元されます。パッケージをレジストリから再ダウンロードする時間が消えるので、npm ci が体感で数十秒速くなることも珍しくありません。
ポイントは、キャッシュの鍵(キー)がpackage-lock.jsonのハッシュから自動生成されることです。
- lockファイルが同じ → キャッシュがヒットして高速
- lockファイルが変わった(依存を追加した等) → キャッシュを作り直す
依存関係が変わればキャッシュも自動で切り替わるので、「古いキャッシュのせいで変な状態になる」ことは起きにくい設計になっています。
キャッシュ②: actions/cache で任意のディレクトリをキャッシュする
npm以外のものをキャッシュしたいときは、汎用の actions/cache を使います。代表例がNext.jsのビルドキャッシュ(.next/cache)です。
- name: Next.jsのビルドキャッシュ
uses: actions/cache@v4
with:
path: .next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-${{ hashFiles('**/*.ts', '**/*.tsx') }}
restore-keys: |
nextjs-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-初見だと呪文に見えるので、部品を分解します。
| 項目 | 意味 |
|---|---|
path | キャッシュするディレクトリ |
key | キャッシュの識別子。完全一致したら復元される |
restore-keys | keyが一致しなかったときの前方一致のフォールバック |
hashFiles(...) | 指定ファイル群のハッシュ値を計算する関数 |
キーの設計思想はこうです。
nextjs-Linux-<lockのハッシュ>-<ソースのハッシュ>
│ │ │ │
│ │ │ └ ソースが変わるたびに新キャッシュを保存
│ │ └ 依存が変わったら古いキャッシュは使わない
│ └ OSが違うキャッシュは混ぜない
└ 何のキャッシュかの名前空間
ソースコードは毎回変わるので key の完全一致はほぼ起きません。そこで restore-keys の前方一致により、「同じ依存関係で作った一番新しいキャッシュ」を復元して差分だけビルドする——という動きになります。

キャッシュの落とし穴
便利な一方で、キャッシュには知っておくべき性質があります。
- 合計10GBの上限(リポジトリごと)。超えると古いものから削除される
- 7日間アクセスがないと自動削除される
- 「キャッシュのせいで挙動がおかしい」疑惑が出たら、Actionsタブの Caches 画面から手動削除できる
- キャッシュはあくまで高速化。キャッシュがなくても成功するワークフローであること(キャッシュ前提の設計にしない)
詳細は依存関係をキャッシュしてワークフローを高速化する(公式ドキュメント)を参照してください。
アーティファクト: 成果物を保存・受け渡しする
次はアーティファクトです。ユースケースは2つあります。
① 実行後にファイルをダウンロードしたい
テストのカバレッジレポートやE2Eテストの失敗時スクリーンショットなど、「CIが作ったファイルを後から人間が見たい」ケースです。actions/upload-artifact でアップロードします。
- name: テストを実行する
run: npm run test -- --coverage
- name: カバレッジレポートを保存する
uses: actions/upload-artifact@v4
if: always() # テストが落ちてもレポートは保存する
with:
name: coverage-report
path: coverage/
retention-days: 7 # 保存期間(デフォルト90日)アップロードされたファイルは、Actionsタブの実行ページ下部からzipでダウンロードできます。if: always() は第4章で学んだステータス関数の実践例で、「テストが失敗したときこそレポートを見たい」ので付けています。
② ジョブ間でファイルを受け渡したい
第5章で「ジョブは別マシンなのでファイルを共有できない」と学びました。ビルド成果物を後続ジョブで使いたいときは、アーティファクト経由で受け渡します。
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: npm }
- run: npm ci
- run: npm run build
- name: ビルド成果物をアップロードする
uses: actions/upload-artifact@v4
with:
name: build-output
path: .next/
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- name: ビルド成果物をダウンロードする
uses: actions/download-artifact@v4
with:
name: build-output
path: .next/
- run: echo "この .next/ をデプロイする(第9章で実践)"upload-artifact で名前を付けて預け、後続ジョブが download-artifact で同じ名前を指定して受け取る——荷物の一時預かりのイメージです。「ビルドは1回だけ、デプロイはその成果物を使う」という構成は、テストしたものと同じ成果物を本番に出すという意味で重要なプラクティスです。
メンターデプロイ直前にもう一度ビルドし直す構成だと、「テストで検証したビルド」と「本番に出たビルド」が別物になります。アーティファクトで同一の成果物を運ぶ——地味ですが、信頼できるデリバリーの基本ですよ。
キャッシュとアーティファクトの使い分け・再確認
この章の冒頭の表に、判断基準を1つ足して締めくくります。
「消えても作り直せるならキャッシュ、消えたら困るならアーティファクト」
- node_modulesの元データ → 作り直せる → キャッシュ
- .next/cache → 作り直せる(遅くなるだけ) → キャッシュ
- 本番に出すビルド成果物 → その実行の成果そのもの → アーティファクト
- テストレポート → その実行の記録 → アーティファクト
まとめ
- ランナーは毎回まっさら。キャッシュで「作り直せるもの」を持ち越して高速化する
- npmは
setup-nodeのcache: npmだけでOK。lockファイルのハッシュで自動管理される - 任意のディレクトリは
actions/cache。キーは「名前空間-OS-依存ハッシュ-ソースハッシュ」+restore-keysの前方一致が定石 - アーティファクトは成果物の保存と受け渡し。
upload-artifact/download-artifactのペアで使う - ビルドは1回、デプロイはその成果物を——が信頼できるパイプラインの基本形
次章では、ジョブ定義の重複問題に取り組みます。複合アクションとReusable Workflowで、ワークフローをDRYに保つ方法です。