今回やること
この記事では、GitHub ActionsのCIを速くする基本を整理します。
CI高速化は、キャッシュ、並列化、不要な実行の削減、重複installの削減から始めると効果が出やすいです。
最初から高度なことをしなくても、設定を少し変えるだけで待ち時間はかなり減ります。
まず時間を測る
高速化の前に、どこで時間がかかっているかを見ます。
見る場所:
- GitHub Actionsのjob一覧
- 各stepの実行時間
- install時間
- lint/test/buildの時間
- queue時間
- 失敗してretryしているstep
「なんとなく遅い」ではなく、どのstepが遅いかを特定します。
setup-nodeのcacheを使う
Node.jsプロジェクトでは、依存関係のインストールが重くなりがちです。
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
cache: npm を使うと、npmのキャッシュを再利用できます。node_modules を直接キャッシュするより、npm ci とnpmキャッシュを組み合わせる方が安定しやすいです。
pnpmなら次のようにします。
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
jobを分けて並列化する
1つのjobで全部を直列にすると、合計時間がそのまま待ち時間になります。
jobs:
ci:
runs-on: ubuntu-latest
steps:
- run: npm run lint
- run: npm test
- run: npm run build
独立しているなら、jobを分けます。
jobs:
lint:
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 lint
test:
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 test
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
runnerの起動やinstallが増えるため、必ず速くなるとは限りません。ただし、lint/test/buildが重い場合は並列化の効果が出やすいです。
古いCIをキャンセルする
PRに何度もpushすると、古いcommitのCIが走り続けることがあります。
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
これを入れると、同じブランチで新しいCIが始まったときに古い実行をキャンセルできます。
PR修正が多いプロジェクトでは効果があります。
pathsで不要なCIを止める
ドキュメントだけの変更でフルCIを回す必要がない場合があります。
on:
pull_request:
paths:
- "src/**"
- "package.json"
- "package-lock.json"
- "tsconfig.json"
- ".github/workflows/ci.yml"
ただし、除外しすぎると必要なCIが動かない危険があります。設定ファイルやビルドに影響するファイルは忘れずに含めます。
installの重複を減らす
jobを分けると、各jobで npm ci が走ります。これは分かりやすい一方、install時間が増えます。
選択肢:
- cacheを効かせて各jobでinstallする
- 1つのjob内でstepを直列にする
- build成果物だけartifactで渡す
- monorepoなら変更パッケージだけ実行する
小さなプロジェクトでは、1job直列の方が速いこともあります。大きくなったら分割します。
artifactを使う
build成果物を後続jobで使うなら、artifactを使います。
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
後続job:
- name: Download build
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
同じbuildを何度もやり直すより、成果物を渡した方が速く安定することがあります。
変更ファイルだけlintする
CI全体では全件lintが必要な場面もありますが、pre-commitや軽いPRチェックでは変更ファイルだけに絞ると速くなります。
例:
npx eslint $(git diff --name-only --diff-filter=ACMR origin/main...HEAD | grep -E '\\.(ts|tsx|js|jsx)$')
ただし、shellで複雑に書きすぎると保守しにくくなります。lint-stagedやLefthookなどのツールを使う方が扱いやすいことが多いです。
高速化のbefore/afterを書く
改善したら、READMEやPRに記録します。
## CI高速化
変更前:
- CI合計: 約8分
- install: 約2分
- lint/test/buildを1jobで直列実行
変更後:
- CI合計: 約4分
- setup-node cacheを追加
- concurrencyで古いCIをキャンセル
- lint/test/buildを分割
転職用ポートフォリオでは、この記録自体がアピールになります。
まとめ
GitHub Actionsの高速化は、まず基本からで十分です。
- stepごとの時間を測る
- setup-nodeのcacheを使う
- 独立した処理を並列化する
- concurrencyで古いCIをキャンセルする
- pathsで不要なCIを減らす
- installの重複を見直す
- artifactで成果物を渡す
- before/afterを記録する
CIは毎日使う開発基盤です。少し速くするだけでも、チーム全体には大きく効きます。