Merge pull request #34 from ingress-it-solutions/dev

Dev
This commit is contained in:
vandanafuletra 2023-05-12 00:52:38 +05:30 committed by GitHub
commit c29eccff0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 400 additions and 420 deletions

View file

@ -27,21 +27,55 @@ inputs:
default: 'github' default: 'github'
PROMPT_TEMPLATE: PROMPT_TEMPLATE:
description: 'The template for the FULL_REVIEW_COMMENT prompt.' description: 'The template for the FULL_REVIEW_COMMENT prompt.'
default: 'Please analyze the pull request''s code and provide me an explanation for your decision in bullet points. Also provide me whether it requires optimization or code correction: default: 'Your task is to act as a code reviewer and review a pull request by summarizing the changes made, identifying potential issues related to logic and runtime, and creating a bullet list of action items needed before the change can be approved. The output should focus on items mentioned in the given code review checklist.
\`\`\` Instructions:
${code} - Review the output of git diff for the pull request
\`\`\`' - Summarize the overview of the changes made
- Identify potential issues related to logic and runtime
- Output as a markdown document, with the following sections:
#### Overview of changes:
- Summarize the overview of the changes made
#### Changelog:
- Summarize the overview in a bullet point to consider it in Change-log.
#### issues:
- Identify potential issues related to logic and runtime
- Identify issues mentioned in the code review checklist
#### Action items:
- Mandatory action items that are must and needed before the change can be approved
#### Joke about this PR:
- Tell me a joke about this Code Review
- If there are no issues, output "None"
- If there are no action items, output "None"
- Create a bullet list of action items needed before the change can be approved
- The response sentences are no longer than 16 words each
- Keep the response sentences as short as possible
- Focus on items mentioned in the given code review checklist:
Code Structure
- Validation
- Business logic should be in service class
- Dont repeat yourself (DRY)
- Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays
- Mass assignment
- Do not execute queries in Blade templates and use eager loading (N + 1 problem)
- Chunk data for data-heavy tasks
- Comment your code, but prefer descriptive method and variable names over comments
- Do not put JS and CSS in Blade templates and do not put any HTML in PHP classes
- Use config and language files, constants instead of text in the code
- Use standard Laravel tools accepted by community
- Follow Laravel naming conventions
- Use shorter and more readable syntax where possible
- Use IoC container or facades instead of new Class
- Are there any unnecessary files, folders, or code modules?
- Does the code follow the Single Responsibility Principle (SRP) and Dont Repeat Yourself (DRY) principle?
Error Handling
- Are all error scenarios covered in the code?
- Are the error messages clear and helpful?
- Is the code handling errors gracefully?
Security
- Are sensitive data and credentials stored securely?
- Are all external libraries and packages up-to-date?
- Is the code protected against common security vulnerabilities such as SQL injection and cross-site scripting (XSS)?
JOKE_TEMPLATE:
description: 'The template for the FULL_REVIEW_JOKE prompt.'
default: 'Please analyze the pull request''s code and can you write a funny joke about it.:
\`\`\`
${code}
\`\`\`'
CODE_TEMPLATE:
description: 'The place for the actual code.'
default: '
\`\`\` \`\`\`
${code} ${code}
\`\`\`' \`\`\`'
@ -49,11 +83,10 @@ ${code}
ANSWER_TEMPLATE: ANSWER_TEMPLATE:
description: 'The template for the answer sent to the GitHub comment.' description: 'The template for the answer sent to the GitHub comment.'
default: 'AI Code Review: default: 'AI Code Review:
=======
### Summary: ### Summary:
${answer}' ${answer}'
runs: runs:
using: 'node16' using: 'node16'
main: 'dist/index.js' main: 'dist/index.js'

375
dist/index.js vendored
View file

@ -39204,202 +39204,176 @@ function configWithProxy(config) {
async function run() { async function run() {
try { try {
// Get input values // Get input values
const programmingLanguage = core.getInput('PROGRAMMING_LANGUAGE'); const programmingLanguage = core.getInput('PROGRAMMING_LANGUAGE');
const openaiToken = core.getInput('OPENAI_TOKEN'); const openaiToken = core.getInput('OPENAI_TOKEN');
const fullReviewComment = core.getInput('FULL_REVIEW_COMMENT'); const fullReviewComment = core.getInput('FULL_REVIEW_COMMENT');
const reviewCommentPrefix = core.getInput('REVIEW_COMMENT_PREFIX'); const reviewCommentPrefix = core.getInput('REVIEW_COMMENT_PREFIX');
const githubToken = core.getInput('GITHUB_TOKEN'); const githubToken = core.getInput('GITHUB_TOKEN');
const githubBaseURL = core.getInput('GITHUB_BASE_URL') || process.env.GITHUB_API_URL; const githubBaseURL = core.getInput('GITHUB_BASE_URL') || process.env.GITHUB_API_URL;
const promptTemplate = core.getInput('PROMPT_TEMPLATE'); const promptTemplate = core.getInput('PROMPT_TEMPLATE');
const codeTemplate = core.getInput('CODE_TEMPLATE'); const maxCodeLength = core.getInput('MAX_CODE_LENGTH');
const jokeTemplate = core.getInput('JOKE_TEMPLATE'); const answerTemplate = core.getInput('ANSWER_TEMPLATE');
const maxCodeLength = core.getInput('MAX_CODE_LENGTH'); const giteaToken = core.getInput('GITHUB_TOKEN');
const answerTemplate = core.getInput('ANSWER_TEMPLATE'); const sourceAt = core.getInput('SOURCE_AT');
const giteaToken = core.getInput('GITHUB_TOKEN');
const sourceAt = core.getInput('SOURCE_AT');
core.debug(`programmingLanguage: ${programmingLanguage}`); core.debug(`programmingLanguage: ${programmingLanguage}`);
core.debug(`openaiToken length: ${openaiToken.length}`); core.debug(`openaiToken length: ${openaiToken.length}`);
core.debug(`fullReviewComment: ${fullReviewComment}`); core.debug(`fullReviewComment: ${fullReviewComment}`);
core.debug(`reviewCommentPrefix: ${reviewCommentPrefix}`); core.debug(`reviewCommentPrefix: ${reviewCommentPrefix}`);
core.debug(`githubToken length: ${githubToken.length}`); core.debug(`githubToken length: ${githubToken.length}`);
core.debug(`githubBaseURL: ${githubBaseURL}`); core.debug(`githubBaseURL: ${githubBaseURL}`);
core.debug(`promptTemplate: ${promptTemplate}`); core.debug(`promptTemplate: ${promptTemplate}`);
core.debug(`codeTemplate: ${codeTemplate}`); core.debug(`maxCodeLength: ${maxCodeLength}`);
core.debug(`jokeTemplate: ${jokeTemplate}`); core.debug(`answerTemplate: ${answerTemplate}`);
core.debug(`maxCodeLength: ${maxCodeLength}`); core.debug(`SourceAt: ${sourceAt}`);
core.debug(`answerTemplate: ${answerTemplate}`);
core.debug(`SourceAt: ${sourceAt}`);
// Get information about the pull request review // Get information about the pull request review
const comment = github.context.payload.comment; const comment = github.context.payload.comment;
const repoName = github.context.payload.repository.name; const repoName = github.context.payload.repository.name;
const repoOwner = github.context.payload.repository.owner.login; const repoOwner = github.context.payload.repository.owner.login;
const prNumber = github.context.payload.number || github.context.payload.issue.number; // get number from a pull request event or comment event const prNumber = github.context.payload.number || github.context.payload.issue.number; // get number from a pull request event or comment event
// Get the code to analyze from the review comment // Get the code to analyze from the review comment
var content = comment && comment.body || ''; var content = comment && comment.body || '';
var completeContent = comment && comment.body || '';
if(sourceAt === 'github') {
const url = `${githubBaseURL}/repos/${repoOwner}/${repoName}/pulls/${prNumber}`; if(sourceAt === 'github') {
console.log(`diff url: ${url}`);
var response = await axios.get(url, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/vnd.github.diff'
}
});
const code = response.data;
core.debug(`diff code: ${code}`);
const files = parsePullRequestDiff(code);
core.debug(`diff files: ${files}`);
if (!content || content == fullReviewComment) { const url = `${githubBaseURL}/repos/${repoOwner}/${repoName}/pulls/${prNumber}`;
// Extract the code from the pull request content console.log(`diff url: ${url}`);
content = codeTemplate.replace('${code}', code); var response = await axios.get(url, {
} else { headers: {
content = content.substring(reviewCommentPrefix.length); Authorization: `token ${githubToken}`,
content = content.replace('${code}', code); Accept: 'application/vnd.github.diff'
const fileNames = findFileNames(content); }
core.debug(`found files name in commment: ${fileNames}`); });
for (const fileName of fileNames) { const code = response.data;
for (const key of Object.keys(files)) { core.debug(`diff code: ${code}`);
if (key.includes(fileName)) { const files = parsePullRequestDiff(code);
core.debug(`replace \${file:${fileName}} with ${key}'s diff`); core.debug(`diff files: ${files}`);
content = content.replace(`\${file:${fileName}}`, files[key]);
break;
}
}
}
}
content = content.substring(0, maxCodeLength);
}
else if(sourceAt === 'gitea')
{
const url = `${githubBaseURL}/api/v1/repos/${repoOwner}/${repoName}/pulls/${prNumber}.diff`;
console.log(`diff url: ${url}`);
var response = await axios.get(url, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/vnd.github.diff'
}
});
const code = response.data;
core.debug(`diff code: ${code}`);
const files = parsePullRequestDiff(code);
core.debug(`diff files: ${files}`);
if (!content || content == fullReviewComment) { if (!content || content == fullReviewComment) {
// Extract the code from the pull request content // Extract the code from the pull request content
content = codeTemplate.replace('${code}', code); content = promptTemplate.replace('${code}', code);
} else { } else {
content = content.substring(reviewCommentPrefix.length); content = content.substring(reviewCommentPrefix.length);
content = content.replace('${code}', code); content = content.replace('${code}', code);
const fileNames = findFileNames(content); const fileNames = findFileNames(content);
core.debug(`found files name in commment: ${fileNames}`); core.debug(`found files name in commment: ${fileNames}`);
for (const fileName of fileNames) { for (const fileName of fileNames) {
for (const key of Object.keys(files)) { for (const key of Object.keys(files)) {
if (key.includes(fileName)) { if (key.includes(fileName)) {
core.debug(`replace \${file:${fileName}} with ${key}'s diff`); core.debug(`replace \${file:${fileName}} with ${key}'s diff`);
content = content.replace(`\${file:${fileName}}`, files[key]); content = content.replace(`\${file:${fileName}}`, files[key]);
break; break;
} }
} }
} }
}
content = content.substring(0, maxCodeLength);
}
// Determine the programming language if it was not provided
if (programmingLanguage == 'auto') {
const detectedLanguage = detect(code);
core.debug(`Detected programming language: ${detectedLanguage}`);
programmingLanguage = detectedLanguage;
}
var messageReview = promptTemplate.replace('${code}', content);
var messageJoke = jokeTemplate.replace('${code}', content);
var reviewInputMessages = [{
role: "system",
content: `You are a master of programming language ${programmingLanguage}`
}, {
role: "user",
content: messageReview
}];
var jokeInputMessages = [{
role: "system",
content: `You are a master of programming language ${programmingLanguage}`
}, {
role: "user",
content: messageJoke
}];
core.debug(`content: ${content}`);
// Call the OpenAI ChatGPT API to analyze the code
responseReview = await axios.post('https://api.openai.com/v1/chat/completions', {
"model": "gpt-3.5-turbo",
"messages": reviewInputMessages
}, configWithProxy({
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openaiToken}`
}
}));
// Call the OpenAI ChatGPT API to analyze the code
responseJoke = await axios.post('https://api.openai.com/v1/chat/completions', {
"model": "gpt-3.5-turbo",
"messages": jokeInputMessages
}, configWithProxy({
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openaiToken}`
}
}));
const answer = response.data.choices[0].message.content + '/n/n' + '### Funny Joke about this PR:' +'/n/n' + responseJoke.data.choices[0].message.content;
core.debug(`openai response: ${answer}`);
if(sourceAt === 'github') {
// Reply to the review comment with the OpenAI response
const octokit = new github.getOctokit(githubToken, {
baseUrl: githubBaseURL
});
await octokit.rest.issues.createComment({
owner: repoOwner,
repo: repoName,
issue_number: prNumber,
body: answerTemplate.replace('${answer}', answer)
});
} else if (sourceAt === 'gitea')
{
// Make a POST request to create a comment on a pull request
const comment = answerTemplate.replace('${answer}', answer);
const url = `${githubBaseURL}/api/v1/repos/${repoOwner}/${repoName}/issues/${prNumber}/comments`;
const headers = { 'Content-Type': 'application/json', 'Authorization': `token ${githubToken}` };
const data = { 'body': `${comment}`};
core.debug(`url: ${url}`);
core.debug(`githubToken: ${githubToken}`);
core.debug(`data.body: ${data.body}`);
var response = await axios.post(url, data, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/json'
} }
}); content = content.substring(0, maxCodeLength);
}
else if(sourceAt === 'gitea')
{
const url = `${githubBaseURL}/api/v1/repos/${repoOwner}/${repoName}/pulls/${prNumber}.diff`;
console.log(`diff url: ${url}`);
var response = await axios.get(url, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/vnd.github.diff'
}
});
const code = response.data;
core.debug(`diff code: ${code}`);
const files = parsePullRequestDiff(code);
core.debug(`diff files: ${files}`);
if (!content || content == fullReviewComment) {
// Extract the code from the pull request content
content = promptTemplate.replace('${code}', code);
} else {
content = content.substring(reviewCommentPrefix.length);
content = content.replace('${code}', code);
const fileNames = findFileNames(content);
core.debug(`found files name in commment: ${fileNames}`);
for (const fileName of fileNames) {
for (const key of Object.keys(files)) {
if (key.includes(fileName)) {
core.debug(`replace \${file:${fileName}} with ${key}'s diff`);
content = content.replace(`\${file:${fileName}}`, files[key]);
break;
}
}
}
}
content = content.substring(0, maxCodeLength);
}
// Determine the programming language if it was not provided
if (programmingLanguage == 'auto') {
const detectedLanguage = detect(code);
core.debug(`Detected programming language: ${detectedLanguage}`);
programmingLanguage = detectedLanguage;
}
var messages = [{
role: "system",
content: `You are a master of programming language ${programmingLanguage}`
}, {
role: "user",
content: content
}];
core.debug(`content: ${content}`);
// Call the OpenAI ChatGPT API to analyze the code
response = await axios.post('https://api.openai.com/v1/chat/completions', {
"model": "gpt-3.5-turbo",
"messages": messages
}, configWithProxy({
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openaiToken}`
}
}));
const answer = response.data.choices[0].message.content;
core.debug(`openai response: ${answer}`);
if(sourceAt === 'github') {
// Reply to the review comment with the OpenAI response
const octokit = new github.getOctokit(githubToken, {
baseUrl: githubBaseURL
});
await octokit.rest.issues.createComment({
owner: repoOwner,
repo: repoName,
issue_number: prNumber,
body: answerTemplate.replace('${answer}', answer)
});
} else if (sourceAt === 'gitea')
{
// Make a POST request to create a comment on a pull request
const comment = answerTemplate.replace('${answer}', answer);
const url = `${githubBaseURL}/api/v1/repos/${repoOwner}/${repoName}/issues/${prNumber}/comments`;
const headers = { 'Content-Type': 'application/json', 'Authorization': `token ${githubToken}` };
const data = { 'body': `${comment}`};
core.debug(`url: ${url}`);
core.debug(`githubToken: ${githubToken}`);
core.debug(`data.body: ${data.body}`);
var response = await axios.post(url, data, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/json'
}
});
}
} catch (error) {
core.setFailed(error.message);
} }
} catch (error) {
core.setFailed(error.message);
}
} }
function parsePullRequestDiff(diffContent) { function parsePullRequestDiff(diffContent) {
@ -39410,22 +39384,22 @@ function parsePullRequestDiff(diffContent) {
let currentLines = []; let currentLines = [];
for (const line of diffLines) { for (const line of diffLines) {
if (line.startsWith('diff --git')) { if (line.startsWith('diff --git')) {
// Start of a new file // Start of a new file
if (currentFile) { if (currentFile) {
files[currentFile] = currentLines.join('\n'); files[currentFile] = currentLines.join('\n');
}
currentFile = line.substring('diff --git'.length + 1);
currentLines = [line];
} else {
// Add the line to the current file's diff
currentLines.push(line);
} }
currentFile = line.substring('diff --git'.length + 1);
currentLines = [line];
} else {
// Add the line to the current file's diff
currentLines.push(line);
}
} }
// Add the last file's diff // Add the last file's diff
if (currentFile) { if (currentFile) {
files[currentFile] = currentLines.join('\n'); files[currentFile] = currentLines.join('\n');
} }
return files; return files;
@ -39436,13 +39410,12 @@ function findFileNames(str) {
const matches = str.matchAll(pattern); const matches = str.matchAll(pattern);
const names = []; const names = [];
for (const match of matches) { for (const match of matches) {
names.push(match[1]); names.push(match[1]);
} }
return names; return names;
} }
run(); run();
})(); })();
module.exports = __webpack_exports__; module.exports = __webpack_exports__;

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

374
index.js
View file

@ -18,202 +18,176 @@ function configWithProxy(config) {
async function run() { async function run() {
try { try {
// Get input values // Get input values
const programmingLanguage = core.getInput('PROGRAMMING_LANGUAGE'); const programmingLanguage = core.getInput('PROGRAMMING_LANGUAGE');
const openaiToken = core.getInput('OPENAI_TOKEN'); const openaiToken = core.getInput('OPENAI_TOKEN');
const fullReviewComment = core.getInput('FULL_REVIEW_COMMENT'); const fullReviewComment = core.getInput('FULL_REVIEW_COMMENT');
const reviewCommentPrefix = core.getInput('REVIEW_COMMENT_PREFIX'); const reviewCommentPrefix = core.getInput('REVIEW_COMMENT_PREFIX');
const githubToken = core.getInput('GITHUB_TOKEN'); const githubToken = core.getInput('GITHUB_TOKEN');
const githubBaseURL = core.getInput('GITHUB_BASE_URL') || process.env.GITHUB_API_URL; const githubBaseURL = core.getInput('GITHUB_BASE_URL') || process.env.GITHUB_API_URL;
const promptTemplate = core.getInput('PROMPT_TEMPLATE'); const promptTemplate = core.getInput('PROMPT_TEMPLATE');
const codeTemplate = core.getInput('CODE_TEMPLATE'); const maxCodeLength = core.getInput('MAX_CODE_LENGTH');
const jokeTemplate = core.getInput('JOKE_TEMPLATE'); const answerTemplate = core.getInput('ANSWER_TEMPLATE');
const maxCodeLength = core.getInput('MAX_CODE_LENGTH'); const giteaToken = core.getInput('GITHUB_TOKEN');
const answerTemplate = core.getInput('ANSWER_TEMPLATE'); const sourceAt = core.getInput('SOURCE_AT');
const giteaToken = core.getInput('GITHUB_TOKEN');
const sourceAt = core.getInput('SOURCE_AT');
core.debug(`programmingLanguage: ${programmingLanguage}`); core.debug(`programmingLanguage: ${programmingLanguage}`);
core.debug(`openaiToken length: ${openaiToken.length}`); core.debug(`openaiToken length: ${openaiToken.length}`);
core.debug(`fullReviewComment: ${fullReviewComment}`); core.debug(`fullReviewComment: ${fullReviewComment}`);
core.debug(`reviewCommentPrefix: ${reviewCommentPrefix}`); core.debug(`reviewCommentPrefix: ${reviewCommentPrefix}`);
core.debug(`githubToken length: ${githubToken.length}`); core.debug(`githubToken length: ${githubToken.length}`);
core.debug(`githubBaseURL: ${githubBaseURL}`); core.debug(`githubBaseURL: ${githubBaseURL}`);
core.debug(`promptTemplate: ${promptTemplate}`); core.debug(`promptTemplate: ${promptTemplate}`);
core.debug(`codeTemplate: ${codeTemplate}`); core.debug(`maxCodeLength: ${maxCodeLength}`);
core.debug(`jokeTemplate: ${jokeTemplate}`); core.debug(`answerTemplate: ${answerTemplate}`);
core.debug(`maxCodeLength: ${maxCodeLength}`); core.debug(`SourceAt: ${sourceAt}`);
core.debug(`answerTemplate: ${answerTemplate}`);
core.debug(`SourceAt: ${sourceAt}`);
// Get information about the pull request review // Get information about the pull request review
const comment = github.context.payload.comment; const comment = github.context.payload.comment;
const repoName = github.context.payload.repository.name; const repoName = github.context.payload.repository.name;
const repoOwner = github.context.payload.repository.owner.login; const repoOwner = github.context.payload.repository.owner.login;
const prNumber = github.context.payload.number || github.context.payload.issue.number; // get number from a pull request event or comment event const prNumber = github.context.payload.number || github.context.payload.issue.number; // get number from a pull request event or comment event
// Get the code to analyze from the review comment // Get the code to analyze from the review comment
var content = comment && comment.body || ''; var content = comment && comment.body || '';
var completeContent = comment && comment.body || '';
if(sourceAt === 'github') {
const url = `${githubBaseURL}/repos/${repoOwner}/${repoName}/pulls/${prNumber}`; if(sourceAt === 'github') {
console.log(`diff url: ${url}`);
var response = await axios.get(url, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/vnd.github.diff'
}
});
const code = response.data;
core.debug(`diff code: ${code}`);
const files = parsePullRequestDiff(code);
core.debug(`diff files: ${files}`);
if (!content || content == fullReviewComment) { const url = `${githubBaseURL}/repos/${repoOwner}/${repoName}/pulls/${prNumber}`;
// Extract the code from the pull request content console.log(`diff url: ${url}`);
content = codeTemplate.replace('${code}', code); var response = await axios.get(url, {
} else { headers: {
content = content.substring(reviewCommentPrefix.length); Authorization: `token ${githubToken}`,
content = content.replace('${code}', code); Accept: 'application/vnd.github.diff'
const fileNames = findFileNames(content); }
core.debug(`found files name in commment: ${fileNames}`); });
for (const fileName of fileNames) { const code = response.data;
for (const key of Object.keys(files)) { core.debug(`diff code: ${code}`);
if (key.includes(fileName)) { const files = parsePullRequestDiff(code);
core.debug(`replace \${file:${fileName}} with ${key}'s diff`); core.debug(`diff files: ${files}`);
content = content.replace(`\${file:${fileName}}`, files[key]);
break;
}
}
}
}
content = content.substring(0, maxCodeLength);
}
else if(sourceAt === 'gitea')
{
const url = `${githubBaseURL}/api/v1/repos/${repoOwner}/${repoName}/pulls/${prNumber}.diff`;
console.log(`diff url: ${url}`);
var response = await axios.get(url, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/vnd.github.diff'
}
});
const code = response.data;
core.debug(`diff code: ${code}`);
const files = parsePullRequestDiff(code);
core.debug(`diff files: ${files}`);
if (!content || content == fullReviewComment) { if (!content || content == fullReviewComment) {
// Extract the code from the pull request content // Extract the code from the pull request content
content = codeTemplate.replace('${code}', code); content = promptTemplate.replace('${code}', code);
} else { } else {
content = content.substring(reviewCommentPrefix.length); content = content.substring(reviewCommentPrefix.length);
content = content.replace('${code}', code); content = content.replace('${code}', code);
const fileNames = findFileNames(content); const fileNames = findFileNames(content);
core.debug(`found files name in commment: ${fileNames}`); core.debug(`found files name in commment: ${fileNames}`);
for (const fileName of fileNames) { for (const fileName of fileNames) {
for (const key of Object.keys(files)) { for (const key of Object.keys(files)) {
if (key.includes(fileName)) { if (key.includes(fileName)) {
core.debug(`replace \${file:${fileName}} with ${key}'s diff`); core.debug(`replace \${file:${fileName}} with ${key}'s diff`);
content = content.replace(`\${file:${fileName}}`, files[key]); content = content.replace(`\${file:${fileName}}`, files[key]);
break; break;
} }
} }
} }
}
content = content.substring(0, maxCodeLength);
}
// Determine the programming language if it was not provided
if (programmingLanguage == 'auto') {
const detectedLanguage = detect(code);
core.debug(`Detected programming language: ${detectedLanguage}`);
programmingLanguage = detectedLanguage;
}
var messageReview = promptTemplate.replace('${code}', content);
var messageJoke = jokeTemplate.replace('${code}', content);
var reviewInputMessages = [{
role: "system",
content: `You are a master of programming language ${programmingLanguage}`
}, {
role: "user",
content: messageReview
}];
var jokeInputMessages = [{
role: "system",
content: `You are a master of programming language ${programmingLanguage}`
}, {
role: "user",
content: messageJoke
}];
core.debug(`content: ${content}`);
// Call the OpenAI ChatGPT API to analyze the code
responseReview = await axios.post('https://api.openai.com/v1/chat/completions', {
"model": "gpt-3.5-turbo",
"messages": reviewInputMessages
}, configWithProxy({
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openaiToken}`
}
}));
// Call the OpenAI ChatGPT API to analyze the code
responseJoke = await axios.post('https://api.openai.com/v1/chat/completions', {
"model": "gpt-3.5-turbo",
"messages": jokeInputMessages
}, configWithProxy({
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openaiToken}`
}
}));
const answer = response.data.choices[0].message.content + '/n/n' + '### Funny Joke about this PR:' +'/n/n' + responseJoke.data.choices[0].message.content;
core.debug(`openai response: ${answer}`);
if(sourceAt === 'github') {
// Reply to the review comment with the OpenAI response
const octokit = new github.getOctokit(githubToken, {
baseUrl: githubBaseURL
});
await octokit.rest.issues.createComment({
owner: repoOwner,
repo: repoName,
issue_number: prNumber,
body: answerTemplate.replace('${answer}', answer)
});
} else if (sourceAt === 'gitea')
{
// Make a POST request to create a comment on a pull request
const comment = answerTemplate.replace('${answer}', answer);
const url = `${githubBaseURL}/api/v1/repos/${repoOwner}/${repoName}/issues/${prNumber}/comments`;
const headers = { 'Content-Type': 'application/json', 'Authorization': `token ${githubToken}` };
const data = { 'body': `${comment}`};
core.debug(`url: ${url}`);
core.debug(`githubToken: ${githubToken}`);
core.debug(`data.body: ${data.body}`);
var response = await axios.post(url, data, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/json'
} }
}); content = content.substring(0, maxCodeLength);
}
else if(sourceAt === 'gitea')
{
const url = `${githubBaseURL}/api/v1/repos/${repoOwner}/${repoName}/pulls/${prNumber}.diff`;
console.log(`diff url: ${url}`);
var response = await axios.get(url, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/vnd.github.diff'
}
});
const code = response.data;
core.debug(`diff code: ${code}`);
const files = parsePullRequestDiff(code);
core.debug(`diff files: ${files}`);
if (!content || content == fullReviewComment) {
// Extract the code from the pull request content
content = promptTemplate.replace('${code}', code);
} else {
content = content.substring(reviewCommentPrefix.length);
content = content.replace('${code}', code);
const fileNames = findFileNames(content);
core.debug(`found files name in commment: ${fileNames}`);
for (const fileName of fileNames) {
for (const key of Object.keys(files)) {
if (key.includes(fileName)) {
core.debug(`replace \${file:${fileName}} with ${key}'s diff`);
content = content.replace(`\${file:${fileName}}`, files[key]);
break;
}
}
}
}
content = content.substring(0, maxCodeLength);
}
// Determine the programming language if it was not provided
if (programmingLanguage == 'auto') {
const detectedLanguage = detect(code);
core.debug(`Detected programming language: ${detectedLanguage}`);
programmingLanguage = detectedLanguage;
}
var messages = [{
role: "system",
content: `You are a master of programming language ${programmingLanguage}`
}, {
role: "user",
content: content
}];
core.debug(`content: ${content}`);
// Call the OpenAI ChatGPT API to analyze the code
response = await axios.post('https://api.openai.com/v1/chat/completions', {
"model": "gpt-3.5-turbo",
"messages": messages
}, configWithProxy({
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openaiToken}`
}
}));
const answer = response.data.choices[0].message.content;
core.debug(`openai response: ${answer}`);
if(sourceAt === 'github') {
// Reply to the review comment with the OpenAI response
const octokit = new github.getOctokit(githubToken, {
baseUrl: githubBaseURL
});
await octokit.rest.issues.createComment({
owner: repoOwner,
repo: repoName,
issue_number: prNumber,
body: answerTemplate.replace('${answer}', answer)
});
} else if (sourceAt === 'gitea')
{
// Make a POST request to create a comment on a pull request
const comment = answerTemplate.replace('${answer}', answer);
const url = `${githubBaseURL}/api/v1/repos/${repoOwner}/${repoName}/issues/${prNumber}/comments`;
const headers = { 'Content-Type': 'application/json', 'Authorization': `token ${githubToken}` };
const data = { 'body': `${comment}`};
core.debug(`url: ${url}`);
core.debug(`githubToken: ${githubToken}`);
core.debug(`data.body: ${data.body}`);
var response = await axios.post(url, data, {
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/json'
}
});
}
} catch (error) {
core.setFailed(error.message);
} }
} catch (error) {
core.setFailed(error.message);
}
} }
function parsePullRequestDiff(diffContent) { function parsePullRequestDiff(diffContent) {
@ -224,22 +198,22 @@ function parsePullRequestDiff(diffContent) {
let currentLines = []; let currentLines = [];
for (const line of diffLines) { for (const line of diffLines) {
if (line.startsWith('diff --git')) { if (line.startsWith('diff --git')) {
// Start of a new file // Start of a new file
if (currentFile) { if (currentFile) {
files[currentFile] = currentLines.join('\n'); files[currentFile] = currentLines.join('\n');
}
currentFile = line.substring('diff --git'.length + 1);
currentLines = [line];
} else {
// Add the line to the current file's diff
currentLines.push(line);
} }
currentFile = line.substring('diff --git'.length + 1);
currentLines = [line];
} else {
// Add the line to the current file's diff
currentLines.push(line);
}
} }
// Add the last file's diff // Add the last file's diff
if (currentFile) { if (currentFile) {
files[currentFile] = currentLines.join('\n'); files[currentFile] = currentLines.join('\n');
} }
return files; return files;
@ -250,7 +224,7 @@ function findFileNames(str) {
const matches = str.matchAll(pattern); const matches = str.matchAll(pattern);
const names = []; const names = [];
for (const match of matches) { for (const match of matches) {
names.push(match[1]); names.push(match[1]);
} }
return names; return names;
} }