Skip to content

Commit 9ffa46a

Browse files
committed
GitHub Actionsで利用できるGeminiを利用したPRレビュースクリプトを作ってみた
1 parent 3f088c3 commit 9ffa46a

File tree

4 files changed

+236
-2
lines changed

4 files changed

+236
-2
lines changed

source/_posts/20250707a_AI_Tips連載はじめます.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ lede: "生成AI周りの進化の速さにワクワクが止まらない方も
4848
| 7/22(火)| 村田 靖拓 | [生成AIとはじめる、バックオフィス業務改革 ~Geminiの有効な使い方~](/articles/20250722a/) |
4949
| 7/23(水)| 柴田 健太 | [わかりやすいADKのAPIリクエストツール作成](/articles/20250723a/) |
5050
| 7/24(木)| 大前 七奈 | [PDFをBigqueryにぶっこんで効率よく構造化する](/articles/20250724a/) |
51-
| 7/25(金)| 清水 雄一郎 | 生成AIでGitLabのMerge Requestコメントを分析し、レビュー観点を抽出してみた |
51+
| 7/25(金)| 片岡 久人 | [GitHub Actionsで利用できるGeminiを利用したPRレビュースクリプトを作ってみた。](/articles/20250725a/) |
5252
| - | - | - |
53-
| 7/28(月)| 片岡 久人 | (TODO) |
53+
| 7/28(月)| 清水 雄一郎 | 生成AIでGitLabのMerge Requestコメントを分析し、レビュー観点を抽出してみた |
5454
| 7/29(火)| 橋本 竜我 | (TODO) |
5555

