Skip to content

Commit d30b995

Browse files
Updated to allow Libs folder, updated docs and ISSUE_TEMPLATES (#5)
* chore(docs): updated the libs to be required, added more docs * chore(action): updated action to ignore lib folder of fixed code
1 parent dfbdf77 commit d30b995

File tree

10 files changed

+657
-3
lines changed

10 files changed

+657
-3
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for Pull Request Magic
4+
title: ''
5+
labels: enhancement
6+
assignees: ''
7+
8+
---
9+
10+
**Is your feature request related to a problem? Please describe.**
11+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12+
13+
**Describe the solution you'd like**
14+
A clear and concise description of what you want to happen.
15+
16+
**Describe alternatives you've considered**
17+
A clear and concise description of any alternative solutions or features you've considered.
18+
19+
**Additional context**
20+
Add any other context or screenshots about the feature request here.

.github/workflows/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ jobs:
2424
openai_api_key: ${{ secrets.OPEN_AI_KEY }}
2525
openai_model: "gpt-4o"
2626
review_code: true
27-
excluded_files: "node_modules, package.json, package-lock.json"
27+
excluded_files: "node_modules, package.json, package-lock.json, lib"

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
node_modules
2-
lib
1+
node_modules

.node-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
20.6.0

CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Repository CODEOWNERS
2+
3+
* @softrams/open-source

lib/index.js

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
"use strict";
2+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3+
if (k2 === undefined) k2 = k;
4+
var desc = Object.getOwnPropertyDescriptor(m, k);
5+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6+
desc = { enumerable: true, get: function() { return m[k]; } };
7+
}
8+
Object.defineProperty(o, k2, desc);
9+
}) : (function(o, m, k, k2) {
10+
if (k2 === undefined) k2 = k;
11+
o[k2] = m[k];
12+
}));
13+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14+
Object.defineProperty(o, "default", { enumerable: true, value: v });
15+
}) : function(o, v) {
16+
o["default"] = v;
17+
});
18+
var __importStar = (this && this.__importStar) || function (mod) {
19+
if (mod && mod.__esModule) return mod;
20+
var result = {};
21+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22+
__setModuleDefault(result, mod);
23+
return result;
24+
};
25+
var __importDefault = (this && this.__importDefault) || function (mod) {
26+
return (mod && mod.__esModule) ? mod : { "default": mod };
27+
};
28+
Object.defineProperty(exports, "__esModule", { value: true });
29+
const fs_1 = require("fs");
30+
const parse_diff_1 = __importDefault(require("parse-diff"));
31+
const core = __importStar(require("@actions/core"));
32+
const github_1 = require("./services/github");
33+
const minimatch_1 = require("minimatch");
34+
const ai_1 = require("./services/ai");
35+
const excludedFiles = core.getInput("excluded_files").split(",").map((s) => s.trim());
36+
const createPullRequestSummary = core.getInput("generate_summary");
37+
const reviewCode = core.getInput("review_code");
38+
const overallReview = core.getInput("overall_code_review");
39+
async function validatePullRequest(diff, details) {
40+
const foundSummary = [];
41+
for (const file of diff) {
42+
for (const chunk of file.chunks) {
43+
const message = await (0, ai_1.prSummaryCreation)(file, chunk, details);
44+
if (message) {
45+
const mappedResults = message.flatMap((result) => {
46+
if (!result.changes) {
47+
return [];
48+
}
49+
if (!result.typeChanges) {
50+
return [];
51+
}
52+
if (!result.checklist) {
53+
return [];
54+
}
55+
return {
56+
changes: result.changes,
57+
typeChanges: result.typeChanges,
58+
checklist: result.checklist,
59+
};
60+
});
61+
foundSummary.push(...mappedResults);
62+
}
63+
}
64+
}
65+
if (foundSummary && foundSummary.length > 0) {
66+
const compiledSummary = await (0, ai_1.summaryAllMessages)(foundSummary);
67+
return compiledSummary;
68+
}
69+
return '';
70+
}
71+
async function validateCode(diff, details) {
72+
const neededComments = [];
73+
for (const file of diff) {
74+
for (const chunk of file.chunks) {
75+
const results = await (0, ai_1.validateCodeViaAI)(file, chunk, details);
76+
if (results) {
77+
const mappedResults = results.flatMap((result) => {
78+
if (!file.to) {
79+
return [];
80+
}
81+
if (!result.lineNumber) {
82+
return [];
83+
}
84+
if (!result.review) {
85+
return [];
86+
}
87+
return {
88+
body: result.review,
89+
path: file.to,
90+
line: parseInt(result.lineNumber, 10)
91+
};
92+
});
93+
if (mappedResults) {
94+
neededComments.push(...mappedResults);
95+
}
96+
}
97+
}
98+
}
99+
return neededComments;
100+
}
101+
async function validateOverallCodeReview(diff, details) {
102+
const detailedFeedback = [];
103+
for (const file of diff) {
104+
for (const chunk of file.chunks) {
105+
const results = await (0, ai_1.obtainFeedback)(file, chunk, details);
106+
if (results) {
107+
const mappedResults = results.flatMap((result) => {
108+
if (!file.to) {
109+
return [];
110+
}
111+
return {
112+
changesOverview: result.changesOverview,
113+
feedback: result.feedback,
114+
improvements: result.improvements,
115+
conclusion: result.conclusion,
116+
};
117+
});
118+
if (mappedResults) {
119+
detailedFeedback.push(...mappedResults);
120+
}
121+
}
122+
}
123+
}
124+
return detailedFeedback;
125+
}
126+
async function main() {
127+
let dif = null;
128+
const { action, repository, number, before, after } = JSON.parse((0, fs_1.readFileSync)(process.env.GITHUB_EVENT_PATH || "", "utf-8"));
129+
const { title, description } = await (0, github_1.PRDetails)(repository, number);
130+
if (action === "opened" || action === "reopened") {
131+
const data = await (0, github_1.getPullRequestDiff)(repository.owner.login, repository.name, number);
132+
dif = data;
133+
}
134+
else if (action === "synchronize") {
135+
const newBaseSha = before;
136+
const newHeadSha = after;
137+
const data = await (0, github_1.compareCommits)({
138+
owner: repository.owner.login,
139+
repo: repository.name,
140+
before: newBaseSha,
141+
after: newHeadSha,
142+
number
143+
});
144+
dif = String(data);
145+
}
146+
else {
147+
console.log('Unknown action', process.env.GITHUB_EVENT_NAME);
148+
return;
149+
}
150+
if (!dif) {
151+
console.log('No diff found, exiting');
152+
return;
153+
}
154+
const diff = (0, parse_diff_1.default)(dif);
155+
const filteredDiff = diff.filter((file) => {
156+
return !excludedFiles.some((pattern) => { var _a; return (0, minimatch_1.minimatch)((_a = file.to) !== null && _a !== void 0 ? _a : "", pattern); });
157+
});
158+
if (action === "opened" || action === "reopened") {
159+
if (createPullRequestSummary) {
160+
console.log('Generating summary for new PR');
161+
const summary = await validatePullRequest(diff, {
162+
title,
163+
description
164+
});
165+
if (summary) {
166+
await (0, github_1.updateBody)(repository.owner.login, repository.name, number, summary);
167+
}
168+
}
169+
if (overallReview) {
170+
const detailedFeedback = await validateOverallCodeReview(filteredDiff, {
171+
title,
172+
description
173+
});
174+
if (detailedFeedback && detailedFeedback.length > 0) {
175+
const resultsFullFeedback = await (0, ai_1.summaryOfAllFeedback)(detailedFeedback);
176+
if (resultsFullFeedback) {
177+
await (0, github_1.commentOnPullRequest)({
178+
owner: repository.owner.login,
179+
repo: repository.name,
180+
number
181+
}, resultsFullFeedback);
182+
}
183+
}
184+
}
185+
}
186+
if (reviewCode) {
187+
const neededComments = await validateCode(filteredDiff, {
188+
title,
189+
description
190+
});
191+
if (neededComments) {
192+
await (0, github_1.createReviewComment)(repository.owner.login, repository.name, number, neededComments);
193+
}
194+
}
195+
}
196+
main().catch((error) => {
197+
console.error(error);
198+
process.exit(1);
199+
});

lib/lib/ai.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"use strict";

lib/lib/github.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
exports.gitDiff = exports.PRDetails = void 0;
7+
const rest_1 = require("@octokit/rest");
8+
const core_1 = __importDefault(require("@actions/core"));
9+
const GITHUB_TOKEN = core_1.default.getInput("GITHUB_TOKEN");
10+
const octokit = new rest_1.Octokit({
11+
auth: GITHUB_TOKEN,
12+
});
13+
async function PRDetails(repository, number) {
14+
// Obtain the PR details
15+
const { data } = await octokit.pulls.get({
16+
owner: repository.owner.login,
17+
repo: repository.name,
18+
pull_number: number,
19+
});
20+
return {
21+
title: data.title || "",
22+
description: data.body || "",
23+
};
24+
}
25+
exports.PRDetails = PRDetails;
26+
async function gitDiff(owner, repo, pull_number) {
27+
// Obtain the PR details
28+
const { data } = await octokit.pulls.get({
29+
owner,
30+
repo,
31+
pull_number,
32+
mediaType: { format: "diff" },
33+
});
34+
return data;
35+
}
36+
exports.gitDiff = gitDiff;

0 commit comments

Comments
 (0)