1. Azure OpenAIを使用したプルリクエストの自動レビューの導入
今やChatGPTなどの生成AIを使用して開発をすることが当たり前になり、多くのエンジニアが生成AIを活用して効率的に業務を進めています。
一方で、セキュリティの観点から、業務に生成AIを使用することは制約を設けている企業も少なくありません。
生成AIを使用して業務の効率化を図りたいが、使用してはならないルールがある。そういった場合、Azure OpenAIを活用することで解決できる可能性があります。
Azure OpenAIとは、Microsoft社が提供する生成AIモデルを使用してカスタムコパイロットを構築するサービスのことであり、このサービスを使用して高水準のセキュリティでセキュアな環境を構築することができます。
当記事では、そのAzure OpenAIとGitHub Actionsを組み合わせてプルリクエストが作成(更新)された時に自動でレビューする仕組みを構築するための手順を紹介します。
なお、こちらの記事ではAzure OpenAI上でのサービス構築については取り扱いません。
2. 自動レビューの概要
ここでのプルリクエストの自動レビューの手順については以下のようなイメージとなります。
このフローでは、以下の手順で自動レビューが行われます。
-
プルリクエストの作成・更新: 開発者がコードの変更をプッシュし、プルリクエストを作成または更新します。
-
GitHub Actionsのトリガー: プルリクエストのイベントを検知して、GitHub Actionsが自動的に起動し、変更されたファイルの差分(diff)を取得します。
-
Azure OpenAIへのリクエスト: 差分情報をAzure OpenAIのAPIに送信し、コードレビューを依頼します。
-
レビュー結果の取得: Azure OpenAIからのレビュー結果を受け取ります。
-
プルリクエストへのコメント投稿: 取得したレビュー結果をプルリクエストのコメントとして投稿し、開発者に対して自動レビューの結果を通知します。
これにより、プルリクエストが作成・更新されるたびに、自動でプルリクエスト内に存在する差分に対するコードレビューが行われます。
3. GitHub Actionsの設定
-
リポジトリのページで、「Actions」タブをクリックします。
-
左側の「New workflow」ボタンをクリックします。
-
「set up a workflow yourself」をクリックします。
-
エディタに以下のスクリプトを入力します。
ここではプロジェクト内のSwiftファイルをレビューする仕様とします。name: ChatGPT Review and Comment on: pull_request: types: [opened, synchronize] jobs: review_and_comment: runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Checkout Repository with Full History uses: actions/checkout@v2 with: fetch-depth: 0 # フル履歴を取得 - name: Find Branch Point Commit id: find-base run: | # 現在のブランチ(HEAD)とベースブランチの分岐点を特定 BASE_COMMIT=$(git merge-base HEAD origin/${{ github.base_ref }}) echo "BASE_COMMIT=$BASE_COMMIT" >> $GITHUB_ENV - name: Checkout Base Branch at Branch Point run: | mkdir base git --work-tree=base checkout $BASE_COMMIT -- . - name: Checkout Head Branch (After Changes) run: | mkdir head git --work-tree=head checkout HEAD -- . - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y curl jq diffutils parallel - name: Generate Diffs for Each Swift File run: | mkdir -p diffs find head/ -type f -name "*.swift" | while read file; do base_file="base/${file#head/}" head_file="$file" if [ -f "$base_file" ]; then diff -u "$base_file" "$head_file" > "diffs/$(basename "$file").diff" || true else diff -u /dev/null "$head_file" > "diffs/$(basename "$file").diff" || true fi # 空のdiffファイルを削除 if [ ! -s "diffs/$(basename "$file").diff" ]; then rm "diffs/$(basename "$file").diff" fi done - name: Review Each Diff in Parallel env: GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESSTOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} OPENAI_ENDPOINT_URL: ${{ secrets.OPENAI_ENDPOINT_URL }} run: | PR_NUMBER=$(jq -r .pull_request.number $GITHUB_EVENT_PATH) PR_TITLE=$(jq -r .pull_request.title $GITHUB_EVENT_PATH) process_diff() { local PART=$1 local FILE_NAME=$(basename "$PART" .diff) PR_BODY=$(cat "$PART" | sed 's/"/\\"/g' | jq -Rs .) JSON_STRING=$(jq -n \ --arg title "$PR_TITLE" \ --arg body "$PR_BODY" \ '{messages: [ {role: "system", content: "あなたは優秀なテックリードです。最新のSwift,SwiftUIの基本的なコードの書き方は熟知しており、それらのコンポーネントやiOSアプリのアンチパターンなどに関しても世界でも有数の知見を持っています。 以下のルールに従ってコードレビューを行ってください。 レビューはプルリクエストが反映されたコードに対して行います。プルリクエストが反映される前のコードを見ることは一切禁じます。 以下で記載する修正前はプルリクエストが反映されたコードを指し、修正後はあなたがレビューを行い修正を反映したものです。 レビューの結果問題がなければそのファイルについては一切の記載を禁じます。 レビューは問題点に対してのみ行い、問題点のないファイルについては記載しないでください。 問題点がある場合には修正前と修正後のコードを提示し、どういった観点から修正すべきかを指摘しているかも含めて提示してください。 問題文の指摘がある場合、Markdown形式で折り畳みが行えるように指摘全体を<details></details>タグで囲み、<summary></summary>タグで指摘を行うファイル名を囲むようにしてください。 "}, {role: "user", content: ($title + "\n\n" + $body)} ], max_tokens: 1500}') # JSONデータをファイルに保存 echo "$JSON_STRING" > request.json RESPONSE=$(curl -X POST "$OPENAI_ENDPOINT_URL" \ -H "Content-Type: application/json" \ -H "api-key: $OPENAI_API_KEY" \ --data @request.json) REVIEW=$(echo "$RESPONSE" | jq -r .choices[0].message.content | sed 's/\n/<br>/g') if [ "$REVIEW" != "null" ] && [ -n "$REVIEW" ]; then echo "Review for $FILE_NAME" > "reviews/review_$(basename "$PART")" echo "$REVIEW" >> "reviews/review_$(basename "$PART")" fi } export -f process_diff mkdir -p reviews parallel process_diff ::: diffs/*.diff - name: Combine Reviews run: | echo "### Combined Review Comments<br>" > combined_reviews.md if [ -z "$(ls -A reviews)" ]; then echo "No reviews to combine<br>" >> combined_reviews.md else for review in reviews/*; do cat "$review" >> combined_reviews.md echo -e "<br>---<br>" >> combined_reviews.md done fi - name: Post Combined Review Comment env: GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESSTOKEN }} run: | PR_NUMBER=$(jq -r .pull_request.number $GITHUB_EVENT_PATH) COMBINED_REVIEW=$(cat combined_reviews.md) POST_DATA=$(jq -n --arg body "$COMBINED_REVIEW" '{"body": $body}') curl -X POST "https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "Content-Type: application/json" \ --data "$POST_DATA"
以下では、YAMLファイルで行われている主な処理を順番にまとめています。
(※ 実際のGitHub Actionsのステップ数とは必ずしも一致しませんが、全体の流れを4つに大きく分けて解説しています)
1. リポジトリのクローンとベースコミットの特定
フル履歴を取得(checkout)
- name: Checkout Repository with Full History
uses: actions/checkout@v2
with:
fetch-depth: 0
リポジトリの全履歴を取得して、後の差分比較が行えるようにします。
ブランチの分岐点(BASE_COMMIT)を特定
- name: Find Branch Point Commit
id: find-base
run: |
BASE_COMMIT=$(git merge-base HEAD origin/${{ github.base_ref }})
echo "BASE_COMMIT=$BASE_COMMIT" >> $GITHUB_ENV
git merge-base
で、Pull Requestのheadブランチ
とbaseブランチ
が分岐した共通のコミットを探し、そのコミットハッシュを環境変数として保存します。
2. ベースブランチとHEADブランチの状態をワークツリーに展開
ベースブランチの状態をフォルダに展開
- name: Checkout Base Branch at Branch Point
run: |
mkdir base
git --work-tree=base checkout $BASE_COMMIT -- .
分岐点(baseコミット)時点のファイルをbase
フォルダに展開して、差分取得時の「比較元」として利用します。
HEADブランチ(変更後)の状態をフォルダに展開
- name: Checkout Head Branch (After Changes)
run: |
mkdir head
git --work-tree=head checkout HEAD -- .
最新(HEAD)のファイルをhead
フォルダに展開して、差分取得時の「比較先」として利用します。
3. Swiftファイルの差分作成とAzure OpenAIへの送信
差分の生成
- name: Generate Diffs for Each Swift File
run: |
mkdir -p diffs
find head/ -type f -name "*.swift" | while read file; do
base_file="base/${file#head/}"
head_file="$file"
if [ -f "$base_file" ]; then
diff -u "$base_file" "$head_file" > "diffs/$(basename "$file").diff" || true
else
diff -u /dev/null "$head_file" > "diffs/$(basename "$file").diff" || true
fi
# 空のdiffファイルを削除
if [ ! -s "diffs/$(basename "$file").diff" ]; then
rm "diffs/$(basename "$file").diff"
fi
done
head
ディレクトリ内のすべての .swift
ファイルを対象に、diff
コマンドでベースとの差分を生成し、diffs
フォルダに保存します。ファイルが新規追加された場合は /dev/null
との差分を取得します。
仮にSwiftファイル
以外をレビューしたい場合にはそのファイルの拡張子を指定することで対象に含めることが可能です。
OpenAI APIを呼び出してレビューを並列実行
- name: Review Each Diff in Parallel
env:
GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESSTOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_ENDPOINT_URL: ${{ secrets.OPENAI_ENDPOINT_URL }}
run: |
PR_NUMBER=$(jq -r .pull_request.number $GITHUB_EVENT_PATH)
PR_TITLE=$(jq -r .pull_request.title $GITHUB_EVENT_PATH)
process_diff() {
local PART=$1
local FILE_NAME=$(basename "$PART" .diff)
PR_BODY=$(cat "$PART" | sed 's/"/\\"/g' | jq -Rs .)
JSON_STRING=$(jq -n \
--arg title "$PR_TITLE" \
--arg body "$PR_BODY" \
'{messages: [
{role: "system", content: "あなたは優秀なテックリードです。最新のSwift,SwiftUIの基本的なコードの書き方は熟知しており、それらのコンポーネントやiOSアプリのアンチパターンなどに関しても世界でも有数の知見を持っています。
以下のルールに従ってコードレビューを行ってください。
レビューはプルリクエストが反映されたコードに対して行います。プルリクエストが反映される前のコードを見ることは一切禁じます。
以下で記載する修正前はプルリクエストが反映されたコードを指し、修正後はあなたがレビューを行い修正を反映したものです。
レビューの結果問題がなければそのファイルについては一切の記載を禁じます。
レビューは問題点に対してのみ行い、問題点のないファイルについては記載しないでください。
問題点がある場合には修正前と修正後のコードを提示し、どういった観点から修正すべきかを指摘しているかも含めて提示してください。
問題文の指摘がある場合、Markdown形式で折り畳みが行えるように指摘全体を<details></details>タグで囲み、<summary></summary>タグで指摘を行うファイル名を囲むようにしてください。
"},
{role: "user", content: ($title + "\n\n" + $body)}
], max_tokens: 1500}')
# JSONデータをファイルに保存
echo "$JSON_STRING" > request.json
RESPONSE=$(curl -X POST "$OPENAI_ENDPOINT_URL" \
-H "Content-Type: application/json" \
-H "api-key: $OPENAI_API_KEY" \
--data @request.json)
REVIEW=$(echo "$RESPONSE" | jq -r .choices[0].message.content | sed 's/\n/<br>/g')
if [ "$REVIEW" != "null" ] && [ -n "$REVIEW" ]; then
echo "Review for $FILE_NAME" > "reviews/review_$(basename "$PART")"
echo "$REVIEW" >> "reviews/review_$(basename "$PART")"
fi
}
export -f process_diff
mkdir -p reviews
parallel process_diff ::: diffs/*.diff
ここでは、parallel
コマンドを使って .diff
ファイルを並列処理し、Azure OpenAI
に差分内容を送信してレビューを行っています。
同時に、レビュー観点をcontent
以降に記述することで独自のレビュー観点からの指摘が可能です。
そして、各差分ファイルごとにJSON
を作成した後、curl
でAPIを呼び出し、返ってきたレビュー結果をファイルに保存しています。
4. レビューコメントをまとめてPull Requestに投稿
レビュー結果を結合
- name: Combine Reviews
run: |
echo "### Combined Review Comments<br>" > combined_reviews.md
if [ -z "$(ls -A reviews)" ]; then
echo "No reviews to combine<br>" >> combined_reviews.md
else
for review in reviews/*; do
cat "$review" >> combined_reviews.md
echo -e "<br>---<br>" >> combined_reviews.md
done
fi
reviews
ディレクトリに保存された各ファイルごとのレビュー結果を一つにまとめて、combined_reviews.md
を作成します。
Pull Requestのコメントとして投稿
- name: Post Combined Review Comment
run: |
PR_NUMBER=$(jq -r .pull_request.number $GITHUB_EVENT_PATH)
COMBINED_REVIEW=$(cat combined_reviews.md)
POST_DATA=$(jq -n --arg body "$COMBINED_REVIEW" '{"body": $body}')
curl -X POST "https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
--data "$POST_DATA"
最後に、生成したレビューコメントをJSON
としてGitHub API
に送り、Pull Request
のコメント欄に投稿する、という流れになっています。
4. GitHub ActionsのSecret設定
yaml
ファイル内に記述されている
OPENAI_ENDPOINT_URL
OPENAI_API_KEY
PERSONAL_ACCESSTOKEN
に関しては、ファイル内にハードコードするのはセキュリティ的な観点からよろしくないため、上記の三点をGitHub Actions Secretとして登録します。
Secretの設定手順は以下の通りです。
1.リポジトリのトップページで、「Settings」タブをクリックします。
2.左側のメニューから「Secrets and variables」 > 「Actions」を選択します。
3.「New repository secret」ボタンをクリックし、以下のキーと値をそれぞれ追加します。
-
OPENAI_ENDPOINT_URL
: Azure OpenAIのAPIエンドポイントURL -
OPENAI_API_KEY
: Azure OpenAIのAPIキー -
PERSONAL_ACCESSTOKEN
: GitHubの個人用アクセス トークン
※GitHubの個人用アクセス トークンは、プルリクエストにコメントを投稿するために必要です。トークン作成時に、repo
権限を付与してください。
作成方法が分からない場合、こちらを参照してください。
5. 動作テスト
自動レビューの設定が完了したら、実際にプルリクエストを作成して動作を確認します。
-
新しいブランチを作成: リポジトリで新しいブランチを作成し、Swiftファイルを追加または変更します。
-
プルリクエストを作成: 変更をプッシュし、ベースブランチに対してプルリクエストを作成します。
-
ワークフローの実行確認: プルリクエストを作成すると、GitHub Actionsが自動的にトリガーされます。リポジトリの「Actions」タブからワークフローの実行状況を確認できます。
-
レビュー結果の確認: ワークフローが正常に完了すると、プルリクエストに自動レビューのコメントが投稿されます。
成功した場合、以下のようなコメントが自動的にプルリクエストのコメント欄に作成されます。
6. おわりに
以上で、Azure OpenAI
とGitHub Actions
を使用したプルリクエストの自動レビューの設定手順について解説しました。
この仕組みを導入することで、コードレビューの効率化や品質向上が期待できます。また、Azure OpenAI
を使用することで、企業内のセキュリティポリシーに準拠した形で生成AIを活用できます。
ぜひ、自社の開発フローに取り入れてみてください!