mirror of
https://github.com/pacnpal/Claude-code-review.git
synced 2025-12-20 04:11:04 -05:00
195 lines
5.0 KiB
JavaScript
195 lines
5.0 KiB
JavaScript
// action.js
|
|
const core = require('@actions/core');
|
|
const github = require('@actions/github');
|
|
const { exec } = require('@actions/exec');
|
|
|
|
async function getPRDetails(octokit, context, prNumber) {
|
|
try {
|
|
console.log(`Getting details for PR #${prNumber}`);
|
|
|
|
// Get PR info
|
|
const { data: pr } = await octokit.rest.pulls.get({
|
|
...context.repo,
|
|
pull_number: parseInt(prNumber)
|
|
});
|
|
|
|
return {
|
|
number: pr.number,
|
|
base: {
|
|
sha: pr.base.sha,
|
|
ref: pr.base.ref
|
|
},
|
|
head: {
|
|
sha: pr.head.sha,
|
|
ref: pr.head.ref
|
|
}
|
|
};
|
|
} catch (error) {
|
|
throw new Error(`Failed to get PR details: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function setupGitConfig() {
|
|
// Configure git to fetch PR refs
|
|
await exec('git', ['config', '--local', '--add', 'remote.origin.fetch', '+refs/pull/*/head:refs/remotes/origin/pr/*']);
|
|
await exec('git', ['fetch', 'origin']);
|
|
}
|
|
|
|
async function getDiff(baseSha, headSha) {
|
|
let diffContent = '';
|
|
|
|
try {
|
|
// Get the full diff with context
|
|
await exec('git', ['diff', '-U10', baseSha, headSha], {
|
|
listeners: {
|
|
stdout: (data) => {
|
|
diffContent += data.toString();
|
|
}
|
|
}
|
|
});
|
|
|
|
return diffContent;
|
|
} catch (error) {
|
|
throw new Error(`Failed to generate diff: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function analyzeWithClaude(diffContent, anthropicKey) {
|
|
if (!diffContent.trim()) {
|
|
return null;
|
|
}
|
|
|
|
const prompt = `You are performing a code review. Please analyze this code diff and provide a thorough review that covers:
|
|
|
|
1. Potential conflicts with existing codebase
|
|
2. Code correctness and potential bugs
|
|
3. Security vulnerabilities or risks
|
|
4. Performance implications
|
|
5. Maintainability and readability issues
|
|
6. Adherence to best practices and coding standards
|
|
7. Suggestions for improvements
|
|
|
|
For each issue found:
|
|
- Explain the problem clearly
|
|
- Rate the severity (Critical/High/Medium/Low)
|
|
- Provide specific recommendations for fixes
|
|
- Include code examples where helpful
|
|
|
|
- If no issues are found in a particular area, explicitly state that.
|
|
- If it's a dependency update, evaluate with strict scrutiny the implications of the change.
|
|
- No matter your findings, give a summary of the pull request.
|
|
|
|
Here is the code diff to review:
|
|
|
|
\`\`\`
|
|
${diffContent}
|
|
\`\`\``;
|
|
|
|
try {
|
|
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'x-api-key': anthropicKey,
|
|
'anthropic-version': '2023-06-01'
|
|
},
|
|
body: JSON.stringify({
|
|
model: 'claude-3-5-sonnet-20241022',
|
|
max_tokens: 4096,
|
|
temperature: 0.7,
|
|
messages: [{
|
|
role: 'user',
|
|
content: prompt
|
|
}]
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
if (!data.content?.[0]?.text) {
|
|
throw new Error(`API Error: ${JSON.stringify(data)}`);
|
|
}
|
|
|
|
return data.content[0].text;
|
|
} catch (error) {
|
|
throw new Error(`Claude API error: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function postReview(octokit, context, review, prNumber) {
|
|
try {
|
|
// Escape special characters for proper formatting
|
|
const escapedReview = review
|
|
.replace(/(?<=[\s\n])`([^`]+)`(?=[\s\n])/g, '\\`$1\\`')
|
|
.replace(/```/g, '\\`\\`\\`')
|
|
.replace(/\${/g, '\\${');
|
|
|
|
await octokit.rest.issues.createComment({
|
|
...context.repo,
|
|
issue_number: prNumber,
|
|
body: `# Claude Code Review\n\n${escapedReview}`
|
|
});
|
|
} catch (error) {
|
|
throw new Error(`Failed to post review: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function run() {
|
|
try {
|
|
// Get inputs
|
|
const token = core.getInput('github-token', { required: true });
|
|
const anthropicKey = core.getInput('anthropic-key', { required: true });
|
|
|
|
// Initialize GitHub client
|
|
const octokit = github.getOctokit(token);
|
|
const context = github.context;
|
|
|
|
// Get PR number from event or input
|
|
let prNumber;
|
|
if (context.eventName === 'pull_request') {
|
|
prNumber = context.payload.pull_request.number;
|
|
} else {
|
|
prNumber = core.getInput('pr-number', { required: true });
|
|
}
|
|
|
|
// Set up git configuration
|
|
await setupGitConfig();
|
|
|
|
// Get PR details
|
|
const pr = await getPRDetails(octokit, context, prNumber);
|
|
console.log(`Retrieved details for PR #${pr.number}`);
|
|
|
|
// Generate diff
|
|
console.log('Generating diff...');
|
|
const diff = await getDiff(pr.base.sha, pr.head.sha);
|
|
|
|
if (!diff) {
|
|
console.log('No relevant changes found');
|
|
core.setOutput('diff_size', '0');
|
|
return;
|
|
}
|
|
|
|
core.setOutput('diff_size', diff.length.toString());
|
|
|
|
// Analyze with Claude
|
|
console.log('Analyzing with Claude...');
|
|
const review = await analyzeWithClaude(diff, anthropicKey);
|
|
|
|
if (!review) {
|
|
console.log('No review generated');
|
|
return;
|
|
}
|
|
|
|
// Post review
|
|
console.log('Posting review...');
|
|
await postReview(octokit, context, review, pr.number);
|
|
|
|
// Set outputs
|
|
core.setOutput('review', review);
|
|
|
|
} catch (error) {
|
|
core.setFailed(error.message);
|
|
}
|
|
}
|
|
|
|
run();
|