5656
# さいごに
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
---
2+
title: "GitHub Actionsで利用できるGeminiを利用したPRレビュースクリプトを作ってみた。"
3+
date: 2025/07/25 00:00:00
4+
postid: a
5+
tag:
6+
- Gemini
7+
- GitHub
8+
- Python
9+
- コードレビュー
10+
category:
11+
- Programming
12+
thumbnail: /images/20250725a/thumbnail.png
13+
author: 片岡久人
14+
lede: "GitHub Actionsを利用して、CI/CDパイプライン上で実行可能なPRのAIレビュー用スクリプトを紹介します。"
15+
---
16+
17+
[AI Tips連載](/articles/20250707a/)の10日目の記事です。
18+
19+
# はじめに
20+
21+
製造エネルギー事業部の片岡です。
22+
23+
AIに関連する連載ということで、今回はプルリクエスト(PR)のAIレビュースクリプトについてご紹介します。
24+
25+
コードレビューはソフトウェア開発において品質を担保する上で不可欠なプロセスですが、手動でのレビューは時間と労力がかかります。近年、LLM(大規模言語モデル)の普及に伴い、AIを活用したコードレビューが注目されています。
26+
本記事では、GitHub Actionsを利用して、CI/CDパイプライン上で実行可能なPRのAIレビュー用スクリプトを紹介します。
27+
28+
GitHub CopilotにもAIレビュー機能は実装されていますが、現状ではCI/CDパイプラインへの直接的な組み込みが難しく、コーディングガイドラインのカスタム機能も有料プラン(Enterprise)でしか利用できません。
29+
30+
チーム開発でAIレビューを本格的に活用するには、これらの点が課題となります。
31+
32+
- [Using GitHub Copilot code review - GitHub Docs](https://docs.github.com/ja/copilot/how-tos/agents/copilot-code-review/using-copilot-code-review)
33+
34+
このような課題を解決するため、GitHub ActionsでGeminiを動かし、CI/CDパイプライン上で実行可能なPRのAIレビュー用スクリプトをご紹介します。
35+
36+
# 前提
37+
38+
本記事では下記の記事を参考に、Github Actions&VertexAIの構成を踏襲し、下記の記事では具体的に紹介されていなかった、**Github Actionsで実行する「Geminiにレビューを依頼し、レビュー結果をGithubのPRに反映する」スクリプトの部分について紹介します。**
39+
40+
- [GoogleのLLM「Gemini」でコードレビューをするGitHub Actionsを自力で構築してみた - NTT docomo Business Engineers' Blog](https://engineers.ntt.com/entry/202503-gemini-cicd-code-review/entry)
41+
42+
<div class="note info" style="background: #e5f8e2; padding:16px; margin:24px 12px; border-radius:8px;"><span class="fa fa-fw fa-check-circle"></span>
43+
Github ActionsのWorkflow定義やプロンプトの実装に関しては↑の記事を参照してください。
44+
</div>
45+
46+
# PRへのAIレビュースクリプト
47+
48+
早速ですが、Gemini APIとGitHub APIを連携させてPRに対してレビューをする実装の例を以下に示します。
49+
50+
```py
51+
import json
52+
import os
53+
import sys
54+
55+
from github import Github, PullRequest
56+
from google import genai
57+
58+
# --- プロンプトファイルのパス ---
59+
# プロンプトファイルが格納されているディレクトリ
60+
PROMPT_DIR = "<your_directory_name>" # ★あなたのディレクトリ名を記載してください。
61+
REVIEW_PROMPT_FILE = os.path.join(PROMPT_DIR, "<your_prompt_file_name>") # ★あなたのプロンプトのファイル名を記載してください。
62+
63+
64+
65+
def load_prompt_from_file(filepath: str) -> str:
66+
"""指定されたファイルパスからプロンプトを読み込む。"""
67+
try:
68+
with open(filepath, 'r', encoding='utf-8') as f:
69+
return f.read()
70+
except FileNotFoundError:
71+
raise FileNotFoundError(f"プロンプトファイルが見つかりません: {filepath}")
72+
except Exception as e:
73+
raise IOError(f"プロンプトファイルの読み込み中にエラーが発生しました: {filepath} - {e}")
74+
75+
def get_pull_request(github_client: Github) -> PullRequest.PullRequest:
76+
"""GitHub Actionsのコンテキストからプルリクエスト情報を取得する"""
77+
event_path = os.getenv('GITHUB_EVENT_PATH')
78+
if not event_path:
79+
raise ValueError("GITHUB_EVENT_PATH環境変数が設定されていません。")
80+
81+
try:
82+
with open(event_path, 'r') as f:
83+
event = json.load(f)
84+
85+
pull_request_number = event['pull_request']['number']
86+
repo_name = event['repository']['full_name']
87+
88+
repo = github_client.get_repo(repo_name)
89+
return repo.get_pull(pull_request_number)
90+
91+
except Exception as e:
92+
print(f"GitHub Actionsイベント情報の取得に失敗しました: {e}")
93+
raise
94+
95+
# memo: ローカル実行用。get_pullのIDを変更して利用する。
96+
# repo = github_client.get_repo("<your-repository-name>")
97+
# # プルリクエストオブジェクトを取得
98+
# return repo.get_pull(1737)
99+
100+
def def run_summary_mode(pr: PullRequest.PullRequest, gemini_client: genai.Client,
101+
# 本記事では割愛
102+
103+
def run_review_mode(pr: PullRequest.PullRequest, gemini_client: genai.Client, prompt_template: str) -> None:
104+
"""
105+
各ファイルのコード差分をGeminiでレビューし、コメントする。
106+
"""
107+
for file in pr.get_files():
108+
if file.patch:
109+
print(f" > ファイル {file.filename} をレビュー中...")
110+
try:
111+
# Geminiでレビューコメントを生成
112+
review_prompt = prompt_template.format(file_diff_placeholder=file.patch)
113+
response = gemini_client.models.generate_content(
114+
model="gemini-2.0-flash",
115+
contents=review_prompt,
116+
config={"response_mime_type": "application/json"}
117+
)
118+
comments = json.loads(response.text)
119+
120+
# GitHubにコメントを投稿
121+
if not comments:
122+
print(f"ファイル {file.filename} に指摘事項はありませんでした。")
123+
continue
124+
125+
for comment_data in comments:
126+
start_line = comment_data.get("start_line")
127+
severity = comment_data.get("severity")
128+
comment_text = comment_data.get("comment")
129+
if start_line and comment_text:
130+
pr.create_review_comment(
131+
body=f"[{severity}] {comment_text}",
132+
commit=pr.get_commits().reversed[0],
133+
path=file.filename,
134+
line=start_line
135+
)
136+
print(f"ファイル {file.filename} の行 {start_line} にコメントしました。")
137+
else:
138+
print(f"警告: Geminiからのレビューコメント形式が不正です: {comment_data}")
139+
140+
except Exception as e:
141+
print(f" > エラー: ファイル {file.filename} のレビュー中に問題が発生しました: {e}")
142+
else:
143+
print(f" > ファイル {file.filename} に変更がないためスキップします。")
144+
print("コードレビューが完了しました。")
145+
146+
147+
def main() -> None:
148+
"""スクリプトのメイン実行ロジック。"""
149+
150+
# 1. コマンドライン引数の解析
151+
if len(sys.argv) < 2 or sys.argv[1] not in ["review", "summary"]:
152+
print("Usage: python gemini_pr_tools.py [review|summary]", file=sys.stderr)
153+
sys.exit(1)
154+
155+
mode = sys.argv[1]
156+
157+
# 2. 環境変数の取得と検証
158+
github_token = os.getenv('GITHUB_TOKEN')
159+
project_id = os.getenv('PROJECT_ID')
160+
161+
if not all([github_token, project_id]):
162+
print("必要な環境変数が設定されていません: GITHUB_TOKEN, PROJECT_ID", file=sys.stderr)
163+
sys.exit(1)
164+
165+
try:
166+
# 3. クライアントの初期化
167+
github_client = Github(github_token)
168+
gemini_client = genai.Client(
169+
vertexai=True,
170+
project=project_id,
171+
location="global"
172+
)
173+
174+
# 4. プロンプトの読み込み
175+
if mode == "review":
176+
prompt_template = load_prompt_from_file(REVIEW_PROMPT_FILE)
177+
print("レビューモードで実行します。")
178+
else: mode == "summary"
179+
prompt_template = load_prompt_from_file(SUMMARY_PROMPT_FILE)
180+
print("概要モードで実行します。")
181+
182+
# 5. プルリクエスト情報の取得
183+
pr = get_pull_request(github_client)
184+
print(f"プルリクエスト #{pr.number} の操作を開始します。")
185+
186+
# 6. モードに応じて処理を実行
187+
if mode == "review":
188+
run_review_mode(pr, gemini_client, prompt_template)
189+
elif mode == "summary":
190+
run_summary_mode(pr, gemini_client, prompt_template)
191+
192+
except Exception as e:
193+
print(f"致命的なエラーが発生しました: {e}", file=sys.stderr)
194+
sys.exit(1)
195+
196+
197+
if __name__ == "__main__":
198+
main()
199+
```
200+
201+
上記のPythonスクリプトによるAIレビューの実装の主要なポイントは以下の通りです。
202+
203+
1. Geminiによるコード差分レビュー
204+
- `pr.get_files()`でプルリクエストに含まれる各ファイルを取得し、file.patch を使用して、そのファイルの変更点(差分情報)を取得します。
205+
- `file.patch` はGitの差分形式で、+ で追加された行、- で削除された行を示す情報です。これをAIへの入力として利用することで、変更された箇所に絞ったレビューが可能になります。
206+
- 取得したコード差分をプロンプトテンプレートに埋め込み、Gemini APIに送信します。`config={"response_mime_type": "application/json"}` を指定することで、レビューコメントをJSON形式で受け取り、後の処理を容易にしています。
207+
2. GitHubへのレビューコメント投稿
208+
- Geminiが生成したレビューコメント(JSON形式)を解析し、start_line (指摘箇所の開始行)、severity (重要度)、comment (具体的なコメント内容) などの情報を取り出します。
209+
- これらの情報を用いて、pr.create_review_comment() メソッドを通じて、GitHubのプルリクエストの該当行にAIによるレビューコメントを自動的に投稿します。
210+
211+
<div class="note warn" style="background: #fdf9e2; padding:16px; margin:24px 12px; border-radius:8px;"><span class="fa fa-fw fa-check-circle"></span>
212+
213+
- GITHUB_TOKENは、workflow定義にて、`GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}`を環境変数に追加してください。
214+
- gemini apiを利用するにあたっては、workflow定義にて、サービスアカウント認証が必要になります。
215+
216+
</div>
217+
218+
# スクリプトの実行結果
219+
220+
このスクリプトに関するPRを作成し、スクリプトを実行したところ、
221+
下記のようにPRのコード差分に対してレビューコメントが記載されることが確認できました。
222+
223+
<img src="/images/20250725a/image.png" alt="image.png" width="833" height="424" loading="lazy">
224+
225+
# おわりに
226+
227+
GitHub ActionsとGemini APIを連携させることで、プルリクエストを自動でAIレビューするスクリプトについて紹介しました。
228+
229+
今後は、Geminiに渡すプロンプトをさらに改善、AIレビューを実施するタイミングを適正化などを実施すると、よりAIレビューが品質の強化に寄与できるものとなると思います。
230+
231+
# 参考(利用ライブラリ)
232+
233+
- [pygithub](https://pypi.org/project/PyGithub/)
234+
- [google-genai](https://pypi.org/project/google-genai/)

source/images/20250725a/image.png

43.3 KB
Loading
20.4 KB
Loading

0 commit comments

Comments
 (0)