CI/CDパイプラインが遅いと、開発フローが阻害されストレスが溜まります。特にnpmやpipの依存関係解決に毎回5分以上かかるのは、チーム全体の生産性を低下させる大きな要因です。
GitHub Actionsのキャッシュ機能を正しく設定すれば、ビルド時間を30〜50%削減できます。本記事では、実際のプロジェクトで使える実装パターンを、初心者から上級者まで網羅的に解説します。
結論:GitHub Actionsキャッシュを使うなら「actions/cache」と「setup-node」の組み合わせが最もコスパが良い
結論から言うと、npm・pip双方に対応した「actions/cache」と言語別セットアップアクションの組み合わせが、導入コスト・保守性・効果のバランスが最も優れています。無料で利用でき、5GBのキャッシュストレージが標準付属。ビルド時間を平均35%短縮でき、チーム全体で月あたり約50時間の削減効果が期待できます。
| キャッシュ戦略 | 対応言語 | セットアップ難度 | 高速化効果 | コスト |
|---|---|---|---|---|
| actions/cache + setup-node | Node.js・npm | ★☆☆☆☆ | 35〜45%削減 | 無料(5GB標準) |
| actions/cache + setup-python | Python・pip | ★★☆☆☆ | 30〜40%削減 | 無料(5GB標準) |
| GitHub提供の言語別キャッシュ | Ruby・Java・Go | ★☆☆☆☆ | 40〜50%削減 | 無料(5GB標準) |
| カスタム手書きキャッシュ | すべて対応 | ★★★★★ | 20〜30%削減 | 無料 |
| DependabotやPackerホストキャッシュ | フルスタック | ★★★☆☆ | 50%以上削減 | 有料(月$1,000〜) |
npm向けキャッシュ設定の実装方法
基本的なnpmキャッシュ:初心者向け最小構成
GitHub Actionsで最も一般的なNode.jsプロジェクト向けのキャッシュ設定です。以下のコードは、`node_modules`ディレクトリを`package-lock.json`のハッシュ値をキーにしてキャッシュします。
name: Build with npm cache
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run build
この設定の重要なポイントは、`setup-node`の`cache: ‘npm’`パラメータです。これを指定するだけで、GitHub ActionsがGlobal Cache内で自動的に`node_modules`をキャッシュしてくれます。初回実行時は約5分かかりますが、2回目以降は1分以下に短縮されます。
`npm ci`(クリーンインストール)を使うことが重要です。`npm install`ではなく`npm ci`を使うことで、ロック情報を完全に尊重し、再現性の高いビルドが保証されます。
複数バージョン対応・マトリックステスト向けnpmキャッシュ
Node.jsの複数バージョンでテストする場合は、各バージョン毎にキャッシュを分離する必要があります。以下のコードでは、バージョン毎に異なるキャッシュキーを生成しています。
name: Test multiple Node versions
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
Matrixでバージョンを指定すると、GitHub Actionsが自動的に`node-version`をキャッシュキーに含めてくれるため、追加の設定は不要です。結果として、Node.js 16、18、20それぞれの`node_modules`が別々のキャッシュスロットに保存されます。
Monorepoおよび複数package.json対応
複数のpackage.jsonを持つMonorepoプロジェクトでは、各パッケージのロックファイルをすべてキャッシュキーに含める必要があります。
name: Build monorepo
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: '18'
- uses: actions/cache@v3
id: cache
with:
path: |
~/.npm
node_modules
packages/*/node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- run: npm ci
- run: npm run build
重要な点は、`path`で複数のディレクトリをパイプで区切って指定していることです。さらに`hashFiles(‘**/package-lock.json’)`により、すべての package-lock.jsonファイルをハッシュ値に含めています。
pip向けキャッシュ設定の実装方法
基本的なpipキャッシュ:Pythonプロジェクト向け最小構成
Pythonプロジェクトではnpmと異なり、言語別セットアップアクション(`setup-python`)の自動キャッシュ機能が限定的なため、`actions/cache`を明示的に使う必要があります。
name: Build with pip cache
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- run: pip install -r requirements.txt
- run: python -m pytest
pipの場合、キャッシュパスは`~/.cache/pip`(Linux/macOS)または`~/AppData/Local/pip/Cache`(Windows)です。上記コードではLinux/macOS標準の`~/.cache/pip`を指定しています。
重要なのは、`requirements.txt`を直接ハッシュするのではなく、より詳細に管理したい場合は`requirements.lock`(Pipenv・Poetry使用時)をハッシュします。
Poetry・Pipenv対応の高度なpipキャッシュ
Poetryを使っている場合、ロックファイルは`poetry.lock`です。以下の設定でPoetryのキャッシュディレクトリも一緒に管理できます。
name: Build with Poetry cache
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Cache Poetry venv
uses: actions/cache@v3
with:
path: ~/.cache/pypoetry
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
${{ runner.os }}-poetry-
- run: pip install poetry
- run: poetry install
- run: poetry run pytest
Poetryを使う際のベストプラクティスは、`poetry install`の前に仮想環境をキャッシュすることです。初回実行時は約2分かかりますが、2回目以降は10秒程度に短縮されます。
複数Pythonバージョンテスト向けpipキャッシュ
npmと同様に、複数のPythonバージョンでテストする場合はバージョン毎にキャッシュを分離します。
name: Test multiple Python versions
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
${{ runner.os }}-pip-
- run: pip install -r requirements.txt
- run: pytest
キャッシュキーにバージョン情報を明示的に含めることで、Python 3.9と3.12の`site-packages`が混在しないようにしています。