mirror of
https://github.com/pacnpal/Claude-code-review.git
synced 2025-12-20 04:11:04 -05:00
Initial commit
This commit is contained in:
211
action.js
Normal file
211
action.js
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Filter for relevant files
|
||||
const lines = diffContent.split('\n');
|
||||
let filtered = '';
|
||||
let keep = false;
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('diff --git')) {
|
||||
keep = false;
|
||||
// Check if file type should be included
|
||||
if (line.match(/\.(js|ts|py|cpp|h|java|cs)$/) &&
|
||||
!line.match(/(package-lock\.json|yarn\.lock|\.md|\.json)/)) {
|
||||
keep = true;
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
filtered += line + '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
} 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.
|
||||
|
||||
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-sonnet-20240229',
|
||||
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();
|
||||
Reference in New Issue
Block a user