
この記事はSTORES Advent Calendar 2025の16日目の記事です。
こんにちは。Webエンジニアをしているotariidaeです。今月は調子に乗って3つも記事を書いています。
この記事では、先日開催されたSTORES Tech Conf 2025 “What Would You Do?”でのポスター発表「LGTM, Dependabot! 見てないけど」の内容をより具体例で解説していきます。
ポスターの内容のおさらい
ポスターの内容は下記で公開しています。
要約:Dependabotなどで依存ライブラリ更新のPRが自動作成されるようになったものの、コードレビューがボトルネックとなり停滞しがちです。 この問題を解決するため、レビューからマージまでのプロセスを一部自動化するワークフローを構築しました。 安全性を担保するための拠り所として、CI、セマンティックバージョニング、AIを採用しました。
具体例
ともかく上記のフローをGitHub Actionsで構築した具体例を次に示します。
on: pull_request: branches: - main types: [opened, synchronize, reopened] jobs: determine: runs-on: ubuntu-latest if: github.event.pull_request.user.login == 'dependabot[bot]' outputs: strategy: ${{ steps.determine.outputs.strategy }} steps: - uses: dependabot/fetch-metadata@v2 id: fetch-metadata - id: determine # パッチバージョンの更新 → 無条件で自動マージ有効 # マイナーバージョンの更新 → AIがapproveしたら自動マージ有効 # それ以外の更新 → AIがレビューコメントだけ投稿 run: | if [ "$UPDATE_TYPE" != "version-update:semver-patch" ]; then echo "strategy=merge-without-review" >> $GITHUB_OUTPUT elif [ "$UPDATE_TYPE" != "version-update:semver-minor" ]; then echo "strategy=merge-with-ai-review" >> $GITHUB_OUTPUT else echo "strategy=ai-review-comment-only" >> $GITHUB_OUTPUT fi env: UPDATE_TYPE: ${{ steps.fetch-metadata.outputs.update-type }} merge-without-review: runs-on: ubuntu-latest needs: determine if: needs.determine.outputs.strategy == 'merge-without-review' steps: - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ vars.BOT_APP_ID }} private-key: ${{ secrets.BOT_PRIVATE_KEY }} owner: heyinc repositories: example-repo - run: | gh pr review --approve "$PR_URL" gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{ github.event.pull_request.html_url }} GH_TOKEN: ${{ steps.app-token.outputs.token }} merge-with-ai-review: runs-on: ubuntu-latest needs: determine if: needs.determine.outputs.strategy == 'merge-with-ai-review' permissions: contents: write pull-requests: write checks: read id-token: write actions: read steps: - uses: actions/checkout@v5 - name: Generate GitHub App token id: claude-code-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.CLAUDE_CODE_APP_ID }} private-key: ${{ secrets.CLAUDE_CODE_APP_PRIVATE_KEY }} owner: heyinc repositories: | example-repo marccket - name: Configure AWS Credentials (OIDC) uses: aws-actions/configure-aws-credentials@v5 with: role-to-assume: ${{ vars.CLAUDE_CODE_ROLE_TO_ASSUME }} role-session-name: auto-merge aws-region: ${{ env.AWS_REGION }} - uses: anthropics/claude-code-action@v1 with: claude_args: | --model global.anthropic.claude-sonnet-4-5-20250929-v1:0 use_bedrock: "true" use_commit_signing: "true" github_token: ${{ steps.claude-code-app-token.outputs.token }} allowed_bots: "*" additional_permissions: | actions: read plugin_marketplaces: "https://x-access-token:${{ steps.claude-code-app-token.outputs.token }}@github.com/heyinc/marccket.git" plugins: "dependabot@marccket" prompt: | /dependabot:lgtcc-dependabot ${{ github.repository }} ${{ github.event.pull_request.number }} - name: check if AI approved id: check-ai-approved run: | APPROVED=$(gh pr view ${{ github.event.pull_request.number }} --json reviews \ --jq '.reviews | any(.state == "APPROVED" and .author.login == "stinc-claude-code-action")') echo "approved=$APPROVED" >> $GITHUB_OUTPUT env: GH_TOKEN: ${{ github.token }} - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ vars.BOT_APP_ID }} private-key: ${{ secrets.BOT_PRIVATE_KEY }} owner: heyinc repositories: example-repo - name: merge if AI approved id: merge if: steps.check-ai-approved.outputs.approved == 'true' run: | gh pr merge --auto --merge "$PR_URL" echo "merged=true" >> $GITHUB_OUTPUT env: PR_URL: ${{ github.event.pull_request.html_url }} GH_TOKEN: ${{ steps.app-token.outputs.token }} ai-review-comment-only: runs-on: ubuntu-latest needs: determine if: needs.determine.outputs.strategy == 'ai-review-comment-only' permissions: contents: write pull-requests: write checks: read id-token: write actions: read steps: - uses: actions/checkout@v5 - name: Generate GitHub App token id: claude-code-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.CLAUDE_CODE_APP_ID }} private-key: ${{ secrets.CLAUDE_CODE_APP_PRIVATE_KEY }} owner: heyinc repositories: | example-repo marccket - name: Configure AWS Credentials (OIDC) uses: aws-actions/configure-aws-credentials@v5 with: role-to-assume: ${{ vars.CLAUDE_CODE_ROLE_TO_ASSUME }} role-session-name: auto-merge aws-region: ${{ env.AWS_REGION }} - uses: anthropics/claude-code-action@v1 with: claude_args: | --model global.anthropic.claude-haiku-4-5-20251001-v1:0 use_bedrock: "true" use_commit_signing: "true" github_token: ${{ steps.claude-code-app-token.outputs.token }} allowed_bots: "*" additional_permissions: | actions: read plugin_marketplaces: "https://x-access-token:${{ steps.claude-code-app-token.outputs.token }}@github.com/heyinc/marccket.git" plugins: "dependabot@marccket" prompt: | /dependabot:review-dependabot ${{ github.repository }} ${{ github.event.pull_request.number }}
このワークフローが実行されると、セマンティックバージョニングに基づき軽微な変更もしくはAIがレビューして承認したものは自動マージが有効になります。あとはリポジトリに設定してあるブランチ保護ルールやルールセットで規定されたCI成功などの条件を満たせば勝手にマージされていくという算段です。
ジョブの構成
最初のdetermineジョブで更新のメタデータを見たうえでどのフローに進むか判定し、各フローごとにジョブを定義しています。
ステップごとのif文とかで1ジョブにまとめることも可能ではありますが、ワークフロー全体の見通しの良さを重視してジョブで分けることにしました。
determineジョブの判定基準
上記の例ではシンプルにupdate-typeだけで判定していますが、実際にはdependency-typeやpackage-ecosystemなども考慮してリポジトリの特性や取れるリスク感に合わせた判定基準にカスタマイズすると良いと思います。
Claude Codeのプラグインマーケットプレイスの利用
このワークフローを複数リポジトリで利用することを踏まえ、AIにレビューさせるためのプロンプトを一元的に管理するためにClaude Codeのプラグインマーケットプレイスを作成しました。marccket*1というのがそれです。
社内用のプライベートリポジトリなので、claude-code-actionの引数で次のように認証情報も指定する必要がありました。
plugin_marketplaces: "https://x-access-token:${{ steps.claude-code-app-token.outputs.token }}@github.com/heyinc/marccket.git"
dependabotというプラグインのなかにreview-dependabot, lgtcc-dependabot*2というカスタムスラッシュコマンドを実装しており、リポジトリ名とPR番号を渡すとレビューしてくれるようにしています。
Claude Code Actionでapproveされたかどうか判定する
Claude Code Actionのstructured outputsも使えますが、愚直にghコマンド+jq芸で判定することにしました。
APPROVED=$(gh pr view ${{ github.event.pull_request.number }} --json reviews \
--jq '.reviews | any(.state == "APPROVED" and .author.login == "stinc-claude-code-action")')
その他Claude Code Actionの設定など
コストを追跡しやすくするためにrole-session-nameを付けたり、自動マージに直結しないレビューコメントのみならSonnetではなくHaikuを使ったり、CIの結果を読ませるためにadditional_permissionsを加えたり、もろもろの設定をしています。
まとめと今後の課題
条件付きでAIのレビュー判断を信じることで、ポスター発表でも記載した通り、このワークフローはSTORES内の複数のリポジトリで活用され累計100件以上のPRをすでにマージした実績をあげてくれています。今のところライブラリ更新起因での障害は発生していません。
一方でまだまだ課題を感じる部分もあります。自動でマージまで至らずに人間の判断に任されたPRはやはり滞留するという点です。特定のシステム専任の開発チームがおらず、プロジェクトカットのエンジニア組織構成を採用しているSTORESでは、やはりなかなかこのようなメンテナンスの類は優先度が上がりません。DependabotのPRには同時に存在する数の上限があるので、人間の判断が滞留するとPRの新陳代謝が起こらずに自動マージも動きません。現状からより一歩進めて、さらに人間に依存することなく安全かつスムーズにメンテナンス業務を回す仕組みをつくりたいなと思っているところです。
みなさんのDependabot PR捌きの参考になれば幸いです。