GitHub Actionsで差分があるサービスのみビルドする仕組み
概要¶
モノレポ構成で複数サービスがある場合、変更のないサービスまでビルドするのは時間・コストの無駄。 差分のあるサービスだけをビルドする仕組みをGitHub Actionsで実装した。
実装の考え方¶
ステップ1: Dockerfileがあるディレクトリを抽出¶
# リポジトリ内のすべてのDockerfileを探してサービス名リストを作る
find . -name "Dockerfile" | sed 's|/Dockerfile||' | sed 's|^./||'
# 出力例: services/api, services/worker, services/frontend
ステップ2: git diffで変更ファイルを取得¶
# mainブランチとの差分ファイル一覧を取得
git diff --name-only origin/main HEAD
# 出力例:
# services/api/main.go
# services/api/handler.go
# README.md
ステップ3: ビルド対象を判定¶
差分ファイルのパスにサービスディレクトリが含まれているかを照らし合わせる。
# services/api に変更があればビルド対象に追加
for service in $(find . -name "Dockerfile" -exec dirname {} \;); do
if git diff --name-only origin/main HEAD | grep -q "^${service}/"; then
echo "$service" >> build_targets.txt
fi
done
GitHub ActionsのYAML例¶
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
services: ${{ steps.detect.outputs.services }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # git diffのために全履歴が必要
- id: detect
run: |
SERVICES=$(find . -name "Dockerfile" -exec dirname {} \; | while read dir; do
if git diff --name-only origin/main HEAD | grep -q "^${dir}/"; then
echo $dir
fi
done | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "services=$SERVICES" >> $GITHUB_OUTPUT
build:
needs: detect-changes
if: needs.detect-changes.outputs.services != '[]'
strategy:
matrix:
service: ${{ fromJson(needs.detect-changes.outputs.services) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t ${{ matrix.service }} ${{ matrix.service }}
ポイント¶
fetch-depth: 0を忘れると git diff が使えない(浅いクローンはコミット履歴がないため)- Dockerfileの場所とサービスのルートディレクトリが一致している構成が前提
- matrix strategyを使うことで差分サービスを並列ビルドできる