取り組みの背景:なぜAIコードレビューが個人開発者にとって重要なのか
Rork Maxを使ってアプリを開発していると、コードの品質管理という課題に直面する場面があります。一人で開発していると、自分のコードをレビューする目が少なく、バグやセキュリティの問題を見落としてしまうことも珍しくありません。
かつては「コードレビューはチーム開発のもの」という認識がありましたが、AIの登場によってその常識は大きく変わりましました。今や、GitHub ActionsとClaude APIを組み合わせることで、Pull Requestをマージするたびに自動でAIがコードをレビューし、問題点を指摘してくれる仕組みを数時間で構築できるようになっています。
ここで扱うのはRork MaxプロジェクトにAIコードレビューパイプラインを導入するための完全な手順を解説します。セットアップから実際の運用まで、実践的なYAMLコードと具体的なユースケースをもとに詳しく説明していきます。
この記事で学べること
GitHub ActionsワークフローとClaude APIの連携の仕組み
PR(プルリクエスト)ごとにAIが自動でコードレビューを実施する設定
バグ検出・セキュリティスキャン・パフォーマンス分析の自動化
レビューコメントを見やすく整形してGitHubに投稿する方法
個人開発・チーム開発それぞれに最適なカスタマイズ戦略
対象読者
Rork MaxでReact Native / Expoアプリを開発している方
一人でアプリをリリースしており、コード品質に不安を感じている方
CI/CDは導入済みだが、コードレビューの自動化まではできていない方
Claude APIを使った実践的な開発自動化に興味がある方
AIコードレビューの全体像
まず、今回構築するシステムの全体像を把握しておきましょう。システムの流れは、「開発者がPRを作成」→「GitHub Actionsがトリガーされる」→「変更されたファイルの差分(diff)を取得」→「Claude APIにコードレビューを依頼」→「AIがバグ・セキュリティリスク・パフォーマンス問題・React Nativeベストプラクティス違反・TypeScriptの型安全性を分析」→「レビュー結果をGitHubのPRコメントとして自動投稿」→「開発者が確認して修正」となっています。
このフローにより、PRをマージする前に毎回AIのチェックが入り、人間のレビューでは見落としやすい問題を自動で検出できます。
システムの主なメリット
AIコードレビューの導入により、以下のメリットが得られます。まず24時間365日稼働 という点で、深夜にコードを書いてもすぐにレビューが入ります。次に一貫した品質基準 として、疲れや気分に左右されないレビューが実現します。また学習コストの削減 として、AIのフィードバックから自動的に良いコードの書き方を学べます。さらにコスト効率 として、月間数百円程度で高品質なレビューが得られます。
Step 1: 前提条件と環境準備
必要なもの
Rork MaxプロジェクトのGitHubリポジトリ
Claude APIキー(Anthropicの開発者ダッシュボードから取得)
GitHubリポジトリへの管理者権限(Secretsの設定に必要)
GitHub Secretsの設定
まず、Claude APIキーをGitHub Secretsに安全に保存します。GitHubリポジトリの Settings を開き、左メニューの Secrets and variables → Actions を選択し、New repository secret をクリックして、名前に ANTHROPIC_API_KEY、値にAPIキーを入力して保存してください。
# 設定するSecret(GitHub UIで設定)
Name: ANTHROPIC_API_KEY
Value: YOUR_ANTHROPIC_API_KEY
Step 2: ワークフローファイルの作成
プロジェクトのルートに .github/workflows/ ディレクトリを作成し、以下のワークフローファイルを配置します。
# .github/workflows/ai-code-review.yml
name : AI Code Review
on :
pull_request :
types : [ opened , synchronize , reopened ]
paths :
- 'app/**'
- 'components/**'
- 'hooks/**'
- 'lib/**'
- 'utils/**'
- 'api/**'
- '*.ts'
- '*.tsx'
jobs :
ai-code-review :
name : AI Code Review with Claude
runs-on : ubuntu-latest
permissions :
contents : read
pull-requests : write
steps :
- name : Checkout repository
uses : actions/checkout@v4
with :
fetch-depth : 0
- name : Set up Node.js
uses : actions/setup-node@v4
with :
node-version : '20'
- name : Install dependencies for review script
run : npm install @anthropic-ai/sdk @octokit/rest
- name : Run AI Code Review
env :
ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER : ${{ github.event.pull_request.number }}
REPO_OWNER : ${{ github.repository_owner }}
REPO_NAME : ${{ github.event.repository.name }}
BASE_SHA : ${{ github.event.pull_request.base.sha }}
HEAD_SHA : ${{ github.event.pull_request.head.sha }}
run : node .github/scripts/ai-review.js
Step 3: AIレビュースクリプトの実装
次に、実際にClaude APIを呼び出してレビューを実行するスクリプトを作成します。
// .github/scripts/ai-review.js
const Anthropic = require ( '@anthropic-ai/sdk' );
const { Octokit } = require ( '@octokit/rest' );
const { execSync } = require ( 'child_process' );
const anthropic = new Anthropic ({ apiKey: process.env. ANTHROPIC_API_KEY });
const octokit = new Octokit ({ auth: process.env. GITHUB_TOKEN });
// 変更差分を取得する関数
function getDiff () {
try {
const baseSha = process.env. BASE_SHA ;
const headSha = process.env. HEAD_SHA ;
const diff = execSync (
`git diff ${ baseSha }...${ headSha } -- '*.ts' '*.tsx' '*.js' '*.jsx'` ,
{ maxBuffer: 1024 * 1024 * 10 }
). toString ();
// 長すぎる差分は切り詰める(トークン節約)
const maxLength = 15000 ;
if (diff. length > maxLength) {
return diff. substring ( 0 , maxLength) + ' \n\n ... (差分が長すぎるため省略)' ;
}
return diff;
} catch (error) {
console. error ( '差分の取得に失敗:' , error.message);
return null ;
}
}
// Claude APIでコードレビューを実施する関数
async function reviewWithClaude ( diff ) {
const systemPrompt = `あなたはReact Native / Expo / Rork Maxアプリ開発の専門家です。
以下の観点からコードレビューを実施してください:
1. バグ・ロジックエラー: nullアクセス、型の不一致、非同期処理の誤り、state更新の問題等
2. セキュリティリスク: APIキーのハードコード、不適切な権限設定、XSSリスク等
3. パフォーマンス: 不要な再レンダリング、useEffectの依存配列の誤り等
4. React Nativeベストプラクティス: Platform.OS分岐の適切さ、StyleSheetの使用方法等
5. TypeScript: any型の濫用、型定義の欠如等
レビュー結果は以下の形式で日本語で返してください:
## AIコードレビュー
### 重大な問題(必ず対処)
(問題の説明と該当箇所)
### 改善推奨
(改善すべき点)
### 提案・ベストプラクティス
(より良い実装方法)
### 良い点
(コードの良い部分)
問題がない場合は「問題は見つかりませんでした」と記載してください。
Markdownテーブルは使用しないでください。` ;
const response = await anthropic.messages. create ({
model: 'claude-opus-4-6' ,
max_tokens: 2048 ,
messages: [
{
role: 'user' ,
content: `以下のコード差分をレビューしてください: \n\n\`\`\` diff \n ${ diff } \n\`\`\` ` ,
},
],
system: systemPrompt,
});
return response.content[ 0 ].text;
}
// GitHubのPRにコメントを投稿する関数
async function postReviewComment ( reviewText ) {
const owner = process.env. REPO_OWNER ;
const repo = process.env. REPO_NAME ;
const prNumber = parseInt (process.env. PR_NUMBER , 10 );
// 既存のAIレビューコメントを検索(重複防止)
const { data : comments } = await octokit.issues. listComments ({
owner,
repo,
issue_number: prNumber,
});
const existingComment = comments. find (
( comment ) =>
comment.user.login === 'github-actions[bot]' &&
comment.body. includes ( 'AIコードレビュー' )
);
const commentBody = `${ reviewText } \n\n --- \n *このレビューはClaude AIによって自動生成されました。*` ;
if (existingComment) {
// 既存コメントを更新
await octokit.issues. updateComment ({
owner,
repo,
comment_id: existingComment.id,
body: commentBody,
});
console. log ( '既存のAIレビューコメントを更新しました' );
} else {
// 新規コメントを投稿
await octokit.issues. createComment ({
owner,
repo,
issue_number: prNumber,
body: commentBody,
});
console. log ( 'AIレビューコメントを投稿しました' );
}
}
// メイン処理
async function main () {
console. log ( 'コード差分を取得中...' );
const diff = getDiff ();
if ( ! diff || diff. trim () === '' ) {
console. log ( '差分が見つかりませんでした' );
return ;
}
console. log ( `差分サイズ: ${ diff . length } 文字` );
console. log ( 'Claude APIでコードレビューを実施中...' );
const reviewText = await reviewWithClaude (diff);
console. log ( 'GitHubにレビューコメントを投稿中...' );
await postReviewComment (reviewText);
console. log ( 'AIコードレビューが完了しました!' );
}
main (). catch (( error ) => {
console. error ( 'エラーが発生しました:' , error);
process. exit ( 1 );
});
Step 4: セキュリティスキャン専用ワークフローの追加
コードレビューに加えて、セキュリティ問題に特化したスキャンワークフローも追加しましょう。.github/scripts/security-scan.js を作成します。
// .github/scripts/security-scan.js
const { Octokit } = require ( '@octokit/rest' );
const { execSync } = require ( 'child_process' );
const fs = require ( 'fs' );
const octokit = new Octokit ({ auth: process.env. GITHUB_TOKEN });
// 危険なパターンのリスト
const DANGEROUS_PATTERNS = [
{
pattern: /api [_-] ? key \s * = \s * ["'][A-Za-z0-9+/] {20,} ["'] / gi ,
desc: 'APIキーのハードコード'
},
{
pattern: /password \s * = \s * ["'][ ^ "'] {8,} ["'] / gi ,
desc: 'パスワードのハードコード'
},
{
pattern: /eval \s * \( / g ,
desc: 'eval()の使用(セキュリティリスク)'
},
{
pattern: /dangerouslySetInnerHTML/ g ,
desc: 'dangerouslySetInnerHTMLの使用(XSSリスク)'
},
];
function getChangedFiles () {
try {
const baseSha = process.env. BASE_SHA ;
const headSha = process.env. HEAD_SHA ;
const output = execSync (
`git diff --name-only ${ baseSha }...${ headSha } -- '*.ts' '*.tsx' '*.js' '*.jsx'`
). toString ();
return output. trim (). split ( ' \n ' ). filter (Boolean);
} catch {
return [];
}
}
function quickScan ( files ) {
const issues = [];
for ( const file of files) {
if ( ! fs. existsSync (file)) continue ;
const content = fs. readFileSync (file, 'utf8' );
const lines = content. split ( ' \n ' );
for ( const { pattern , desc } of DANGEROUS_PATTERNS ) {
lines. forEach (( line , index ) => {
if (pattern. test (line)) {
issues. push ({ file, line: index + 1 , issue: desc, code: line. trim () });
}
pattern.lastIndex = 0 ;
});
}
}
return issues;
}
async function main () {
const files = getChangedFiles ();
if (files. length === 0 ) {
console. log ( '変更されたファイルがありません' );
return ;
}
const quickIssues = quickScan (files);
let reportBody = '## セキュリティスキャン結果 \n\n ' ;
if (quickIssues. length > 0 ) {
reportBody += '### 検出された問題 \n\n ' ;
for ( const issue of quickIssues) {
reportBody += `- **${ issue . file }** (行 ${ issue . line }): ${ issue . issue } \n ` ;
reportBody += ` \` ${ issue . code . substring ( 0 , 100 ) } \`\n ` ;
}
} else {
reportBody += '### 明らかなセキュリティ問題は検出されませんでした \n\n ' ;
}
reportBody += `スキャン対象ファイル数: ${ files . length } \n ` ;
reportBody += ' \n --- \n *このスキャンはAIによって自動実施されました。*' ;
if (process.env. PR_NUMBER ) {
await octokit.issues. createComment ({
owner: process.env. REPO_OWNER ,
repo: process.env. REPO_NAME ,
issue_number: parseInt (process.env. PR_NUMBER , 10 ),
body: reportBody,
});
}
console. log ( 'セキュリティスキャン完了' );
// 重大な問題がある場合はワークフローを失敗させる
if (quickIssues. some (( i ) => i.issue. includes ( 'APIキー' ) || i.issue. includes ( 'パスワード' ))) {
console. error ( '重大なセキュリティ問題が検出されました' );
process. exit ( 1 );
}
}
main (). catch (( error ) => {
console. error ( 'エラー:' , error);
process. exit ( 1 );
});
Step 5: パフォーマンス分析の自動化
React Nativeアプリ特有のパフォーマンス問題を検出する専用チェックを追加します。
// .github/scripts/performance-check.js
const fs = require ( 'fs' );
const { execSync } = require ( 'child_process' );
const PERF_PATTERNS = [
{
pattern: /console \. (log | warn | error)/ g ,
desc: 'console文が本番環境に残っています - __DEV__で囲むか削除してください' ,
severity: 'warning' ,
},
{
pattern: / \. map \( [ ^ )] + \) \s * \. \s * filter/ g ,
desc: '.map().filter()の順序 - filterを先にするとパフォーマンスが向上します' ,
severity: 'info' ,
},
{
pattern: /new \s + \w + \(\) / g ,
desc: 'レンダリング中のオブジェクト生成 - useMemoやuseRefへの移行を検討' ,
severity: 'info' ,
},
];
function getChangedFiles () {
try {
const baseSha = process.env. BASE_SHA ;
const headSha = process.env. HEAD_SHA ;
const output = execSync (
`git diff --name-only ${ baseSha }...${ headSha } -- '*.tsx' '*.ts'`
). toString ();
return output. trim (). split ( ' \n ' ). filter (Boolean);
} catch {
return [];
}
}
function analyzeFile ( filePath ) {
if ( ! fs. existsSync (filePath)) return [];
const content = fs. readFileSync (filePath, 'utf8' );
const findings = [];
for ( const { pattern , desc , severity } of PERF_PATTERNS ) {
const matches = content. match (pattern);
if (matches) findings. push ({ file: filePath, count: matches. length , desc, severity });
pattern.lastIndex = 0 ;
}
return findings;
}
function main () {
const files = getChangedFiles ();
const allFindings = files. flatMap (analyzeFile);
if (allFindings. length === 0 ) {
console. log ( 'パフォーマンス問題は検出されませんでした' );
return ;
}
console. log ( ' \n パフォーマンス分析結果:' );
for ( const finding of allFindings) {
const icon = finding.severity === 'warning' ? '[WARNING]' : '[INFO]' ;
console. log ( `${ icon } [${ finding . file }] ${ finding . desc } (${ finding . count }件)` );
}
}
main ();
Step 6: PRサイズに応じたコスト最適化
月間のAPIコストを管理するため、PRサイズに応じてレビュー戦略を変える仕組みを実装します。
# .github/workflows/ai-code-review.yml のサイズチェック部分
jobs :
check-size :
name : Check PR Size
runs-on : ubuntu-latest
outputs :
should-review : ${{ steps.check.outputs.should-review }}
steps :
- uses : actions/checkout@v4
with :
fetch-depth : 0
- id : check
run : |
CHANGED_LINES=$(git diff \
${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} \
--shortstat | grep -oP '\d+ insertion' | grep -oP '\d+' || echo 0)
echo "Changed lines: $CHANGED_LINES"
if [ "$CHANGED_LINES" -gt 2000 ]; then
echo "should-review=partial" >> $GITHUB_OUTPUT
else
echo "should-review=full" >> $GITHUB_OUTPUT
fi
Claude Opus 4.6を使用した場合の月間コストの目安は以下のとおりです。平均的なPR(200行変更)は入力2,000トークン + 出力500トークンで約0.025ドル、月間30PRであれば約0.75ドル(約120円)、月間100PRであれば約2.50ドル(約400円)程度です。個人開発者にとって非常にリーズナブルなコストで、高品質なレビューが得られます。
Step 7: プロジェクト固有のカスタマイズ
実際に運用を始めると、レビューの精度を高めるためのチューニングが重要になります。
// ai-review.js のシステムプロンプトにプロジェクト固有のルールを追加
const projectSpecificRules = `
また、このプロジェクト固有のルールも確認してください:
- Supabaseのクライアントは必ず lib/supabase.ts から import すること
- 環境変数はすべて .env.local に定義し、直接コードに書かないこと
- コンポーネントは components/ ディレクトリに配置すること
- APIレスポンスの型は必ず定義し、any型を使用しないこと
` ;
// レビュー結果のログ記録(後で分析できるように)
const logEntry = {
date: new Date (). toISOString (),
prNumber: process.env. PR_NUMBER ,
diffSize: diff. length ,
hasIssues: reviewText. includes ( '重大な問題' ),
model: 'claude-opus-4-6' ,
};
const fs = require ( 'fs' );
fs. appendFileSync ( '.github/review-logs.jsonl' , JSON . stringify (logEntry) + ' \n ' );
Step 8: チーム開発への拡張
チーム開発では、以下の拡張によってさらに効果的なコードレビュー体制が構築できます。
重大な問題が検出された場合にPRのマージを自動でブロックするには、GitHubのBranch Protection Rulesを活用します。GitHubリポジトリの Settings → Branches → Branch protection rules で main ブランチを選択し、「Require status checks to pass before merging」の設定で今回作成したワークフロー(AI Code Review with Claude)を必須チェックとして登録します。
Slack通知との連携については、既存のRork Labの記事「GitHub Actions + EAS BuildによるCI/CDパイプライン構築 」も参考にしてください。Slack Webhookを使った通知の仕組みは同様の方法で実装できます。
個人開発者の視点から(実体験メモ)
まとめ
ここで扱うのはGitHub ActionsとClaude APIを組み合わせてRork MaxプロジェクトにAI自動コードレビューを導入する方法を詳しく解説しました。
実装のポイントを振り返ると、まずGitHub Secretsを使ってAPIキーを安全に管理すること、次にPRごとにコード差分を取得してClaude APIに送信するワークフローを構築すること、そしてレビュー結果を見やすい形式でGitHubのPRコメントとして自動投稿することが核心です。セキュリティスキャンとパフォーマンス分析を組み合わせることで、より包括的な品質保証が実現できます。
個人開発者が一人でアプリをリリースし続けるためには、このような自動化の仕組みが非常に心強い味方になります。コードを書くたびにAIが確認してくれることで、見落としによるバグやセキュリティリスクを大幅に減らし、App StoreでのリジェクトリスクやユーザーからのNegativeレビューを防ぐことができます。
AIを活用した開発自動化についてさらに深く