Add a limit for file number, improve the C# handling

This commit is contained in:
Alejandro Ramirez 2024-11-25 12:43:31 -05:00
parent a9a064dfa1
commit 9c6f0593c9
4 changed files with 90 additions and 15 deletions

View file

@ -20,6 +20,7 @@
"ts-node": "^10.9.1" "ts-node": "^10.9.1"
}, },
"devDependencies": { "devDependencies": {
"@types/minimatch": "^5.1.2",
"@types/node": "^18.15.5", "@types/node": "^18.15.5",
"@vercel/ncc": "^0.36.1", "@vercel/ncc": "^0.36.1",
"prettier": "^2.8.6", "prettier": "^2.8.6",

View file

@ -2,12 +2,13 @@ import { readFileSync } from "fs";
import * as core from "@actions/core"; import * as core from "@actions/core";
import OpenAI from "openai"; import OpenAI from "openai";
import { Octokit } from "@octokit/rest"; import { Octokit } from "@octokit/rest";
import parseDiff, { Chunk, File } from "parse-diff"; import parseDiff, { Chunk, File, Change } from "parse-diff";
import minimatch from "minimatch"; import minimatch from "minimatch";
const GITHUB_TOKEN: string = core.getInput("GITHUB_TOKEN"); const GITHUB_TOKEN: string = core.getInput("GITHUB_TOKEN");
const OPENAI_API_KEY: string = core.getInput("OPENAI_API_KEY"); const OPENAI_API_KEY: string = core.getInput("OPENAI_API_KEY");
const OPENAI_API_MODEL: string = core.getInput("OPENAI_API_MODEL"); const OPENAI_API_MODEL: string = core.getInput("OPENAI_API_MODEL");
const MAX_FILES: number = 25;
const octokit = new Octokit({ auth: GITHUB_TOKEN }); const octokit = new Octokit({ auth: GITHUB_TOKEN });
@ -46,14 +47,36 @@ async function getDiff(
repo: string, repo: string,
pull_number: number pull_number: number
): Promise<string | null> { ): Promise<string | null> {
const response = await octokit.pulls.get({ try {
owner, const response = await octokit.request('GET /repos/{owner}/{repo}/pulls/{pull_number}', {
repo, owner,
pull_number, repo,
mediaType: { format: "diff" }, pull_number,
}); mediaType: {
// @ts-expect-error - response.data is a string format: 'diff',
return response.data; },
});
return response.data as unknown as string;
} catch (error) {
console.error('Error fetching diff:', error);
return null;
}
}
function getLanguageContext(filename: string): string {
if (filename.endsWith('.cs')) {
return `This is a C# file using .NET 8 features. Consider the following when reviewing:
- C# 12 features like primary constructors, collection expressions, and inline arrays
- .NET 8 features including native AOT compilation considerations
- Performance implications and best practices
- Dependency injection patterns
- Async/await usage
- SOLID principles
- Nullable reference types
- Record types and pattern matching
- Memory management and disposable resources`;
}
return '';
} }
async function analyzeCode( async function analyzeCode(
@ -62,7 +85,16 @@ async function analyzeCode(
): Promise<Array<{ body: string; path: string; line: number }>> { ): Promise<Array<{ body: string; path: string; line: number }>> {
const comments: Array<{ body: string; path: string; line: number }> = []; const comments: Array<{ body: string; path: string; line: number }> = [];
for (const file of parsedDiff) { // If there are more than MAX_FILES, only process the first MAX_FILES
const filesToProcess = parsedDiff.length > MAX_FILES
? parsedDiff.slice(0, MAX_FILES)
: parsedDiff;
if (parsedDiff.length > MAX_FILES) {
console.log(`Pull request contains ${parsedDiff.length} files. Processing only the first ${MAX_FILES} files.`);
}
for (const file of filesToProcess) {
if (file.to === "/dev/null") continue; // Ignore deleted files if (file.to === "/dev/null") continue; // Ignore deleted files
for (const chunk of file.chunks) { for (const chunk of file.chunks) {
const prompt = createPrompt(file, chunk, prDetails); const prompt = createPrompt(file, chunk, prDetails);
@ -79,6 +111,8 @@ async function analyzeCode(
} }
function createPrompt(file: File, chunk: Chunk, prDetails: PRDetails): string { function createPrompt(file: File, chunk: Chunk, prDetails: PRDetails): string {
const languageContext = getLanguageContext(file.to || '');
return `Your task is to review pull requests. Instructions: return `Your task is to review pull requests. Instructions:
- Provide the response in following JSON format: {"reviews": [{"lineNumber": <line_number>, "reviewComment": "<review comment>"}]} - Provide the response in following JSON format: {"reviews": [{"lineNumber": <line_number>, "reviewComment": "<review comment>"}]}
- Do not give positive comments or compliments. - Do not give positive comments or compliments.
@ -87,6 +121,8 @@ function createPrompt(file: File, chunk: Chunk, prDetails: PRDetails): string {
- Use the given description only for the overall context and only comment the code. - Use the given description only for the overall context and only comment the code.
- IMPORTANT: NEVER suggest adding comments to the code. - IMPORTANT: NEVER suggest adding comments to the code.
${languageContext}
Review the following code diff in the file "${ Review the following code diff in the file "${
file.to file.to
}" and take the pull request title and description into account when writing the response. }" and take the pull request title and description into account when writing the response.
@ -103,8 +139,7 @@ Git diff to review:
\`\`\`diff \`\`\`diff
${chunk.content} ${chunk.content}
${chunk.changes ${chunk.changes
// @ts-expect-error - ln and ln2 exists where needed .map((c: Change) => `${c.ln ? c.ln : c.ln2} ${c.content}`)
.map((c) => `${c.ln ? c.ln : c.ln2} ${c.content}`)
.join("\n")} .join("\n")}
\`\`\` \`\`\`
`; `;
@ -224,10 +259,10 @@ async function main() {
const excludePatterns = core const excludePatterns = core
.getInput("exclude") .getInput("exclude")
.split(",") .split(",")
.map((s) => s.trim()); .map((s: string) => s.trim());
const filteredDiff = parsedDiff.filter((file) => { const filteredDiff = parsedDiff.filter((file: File) => {
return !excludePatterns.some((pattern) => return !excludePatterns.some((pattern: string) =>
minimatch(file.to ?? "", pattern) minimatch(file.to ?? "", pattern)
); );
}); });

34
src/types/parse-diff.d.ts vendored Normal file
View file

@ -0,0 +1,34 @@
declare module 'parse-diff' {
export interface Change {
type: string;
content: string;
ln?: number;
ln2?: number;
normal?: boolean;
add?: boolean;
del?: boolean;
}
export interface Chunk {
content: string;
changes: Change[];
oldStart: number;
oldLines: number;
newStart: number;
newLines: number;
}
export interface File {
chunks: Chunk[];
deletions: number;
additions: number;
from: string;
to: string;
index: string[];
binary?: boolean;
new?: boolean;
deleted?: boolean;
}
export default function parseDiff(diff: string): File[];
}

View file

@ -163,6 +163,11 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
"@types/minimatch@^5.1.2":
version "5.1.2"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
"@types/node-fetch@^2.6.4": "@types/node-fetch@^2.6.4":
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.9.tgz#15f529d247f1ede1824f7e7acdaa192d5f28071e" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.9.tgz#15f529d247f1ede1824f7e7acdaa192d5f28071e"