-
Notifications
You must be signed in to change notification settings - Fork 348
/
bot.ts
125 lines (102 loc) · 3.36 KB
/
bot.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { Context, Probot } from 'probot';
import { Chat } from './chat.js';
const OPENAI_API_KEY = 'OPENAI_API_KEY';
const MAX_PATCH_COUNT = 4000;
export const robot = (app: Probot) => {
const loadChat = async (context: Context) => {
if (process.env.OPENAI_API_KEY) {
return new Chat(process.env.OPENAI_API_KEY);
}
const repo = context.repo();
try {
const { data } = (await context.octokit.request(
'GET /repos/{owner}/{repo}/actions/variables/{name}',
{
owner: repo.owner,
repo: repo.repo,
name: OPENAI_API_KEY,
}
)) as any;
if (!data?.value) {
return null;
}
return new Chat(data.value);
} catch {
await context.octokit.issues.createComment({
repo: repo.repo,
owner: repo.owner,
issue_number: context.pullRequest().pull_number,
body: `Seems you are using me but didn't get OPENAI_API_KEY seted in Variables/Secrets for this repo. you could follow [readme](https://github.com/anc95/ChatGPT-CodeReview) for more information`,
});
return null;
}
};
app.on(
['pull_request.opened', 'pull_request.synchronize'],
async (context) => {
const repo = context.repo();
const chat = await loadChat(context);
if (!chat) {
return 'no chat';
}
const pull_request = context.payload.pull_request;
if (
pull_request.state === 'closed' ||
pull_request.locked ||
pull_request.draft
) {
return 'invalid event paylod';
}
const data = await context.octokit.repos.compareCommits({
owner: repo.owner,
repo: repo.repo,
base: context.payload.pull_request.base.sha,
head: context.payload.pull_request.head.sha,
});
let { files: changedFiles, commits } = data.data;
if (context.payload.action === 'synchronize' && commits.length >= 2) {
const {
data: { files },
} = await context.octokit.repos.compareCommits({
owner: repo.owner,
repo: repo.repo,
base: commits[commits.length - 2].sha,
head: commits[commits.length - 1].sha,
});
const filesNames = files?.map((file) => file.filename) || [];
changedFiles = changedFiles?.filter((file) =>
filesNames.includes(file.filename)
);
}
if (!changedFiles?.length) {
return 'no change';
}
console.time('gpt cost');
for (let i = 0; i < changedFiles.length; i++) {
const file = changedFiles[i];
const patch = file.patch || '';
if(file.status !== 'modified' && file.status !== 'added') {
continue;
}
if (!patch || patch.length > MAX_PATCH_COUNT) {
continue;
}
const res = await chat?.codeReview(patch);
if (!!res) {
await context.octokit.pulls.createReviewComment({
repo: repo.repo,
owner: repo.owner,
pull_number: context.pullRequest().pull_number,
commit_id: commits[commits.length - 1].sha,
path: file.filename,
body: res,
position: patch.split('\n').length - 1,
});
}
}
console.timeEnd('gpt cost');
console.info('successfully reviewed', context.payload.pull_request.html_url);
return 'success';
}
);
};