mirror of
				https://github.com/ingress-it-solutions/gitea-code-review-action.git
				synced 2025-10-30 22:11:08 +00:00 
			
		
		
		
	
				commit
				
					
						c29eccff0d
					
				
			
		
					 4 changed files with 400 additions and 420 deletions
				
			
		
							
								
								
									
										67
									
								
								action.yaml
									
										
									
									
									
								
							
							
						
						
									
										67
									
								
								action.yaml
									
										
									
									
									
								
							|  | @ -27,21 +27,55 @@ inputs: | |||
|     default: 'github' | ||||
|   PROMPT_TEMPLATE: | ||||
|     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: | ||||
| \`\`\` | ||||
| ${code} | ||||
| \`\`\`' | ||||
| 
 | ||||
|   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: ' | ||||
|     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: | ||||
|     - 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)? | ||||
|          | ||||
| \`\`\` | ||||
| ${code} | ||||
| \`\`\`' | ||||
|  | @ -49,11 +83,10 @@ ${code} | |||
|   ANSWER_TEMPLATE: | ||||
|     description: 'The template for the answer sent to the GitHub comment.' | ||||
|     default: 'AI Code Review: | ||||
| 
 | ||||
| ======= | ||||
| ### Summary: | ||||
| 
 | ||||
| ${answer}' | ||||
| 
 | ||||
| runs: | ||||
|   using: 'node16' | ||||
|   main: 'dist/index.js' | ||||
|  |  | |||
							
								
								
									
										375
									
								
								dist/index.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										375
									
								
								dist/index.js
									
										
									
									
										vendored
									
									
								
							|  | @ -39204,202 +39204,176 @@ function configWithProxy(config) { | |||
| 
 | ||||
| 
 | ||||
| async function run() { | ||||
|   try { | ||||
|     // Get input values
 | ||||
|     const programmingLanguage = core.getInput('PROGRAMMING_LANGUAGE'); | ||||
|     const openaiToken = core.getInput('OPENAI_TOKEN'); | ||||
|     const fullReviewComment = core.getInput('FULL_REVIEW_COMMENT'); | ||||
|     const reviewCommentPrefix = core.getInput('REVIEW_COMMENT_PREFIX'); | ||||
|     const githubToken = core.getInput('GITHUB_TOKEN'); | ||||
|     const githubBaseURL = core.getInput('GITHUB_BASE_URL') || process.env.GITHUB_API_URL; | ||||
|     const promptTemplate = core.getInput('PROMPT_TEMPLATE'); | ||||
|     const codeTemplate = core.getInput('CODE_TEMPLATE'); | ||||
|     const jokeTemplate = core.getInput('JOKE_TEMPLATE'); | ||||
|     const maxCodeLength = core.getInput('MAX_CODE_LENGTH'); | ||||
|     const answerTemplate = core.getInput('ANSWER_TEMPLATE'); | ||||
|     const giteaToken = core.getInput('GITHUB_TOKEN'); | ||||
|     const sourceAt = core.getInput('SOURCE_AT'); | ||||
|     try { | ||||
|         // Get input values
 | ||||
|         const programmingLanguage = core.getInput('PROGRAMMING_LANGUAGE'); | ||||
|         const openaiToken = core.getInput('OPENAI_TOKEN'); | ||||
|         const fullReviewComment = core.getInput('FULL_REVIEW_COMMENT'); | ||||
|         const reviewCommentPrefix = core.getInput('REVIEW_COMMENT_PREFIX'); | ||||
|         const githubToken = core.getInput('GITHUB_TOKEN'); | ||||
|         const githubBaseURL = core.getInput('GITHUB_BASE_URL') || process.env.GITHUB_API_URL; | ||||
|         const promptTemplate = core.getInput('PROMPT_TEMPLATE'); | ||||
|         const maxCodeLength = core.getInput('MAX_CODE_LENGTH'); | ||||
|         const answerTemplate = core.getInput('ANSWER_TEMPLATE'); | ||||
|         const giteaToken = core.getInput('GITHUB_TOKEN'); | ||||
|         const sourceAt = core.getInput('SOURCE_AT'); | ||||
| 
 | ||||
|     core.debug(`programmingLanguage: ${programmingLanguage}`); | ||||
|     core.debug(`openaiToken length: ${openaiToken.length}`); | ||||
|     core.debug(`fullReviewComment: ${fullReviewComment}`); | ||||
|     core.debug(`reviewCommentPrefix: ${reviewCommentPrefix}`); | ||||
|     core.debug(`githubToken length: ${githubToken.length}`); | ||||
|     core.debug(`githubBaseURL: ${githubBaseURL}`); | ||||
|     core.debug(`promptTemplate: ${promptTemplate}`); | ||||
|     core.debug(`codeTemplate: ${codeTemplate}`); | ||||
|     core.debug(`jokeTemplate: ${jokeTemplate}`); | ||||
|     core.debug(`maxCodeLength: ${maxCodeLength}`); | ||||
|     core.debug(`answerTemplate: ${answerTemplate}`); | ||||
|     core.debug(`SourceAt: ${sourceAt}`); | ||||
|         core.debug(`programmingLanguage: ${programmingLanguage}`); | ||||
|         core.debug(`openaiToken length: ${openaiToken.length}`); | ||||
|         core.debug(`fullReviewComment: ${fullReviewComment}`); | ||||
|         core.debug(`reviewCommentPrefix: ${reviewCommentPrefix}`); | ||||
|         core.debug(`githubToken length: ${githubToken.length}`); | ||||
|         core.debug(`githubBaseURL: ${githubBaseURL}`); | ||||
|         core.debug(`promptTemplate: ${promptTemplate}`); | ||||
|         core.debug(`maxCodeLength: ${maxCodeLength}`); | ||||
|         core.debug(`answerTemplate: ${answerTemplate}`); | ||||
|         core.debug(`SourceAt: ${sourceAt}`); | ||||
| 
 | ||||
|     // Get information about the pull request review
 | ||||
|     const comment = github.context.payload.comment; | ||||
|     const repoName = github.context.payload.repository.name; | ||||
|     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
 | ||||
|         // Get information about the pull request review
 | ||||
|         const comment = github.context.payload.comment; | ||||
|         const repoName = github.context.payload.repository.name; | ||||
|         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
 | ||||
| 
 | ||||
|     // Get the code to analyze from the review comment
 | ||||
|       var content = comment && comment.body || ''; | ||||
|       var completeContent = comment && comment.body || ''; | ||||
|       if(sourceAt === 'github') { | ||||
|         // Get the code to analyze from the review comment
 | ||||
|         var content = comment && comment.body || ''; | ||||
| 
 | ||||
|           const url = `${githubBaseURL}/repos/${repoOwner}/${repoName}/pulls/${prNumber}`; | ||||
|           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(sourceAt === 'github') { | ||||
| 
 | ||||
|           if (!content || content == fullReviewComment) { | ||||
|               // Extract the code from the pull request content
 | ||||
|               content = codeTemplate.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); | ||||
|       } | ||||
|       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}`); | ||||
|             const url = `${githubBaseURL}/repos/${repoOwner}/${repoName}/pulls/${prNumber}`; | ||||
|             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 = codeTemplate.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 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' | ||||
|             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); | ||||
|         } | ||||
|         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) { | ||||
|  | @ -39410,22 +39384,22 @@ function parsePullRequestDiff(diffContent) { | |||
|     let currentLines = []; | ||||
| 
 | ||||
|     for (const line of diffLines) { | ||||
|       if (line.startsWith('diff --git')) { | ||||
|         // Start of a new file
 | ||||
|         if (currentFile) { | ||||
|           files[currentFile] = currentLines.join('\n'); | ||||
|         if (line.startsWith('diff --git')) { | ||||
|             // Start of a new file
 | ||||
|             if (currentFile) { | ||||
|                 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
 | ||||
|     if (currentFile) { | ||||
|       files[currentFile] = currentLines.join('\n'); | ||||
|         files[currentFile] = currentLines.join('\n'); | ||||
|     } | ||||
| 
 | ||||
|     return files; | ||||
|  | @ -39436,13 +39410,12 @@ function findFileNames(str) { | |||
|     const matches = str.matchAll(pattern); | ||||
|     const names = []; | ||||
|     for (const match of matches) { | ||||
|       names.push(match[1]); | ||||
|         names.push(match[1]); | ||||
|     } | ||||
|     return names; | ||||
| } | ||||
| 
 | ||||
| run(); | ||||
| 
 | ||||
| })(); | ||||
| 
 | ||||
| module.exports = __webpack_exports__; | ||||
|  |  | |||
							
								
								
									
										2
									
								
								dist/index.js.map
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/index.js.map
									
										
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										376
									
								
								index.js
									
										
									
									
									
								
							
							
						
						
									
										376
									
								
								index.js
									
										
									
									
									
								
							|  | @ -18,202 +18,176 @@ function configWithProxy(config) { | |||
| 
 | ||||
| 
 | ||||
| async function run() { | ||||
|   try { | ||||
|     // Get input values
 | ||||
|     const programmingLanguage = core.getInput('PROGRAMMING_LANGUAGE'); | ||||
|     const openaiToken = core.getInput('OPENAI_TOKEN'); | ||||
|     const fullReviewComment = core.getInput('FULL_REVIEW_COMMENT'); | ||||
|     const reviewCommentPrefix = core.getInput('REVIEW_COMMENT_PREFIX'); | ||||
|     const githubToken = core.getInput('GITHUB_TOKEN'); | ||||
|     const githubBaseURL = core.getInput('GITHUB_BASE_URL') || process.env.GITHUB_API_URL; | ||||
|     const promptTemplate = core.getInput('PROMPT_TEMPLATE'); | ||||
|     const codeTemplate = core.getInput('CODE_TEMPLATE'); | ||||
|     const jokeTemplate = core.getInput('JOKE_TEMPLATE'); | ||||
|     const maxCodeLength = core.getInput('MAX_CODE_LENGTH'); | ||||
|     const answerTemplate = core.getInput('ANSWER_TEMPLATE'); | ||||
|     const giteaToken = core.getInput('GITHUB_TOKEN'); | ||||
|     const sourceAt = core.getInput('SOURCE_AT'); | ||||
|     try { | ||||
|         // Get input values
 | ||||
|         const programmingLanguage = core.getInput('PROGRAMMING_LANGUAGE'); | ||||
|         const openaiToken = core.getInput('OPENAI_TOKEN'); | ||||
|         const fullReviewComment = core.getInput('FULL_REVIEW_COMMENT'); | ||||
|         const reviewCommentPrefix = core.getInput('REVIEW_COMMENT_PREFIX'); | ||||
|         const githubToken = core.getInput('GITHUB_TOKEN'); | ||||
|         const githubBaseURL = core.getInput('GITHUB_BASE_URL') || process.env.GITHUB_API_URL; | ||||
|         const promptTemplate = core.getInput('PROMPT_TEMPLATE'); | ||||
|         const maxCodeLength = core.getInput('MAX_CODE_LENGTH'); | ||||
|         const answerTemplate = core.getInput('ANSWER_TEMPLATE'); | ||||
|         const giteaToken = core.getInput('GITHUB_TOKEN'); | ||||
|         const sourceAt = core.getInput('SOURCE_AT'); | ||||
| 
 | ||||
|     core.debug(`programmingLanguage: ${programmingLanguage}`); | ||||
|     core.debug(`openaiToken length: ${openaiToken.length}`); | ||||
|     core.debug(`fullReviewComment: ${fullReviewComment}`); | ||||
|     core.debug(`reviewCommentPrefix: ${reviewCommentPrefix}`); | ||||
|     core.debug(`githubToken length: ${githubToken.length}`); | ||||
|     core.debug(`githubBaseURL: ${githubBaseURL}`); | ||||
|     core.debug(`promptTemplate: ${promptTemplate}`); | ||||
|     core.debug(`codeTemplate: ${codeTemplate}`); | ||||
|     core.debug(`jokeTemplate: ${jokeTemplate}`); | ||||
|     core.debug(`maxCodeLength: ${maxCodeLength}`); | ||||
|     core.debug(`answerTemplate: ${answerTemplate}`); | ||||
|     core.debug(`SourceAt: ${sourceAt}`); | ||||
|         core.debug(`programmingLanguage: ${programmingLanguage}`); | ||||
|         core.debug(`openaiToken length: ${openaiToken.length}`); | ||||
|         core.debug(`fullReviewComment: ${fullReviewComment}`); | ||||
|         core.debug(`reviewCommentPrefix: ${reviewCommentPrefix}`); | ||||
|         core.debug(`githubToken length: ${githubToken.length}`); | ||||
|         core.debug(`githubBaseURL: ${githubBaseURL}`); | ||||
|         core.debug(`promptTemplate: ${promptTemplate}`); | ||||
|         core.debug(`maxCodeLength: ${maxCodeLength}`); | ||||
|         core.debug(`answerTemplate: ${answerTemplate}`); | ||||
|         core.debug(`SourceAt: ${sourceAt}`); | ||||
| 
 | ||||
|     // Get information about the pull request review
 | ||||
|     const comment = github.context.payload.comment; | ||||
|     const repoName = github.context.payload.repository.name; | ||||
|     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
 | ||||
|         // Get information about the pull request review
 | ||||
|         const comment = github.context.payload.comment; | ||||
|         const repoName = github.context.payload.repository.name; | ||||
|         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
 | ||||
| 
 | ||||
|     // Get the code to analyze from the review comment
 | ||||
|       var content = comment && comment.body || ''; | ||||
|       var completeContent = comment && comment.body || ''; | ||||
|       if(sourceAt === 'github') { | ||||
|         // Get the code to analyze from the review comment
 | ||||
|         var content = comment && comment.body || ''; | ||||
| 
 | ||||
|           const url = `${githubBaseURL}/repos/${repoOwner}/${repoName}/pulls/${prNumber}`; | ||||
|           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(sourceAt === 'github') { | ||||
| 
 | ||||
|           if (!content || content == fullReviewComment) { | ||||
|               // Extract the code from the pull request content
 | ||||
|               content = codeTemplate.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); | ||||
|       } | ||||
|       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}`); | ||||
|             const url = `${githubBaseURL}/repos/${repoOwner}/${repoName}/pulls/${prNumber}`; | ||||
|             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 = codeTemplate.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 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' | ||||
|             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); | ||||
|         } | ||||
|         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) { | ||||
|  | @ -224,22 +198,22 @@ function parsePullRequestDiff(diffContent) { | |||
|     let currentLines = []; | ||||
| 
 | ||||
|     for (const line of diffLines) { | ||||
|       if (line.startsWith('diff --git')) { | ||||
|         // Start of a new file
 | ||||
|         if (currentFile) { | ||||
|           files[currentFile] = currentLines.join('\n'); | ||||
|         if (line.startsWith('diff --git')) { | ||||
|             // Start of a new file
 | ||||
|             if (currentFile) { | ||||
|                 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
 | ||||
|     if (currentFile) { | ||||
|       files[currentFile] = currentLines.join('\n'); | ||||
|         files[currentFile] = currentLines.join('\n'); | ||||
|     } | ||||
| 
 | ||||
|     return files; | ||||
|  | @ -250,9 +224,9 @@ function findFileNames(str) { | |||
|     const matches = str.matchAll(pattern); | ||||
|     const names = []; | ||||
|     for (const match of matches) { | ||||
|       names.push(match[1]); | ||||
|         names.push(match[1]); | ||||
|     } | ||||
|     return names; | ||||
| } | ||||
| 
 | ||||
| run(); | ||||
| run(); | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue