1. はじめに
会社では、PRを出すとAIが自動でコードレビューをしてくれます。初めて見たときは「なにこれ、すごい!」と思いました。
ところが、前の記事でCI/CDを勉強してからワークフローのファイルを見たら、なんと読めたんです。すごいと思っていたものも、基本は前回勉強したことと同じでした。
そうなると気になるのが、「これ、本当にどこまで見てくれるの?」です。これはもう、自分で試すしかありません。
PRを出すと、AIが自動でコードレビューをしてくれるしくみを、自分のリポジトリに入れて試しました。
知りたかったのは、次の3つです。
- AIは、問題を見つけられるのか
- その指摘は、正確なのか
- チェックツールと併用したほうがいいのか
問題を9個仕込んだPRを出し、指摘された修正案を実際に適用して確かめました。
この記事は、初めてのCI/CDとチェックの記事の続きです。イベントやランナーなどの基本は、前の記事に書きました。
CI/CDとAIレビューは初心者です。間違いなどがあれば、コメントで教えていただけるとうれしいです。この記事の検証と執筆には、AI(Claude Code)の手も借りました。
2. 先に結論
AIのレビューは頼りになりました。ただし、修正案は人の確認が必要です。チェックツールとの併用もおすすめです。
| 知りたかったこと | 結果 |
|---|---|
| 問題を見つけられるか | 仕込んだ問題を9個すべて指摘した(約1分) |
| 指摘は正確か | 指摘の内容は正確だが、小さな誤りはある。適用できる修正案は9個中5個が、そのままでは壊れた |
| ツールと併用すべきか | 併用したほうがいい。壊れた修正案は、html-validateとESLintが止めた |
今回の小さな検証で見えた棲み分けは、次のとおりです。
| 担当 | 得意なこと | 気をつけること |
|---|---|---|
| チェックツール | 決まった間違いを、毎回同じように止める | ツールの範囲しか見ない |
| AI | 意味の食い違いに気づき、理由まで説明する | 結果は毎回同じではなく、修正案も間違える |
| 人 | 修正案を確かめ、仕様を決める | AIの文章がもっともらしくても、そのまま信じない |
小さなページを1つ使っただけの検証なので、「9個すべて」「9個中5個」といった結果が、ほかのコードでも同じになるとは限りません。自分の環境ではこうなった、という記録です。
3. 環境
前回の記事で作った、Cloudflare Workersへ公開するリポジトリに追加しました。
| 項目 | 内容 |
|---|---|
| リポジトリ | GitHubのPrivateリポジトリ(無料プラン) |
| レビュー | anthropics/claude-code-action@v1 |
| 認証 | Claudeの有料プラン(Pro・Maxなど)で作ったトークン(claude setup-token) |
ルールのチェックに使ったツールは、次の3つです。
- html-validate 11.18.0(最初から)
- ESLint 10.14.0(7章で追加)
- gitleaks 8.30.1(7章で追加)
機能の追加は、check.ymlとdeploy.ymlに手を入れずに、ワークフローをファイル1つ足すだけです。AIのレビューは意見なので、needs:にはつなぎませんでした。指摘があっても公開は止まりません。
4. 手順
(1) Claude GitHub Appを入れる
https://github.com/apps/claude を開いて、インストールします。このページは、自分のGitHubのメニューからはたどれませんでした。
最初はAll repositoriesが選ばれています。権限が広いので、Only select repositoriesに変えて、使うリポジトリだけを選びました。
インストール後にGitHub の接続を完了という画面が出ましたが、これはclaude.aiとつなぐためのものです。押さずに進めても、レビューは動きました。
(2) トークンを作ってSecretsに入れる
Pro・Maxなどの有料プランの契約があれば、次のコマンドでトークンを作れます。API契約は使いませんでした。
claude setup-token
表示されたトークンを、リポジトリのSettings → Secrets and variables → Actionsに、CLAUDE_CODE_OAUTH_TOKENという名前で入れます。トークンはコードには書きません。
このトークンは、作った人のClaudeの契約に結び付いていて、レビューの実行にはその契約の利用枠が使われます。複数のリポジトリでSecretを共有する場合や、商品・サービスに組み込む場合は、公式はAPIキーを案内しています。条件は変わることがあるので、GitHub Actionsの公式ドキュメントと利用条件のページを確認してください。
(3) ワークフローを足す
公式の例を元に、.github/workflows/claude-code-review.ymlを作りました。
name: Claude Code Review
on:
pull_request:
branches: [main]
types: [opened, synchronize, ready_for_review, reopened]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Review with Claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}
このプルリクエストの変更をレビューしてください。
観点: バグ、セキュリティ、アクセシビリティ、読みやすさ。
レビューは日本語で書いてください。
- 特定の行についての指摘は、インラインコメントで書いてください
- 全体の所感は、gh pr comment で1件だけ書いてください
- 問題がなければ、無理に指摘を作らないでください
claude_args: |
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
前回と違うところは、次の4つです。
| 書き方 | 意味 |
|---|---|
types: |
PRで何が起きたときに動くか。ready_for_reviewは、下書きのPRをレビュー可能にしたとき |
id-token: write |
ワークフローの札をもらう権限。この札を、Claude GitHub Appの鍵と交換する |
prompt: |
Claudeへの指示。${{ }}は、ランナーに渡る前にGitHubが値へ置き換える |
--allowedTools |
使ってよい道具。許可ボタンを押す人がいないので、先に書いておく |
このファイルを足したPRをmainにマージすると、次のPRからレビューが動きます。追加するPR自体はレビューされません(8章)。
5. AIは問題を見つけられるか(問題を9個仕込んだPR)
割り勘を計算するページを足すPRを作り、わざと問題を入れました。フェアなテストにするため、PRのタイトルには仕込みのことを書いていません。このPRは最後に閉じて、公開していません。APIキーは、作った偽の値です。
const EXCHANGE_API_KEY = "a1b2c3d4e5f60718293a4b5c6d7e8f90";
// サービス料は10%
const SERVICE_RATE = 0.08;
const params = new URLSearchParams(location.search);
const groupName = params.get("group");
if (groupName) {
document.getElementById("group-title").innerHTML = groupName + "の割り勘";
}
async function showDollar(yen) {
const res = await fetch("http://api.example.com/rate?base=JPY&key=" + EXCHANGE_API_KEY);
const data = await res.json();
document.getElementById("dollar").textContent = "約" + (yen * data.USD).toFixed(2) + "ドル";
}
document.getElementById("calc-button").addEventListener("click", () => {
const total = document.getElementById("total").value;
const people = document.getElementById("people").value;
const service = total * SERVICE_RATE;
const sum = total + service;
const each = Math.floor(sum / people);
document.getElementById("result").textContent = "1人あたり " + each + " 円";
showDollar(each);
});
結果は、Check / htmlが緑で、Claudeは約1分で指摘を書きました。
指摘があると赤くなると思っていましたが、Claude Code Reviewは緑でした。この緑は、処理が失敗せずに終わったという意味で、問題がなかったという意味ではありません。そのため、重大な指摘があっても、Merge pull requestは押せる状態のままです。
| # | 仕込んだ問題 | Claude |
|---|---|---|
| 1 | URLの値をinnerHTMLに入れるXSS |
指摘 |
| 2 | APIキーの直書き | 指摘 |
| 3 |
http://でキーを送る |
指摘 |
| 4 | 文字列の連結(total + service) |
指摘 |
| 5 | 説明は10%、コードは0.08 | 指摘 |
| 6 |
divをボタンにしている |
指摘 |
| 7 | 人数0・未入力のチェックがない | 指摘 |
| 8 | 端数の切り捨て | 指摘(インラインコメントの中) |
| 9 | 通信エラーの処理がない | 指摘 |
さらに、仕込んでいない問題も指摘しました。結果を読み上げソフトに知らせるaria-live、入力欄のminとstep、ボタンの文字のコントラスト不足(2回目のレビューのみ)です。
最後に、問題のないPRも試しました。1行足すだけのPRでは、インラインコメントは0件で、所感に「指摘事項はありません。LGTM」とだけ書かれました。prompt:に書いた「無理に指摘を作らないで」を外した場合とは、比べていません。
6. 指摘は正確か(修正案を適用すると、壊れた)
指摘は正確でしたが、Apply suggestionで適用できる修正案は、9個中5個がそのままでは壊れました。
この記事で一番伝えたいところです。直す内容は合っていますが、当てる行が合っていません。原因は分かっていません。
| レビュー | 修正案 | 結果 |
|---|---|---|
| 1回目 |
SERVICE_RATEを0.1に |
問題なし |
| 1回目 |
innerHTMLをtextContentに |
問題なし |
| 1回目 | 文字列連結の修正 | 宣言が重複して文法エラー |
| 1回目 |
divをbuttonに |
人数の入力欄が置き換わる |
| 1回目 |
aria-liveの追加 |
計算ボタンのdivが消え、IDが重複する |
| 2回目 |
SERVICE_RATEを0.1に |
問題なし |
| 2回目 | 入力欄の追加 | 問題なし |
| 2回目 |
textContentに(8行目に当てた) |
ifを置き換えて文法エラー |
| 2回目 | 文字列連結の修正(行を足した) | 宣言が重複して文法エラー |
PRで実際に適用したのは3個です(SERVICE_RATE、div、2回目の文字列連結)。1回目の文字列連結は、コピーに当ててChromeで文法エラーを確かめました。残りの5個は実際には適用せず、壊れるかどうかをコードを読んで確かめました(問題なし3個、壊れる2個)。
具体例です。divの修正案は「divをbuttonに変える」という内容ですが、実際に置き換えていたのは1行上の<input id="people">でした。
これを適用すると、Check / htmlが赤になりました。
22:14 error Duplicate ID "calc-button" no-dup-id
AIの修正案の間違いを、前回作ったチェックが止めた形です。
ただし、html-validateが止めたのは重複IDだけです。人数の入力欄が消えたことは、2回目のレビューでClaudeが指摘しました。ボタンを押すとnullのエラーになる、と影響まで書かれています。ただし、自分の修正案が原因とは書いていません。
文章にも、小さな誤りがありました。
- PRは未マージで、リポジトリもPrivateなのに、「既に公開されている」と書かれていた
-
"1000" + 80の結果が"10000.08"相当と書かれていた(正しくは"100080")
どちらも、2回目のレビューでは直っていました。毎回同じ結果になるわけではない、ということでもあります。
7. ツールと併用すべきか(チェックを増やして比べ直す)
ここまでの比較には、問題がありました。html-validateは.jsを読まないので、JavaScriptの問題を見つけられないのは当たり前です。
「チェックがHTMLだけだからでは?」と気づき、前回の記事の表にあったESLintとgitleaksをcheck.ymlに足して、比べ直しました。
js:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5
- name: Install ESLint
run: npm install --no-save eslint@10.14.0 @eslint/js@10.0.1
- name: Lint JavaScript
run: npx eslint --no-error-on-unmatched-pattern public/
secrets:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5
- name: Install gitleaks
run: curl -sSL https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz | tar xz gitleaks
- name: Scan for secrets
run: ./gitleaks dir . --redact --no-banner
deploy.ymlは変えていません。check.yml全体を呼んでいるので、deployは増えたジョブも待ちました。
この状態で、宣言が重複する修正案を適用すると、ESLintが止めました。
21:9 error Parsing error: Identifier 'total' has already been declared
gitleaksは、偽のAPIキーにも反応しました。ログにはleaks found: 1だけが出ます。同じ版を手元で詳しく動かすと、generic-api-keyというルールが1行目を示していました。本物かどうかは見ていません。
試した結果と、試していない予想を分けます。
| 問題 | ツール | 状態 | Claude |
|---|---|---|---|
| 修正案の宣言重複 | ESLint | 実測で検出 | 指摘 |
| 重複ID | html-validate | 実測で検出 | 指摘 |
| APIキーの直書き | gitleaks | 実測で検出 | 指摘 |
| 消えた入力欄 | html-validate | 実測で見つからず | 指摘 |
| XSS | ESLintのプラグイン | 未検証 | 指摘 |
| 文字列の計算 | tsc | 未検証 | 指摘 |
divボタン、コントラスト |
axeなど | 未検証 | 指摘 |
| 10%と0.08 | 思いつかない | 未検証 | 指摘 |
| 端数の扱い | 思いつかない | 未検証 | 指摘 |
ツールを適切に選べば、実際に止められる範囲は広がりました。最後の2つのように、意味や仕様に関わる問題は、AIの得意分野に残りそうです。
8. 補足: 緑でも、レビューされていないことがある
Actionsは緑なのに、レビューが届かない場面は2回ありました。
| 場面 | 結果 | ログに出ていたこと |
|---|---|---|
| ワークフローを追加した最初のPR | 11秒で緑、コメントなし | Skipping action due to workflow validation |
試しに--allowedToolsを外したPR |
約1分で緑、コメントなし | "permission_denials_count": 5 |
1つ目は、mainと同じでないワークフローではレビューが飛ばされるためです。ログによると正常な動きで、マージすれば動き始めます。2つ目は、許可されていない操作を5回拒否されても、失敗にはなりませんでした。
コメントが付かないときは、緑でもログを見るのがおすすめです。
9. まとめ
AIのコードレビューは、問題を見つける相手としてはとても頼りになりました。ただし、ツールの代わりでも、人の代わりでもありません。
実際に使うなら、次の3つに気をつけたいと思います。
- 修正案は、赤い行を見てから適用する: 消える行が指摘された行か確かめる
-
コメントがなければ、緑でもログを見る: スキップや
permission_denials_countを確かめる - pushの回数に気をつける: pushのたびに同じ指摘が投稿し直され、PRが長くなる
AIの修正案をそのまま適用しても問題が解決しなかったときは、思わず「え???」となりました。指摘は頼りになりますが、一度は人が確認しないとダメだなと思いました。
その一方で、チェックツールを適切に選べば、ツールとAIはお互いに補い合えると感じました。
今回はAIへの指示文(prompt:に書いた文章)を変えて比べていません。指示の出し方で結果が変わる可能性はありますが、未検証です。ここは、いつか試してみたいです。





