name: Code Review
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [master, main]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
checks: write
issues: write
jobs:
# PR 代码审查:只审查 PR 中变更的文件
pr-code-review:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code
- name: Get PR changed files
id: pr-changed-files
run: |
# 获取 PR 中变更的文件列表
# 使用 git diff 获取 PR 分支与目标分支的差异
PR_BASE=${{ github.event.pull_request.base.ref }}
PR_HEAD=${{ github.event.pull_request.head.ref }}
# 获取变更的文件
git diff --name-only origin/$PR_BASE...origin/$PR_HEAD > pr_changed_files.txt
# 获取每个文件的变更内容
echo "pr_files=$(cat pr_changed_files.txt | tr '\n' ' ')" >> $GITHUB_OUTPUT
# 输出文件数量
echo "file_count=$(wc -l < pr_changed_files.txt)" >> $GITHUB_OUTPUT
- name: Run PR Code Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
run: |
echo "Reviewing PR #${{ github.event.pull_request.number }}"
echo "Changed files: ${{ steps.pr-changed-files.outputs.pr_files }}"
# 创建审查结果文件
> pr_review.md
# 创建审查提示模板文件
cat > review_prompt.txt << 'INNEREOF'
请审查这个文件的变更(使用中文回复):
变更内容请查看传入的 diff 信息。
重要:只报告需要改进或修复的问题!
审查要求:
1. 只指出实际存在的问题,不要描述正常的实现
2. 如果代码质量良好且没有问题,请直接回复"代码质量良好,没有发现需要改进的地方"
3. 如果发现问题,请提供:
- 问题描述
- 具体的行号(如果适用)
- 修复方案
- 优先级(高/中/低)
检查重点:
- 代码质量:可读性、命名规范、重复代码
- 安全漏洞:是否有敏感信息泄露、输入验证、认证授权
- 维护性:测试覆盖率、性能考虑、文档清晰度
- 逻辑错误或潜在的 bug:比如是否正确处理了各种边界情况
- 性能问题
请不要报告:
- 正常的代码结构
- 符合规范的实现
- 已经正确处理的部分
- 错误处理(已修复)
INNEREOF
# 对每个变更的文件进行审查
while IFS= read -r file; do
if [ -n "$file" ] && [ -f "$file" ]; then
echo "" >> pr_review.md
echo "========================================" >> pr_review.md
echo "Reviewing file: $file" >> pr_review.md
echo "========================================" >> pr_review.md
echo "" >> pr_review.md
# 获取文件的变更差异
if ! git diff origin/${{ github.event.pull_request.base.ref }}...origin/${{ github.event.pull_request.head.ref }} -- "$file" > "${file}_diff.txt" 2>/dev/null; then
echo "Failed to get diff for file: $file" >> pr_review.md
continue
fi
# 安全地构建审查请求文件
{
echo "文件: $file"
echo ""
echo "变更内容:"
echo '```diff'
cat "${file}_diff.txt"
echo '```'
echo ""
cat review_prompt.txt
} > review_request.txt
# 运行 Claude Code 审查
cat review_request.txt | npx @anthropic-ai/claude-code review --model=opus --agent=code-reviewer >> pr_review.md
# 清理临时文件
rm -f "${file}_diff.txt" review_request.txt
echo "" >> pr_review.md
fi
done < pr_changed_files.txt
# 清理临时文件
rm -f review_prompt.txt
- name: Create File-level Review Comments
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const { execSync } = require('child_process');
// 获取 PR 信息
const prNumber = context.issue.number;
const baseSha = context.payload.pull_request.base.sha;
const headSha = context.payload.pull_request.head.sha;
// 读取审查结果
let reviewContent = '';
try {
reviewContent = fs.readFileSync('pr_review.md', 'utf8');
} catch (error) {
console.error('Could not read review file:', error);
return;
}
// 如果没有审查内容,则返回
if (!reviewContent.trim()) {
console.log('No review content found');
return;
}
// 创建 PR 主体评论
const summaryComment = "## 🤖 PR Code Review Results\n\n" +
reviewContent + "\n\n" +
"---\n" +
"*This review was generated by Claude Code using the Opus model.*";
// 查找是否已有 Claude Code 的评论
const { data: comments } = await github.rest.issues.listComments({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
});
const existingComment = comments.find(comment =>
comment.body.includes('🤖 PR Code Review Results') &&
comment.user.type === 'Bot'
);
if (existingComment) {
await github.rest.issues.updateComment({
comment_id: existingComment.id,
owner: context.repo.owner,
repo: context.repo.repo,
body: summaryComment
});
} else {
await github.rest.issues.createComment({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: summaryComment
});
}
# Push 代码审查:审查推送到 master 分支的变更
push-code-review:
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code
- name: Get changed files
id: changed-files
run: |
# 获取与上次提交相比的变更文件
if [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then
git diff --name-only ${{ github.event.before }} ${{ github.sha }} > changed_files.txt
else
# 首次推送,获取最近的提交
git diff --name-only HEAD~1 HEAD > changed_files.txt
fi
echo "changed_files=$(cat changed_files.txt | tr '\n' ' ')" >> $GITHUB_OUTPUT
- name: Run Code Review on Push
if: steps.changed-files.outputs.changed_files != ''
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
run: |
echo "Reviewing push to ${{ github.ref }}"
echo "Changed files: ${{ steps.changed-files.outputs.changed_files }}"
# 创建审查结果文件
> push_review.md
# 创建审查提示模板文件
cat > review_prompt.txt << 'INNEREOF'
请审查这个文件的变更(使用中文回复):
变更内容请查看传入的 diff 信息。
重要:只报告需要改进或修复的问题!
审查要求:
1. 只指出实际存在的问题,不要描述正常的实现
2. 如果代码质量良好且没有问题,请直接回复"代码质量良好,没有发现需要改进的地方"
3. 如果发现问题,请提供:
- 问题描述
- 具体的行号(如果适用)
- 修复方案
- 优先级(高/中/低)
检查重点:
- 代码质量:可读性、命名规范、重复代码
- 安全漏洞:是否有敏感信息泄露、输入验证、认证授权
- 维护性:测试覆盖率、性能考虑、文档清晰度
- 逻辑错误或潜在的 bug:比如是否正确处理了各种边界情况
- 性能问题
请不要报告:
- 正常的代码结构
- 符合规范的实现
- 已经正确处理的部分
- 错误处理(已修复)
INNEREOF
# 对每个变更的文件进行审查
while IFS= read -r file; do
if [ -n "$file" ] && [ -f "$file" ]; then
echo "" >> push_review.md
echo "========================================" >> push_review.md
echo "Reviewing file: $file" >> push_review.md
echo "========================================" >> push_review.md
echo "" >> push_review.md
# 获取文件的变更差异
if ! git diff ${{ github.event.before }} ${{ github.sha }} -- "$file" > "${file}_diff.txt" 2>/dev/null; then
echo "Failed to get diff for file: $file" >> push_review.md
continue
fi
# 安全地构建审查请求文件
{
echo "文件: $file"
echo ""
echo "变更内容:"
echo '```diff'
cat "${file}_diff.txt"
echo '```'
echo ""
cat review_prompt.txt
} > review_request.txt
# 运行 Claude Code 审查
cat review_request.txt | npx @anthropic-ai/claude-code review --model=opus --agent=code-reviewer >> push_review.md
# 清理临时文件
rm -f "${file}_diff.txt" review_request.txt
echo "" >> push_review.md
fi
done < changed_files.txt
# 清理临时文件
rm -f review_prompt.txt
- name: Comment Commit with Review Results
if: always() && steps.changed-files.outputs.changed_files != ''
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
try {
const reviewContent = fs.readFileSync('push_review.md', 'utf8');
if (!reviewContent.trim()) {
console.log('Review content is empty, skipping comment');
return;
}
const commitSha = context.sha.substring(0, 7);
const commentBody = "## 🤖 Code Review for Push " + commitSha + "\n\n" +
reviewContent + "\n\n" +
"---\n" +
"*This review was generated by Claude Code using the Opus model.*";
await github.rest.repos.createCommitComment({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha,
body: commentBody
});
} catch (error) {
console.error('Error creating commit comment:', error);
}
- name: Create Issue for Critical Findings
if: failure() && steps.changed-files.outputs.changed_files != ''
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
let reviewContent = '';
let changedFiles = '${{ steps.changed-files.outputs.changed_files }}';
try {
reviewContent = fs.readFileSync('push_review.md', 'utf8');
} catch (error) {
console.error('Could not read review file:', error);
}
let issueBody = '## 🚨 Code Review - Critical Issues Detected\n\n';
issueBody += 'The automated code review detected critical issues in the recent push.\n\n';
if (changedFiles) {
issueBody += '### 📁 Changed Files\n\n';
issueBody += '```\n' + changedFiles + '\n```\n\n';
}
if (reviewContent.trim()) {
issueBody += '### 📋 Review Details\n\n';
issueBody += reviewContent + '\n\n';
}
issueBody += '### 📌 Next Steps\n\n';
issueBody += '1. Review the critical issues listed above\n';
issueBody += '2. Address the identified problems\n';
issueBody += '3. Push the fixes to resolve this issue\n\n';
issueBody += '[View Workflow Logs](' + context.payload.repository.html_url + '/actions/runs/' + context.runId + ')\n\n';
issueBody += '---\n';
issueBody += '*This issue was automatically created by Claude Code using the Opus model.*';
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'Code Review - Critical Issues Detected',
body: issueBody,
labels: ['code-review', 'critical']
});