feat: add AI code reviewer using Gemini API

swadhinbiswas committed 3 months ago
8afe9a8
1 parent 59efa59
commit 8afe9a8
2 files changed
+153 -0
▼.github/workflows/ai-review.yml
+34-0
1
+name: AI Code Reviewer
2
+
3
+on:
4
+ pull_request:
5
+ types: [opened, synchronize]
6
+
7
+permissions:
8
+ pull-requests: write
9
+ contents: read
10
+
11
+jobs:
12
+ review:
13
+ name: Gemini AI Review
14
+ runs-on: ubuntu-latest
15
+ timeout-minutes: 5
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - uses: oven-sh/setup-bun@v2
22
+ with:
23
+ bun-version: latest
24
+
25
+ - name: Run AI Review
26
+ env:
27
+ # Use user's PAT to count towards their GitHub contribution graph, fallback to standard bot token
28
+ GITHUB_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
29
+ GEMINI_API_KEY: ${{ secrets.GEMINIKEY }}
30
+ PR_NUMBER: ${{ github.event.pull_request.number }}
31
+ REPO: ${{ github.repository }}
32
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
33
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
34
+ run: bun run scripts/ai-review.ts
0
35
▼scripts/ai-review.ts
+119-0
1
+import { execSync } from 'child_process';
2
+
3
+const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
4
+const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
5
+const PR_NUMBER = process.env.PR_NUMBER;
6
+const REPO = process.env.REPO;
7
+const BASE_SHA = process.env.BASE_SHA;
8
+const HEAD_SHA = process.env.HEAD_SHA;
9
+
10
+if (!GEMINI_API_KEY) {
11
+ console.log("GEMINI_API_KEY is not set. Skipping AI review.");
12
+ process.exit(0);
13
+}
14
+
15
+if (!GITHUB_TOKEN) {
16
+ console.log("GITHUB_TOKEN is not set. Skipping AI review.");
17
+ process.exit(0);
18
+}
19
+
20
+async function run() {
21
+ try {
22
+ // 1. Get the diff
23
+ console.log(`Getting diff between ${BASE_SHA} and ${HEAD_SHA}`);
24
+ const diff = execSync(`git diff ${BASE_SHA} ${HEAD_SHA}`).toString();
25
+
26
+ if (!diff.trim()) {
27
+ console.log("No diff found. Skipping review.");
28
+ return;
29
+ }
30
+
31
+ if (diff.length > 50000) {
32
+ console.log("Diff is too large for AI review (max 50,000 characters). Skipping.");
33
+ return;
34
+ }
35
+
36
+ // 2. Call Gemini API
37
+ console.log("Requesting review from Gemini 1.5 Flash...");
38
+ const prompt = `You are a senior software engineer conducting a code review on a pull request.
39
+Review the following git diff and provide constructive, human-like, and professional feedback.
40
+Focus on identifying logic errors, security issues, performance bottlenecks, and best practice violations.
41
+If the code looks perfect, say so in a friendly way.
42
+
43
+Important constraints:
44
+- Do NOT output any internal reasoning, thoughts, or <think> tags.
45
+- Output ONLY the final review in markdown format.
46
+- Be concise but thorough.
47
+
48
+Diff:
49
+\`\`\`diff
50
+${diff}
51
+\`\`\`
52
+`;
53
+
54
+ // We use the same API endpoint format from your curl command
55
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${GEMINI_API_KEY}`, {
56
+ method: 'POST',
57
+ headers: {
58
+ 'Content-Type': 'application/json'
59
+ },
60
+ body: JSON.stringify({
61
+ contents: [
62
+ {
63
+ parts: [
64
+ {
65
+ text: prompt
66
+ }
67
+ ]
68
+ }
69
+ ]
70
+ })
71
+ });
72
+
73
+ if (!response.ok) {
74
+ const errorText = await response.text();
75
+ console.error(`Gemini API Error: ${response.status} - ${errorText}`);
76
+ process.exit(1);
77
+ }
78
+
79
+ const data = await response.json();
80
+ let reviewComment = data.candidates?.[0]?.content?.parts?.[0]?.text;
81
+
82
+ if (!reviewComment) {
83
+ console.error("No content received from Gemini.");
84
+ process.exit(1);
85
+ }
86
+
87
+ // Add a signature
88
+ reviewComment += "\n\n— *AI Code Review (Powered by Gemini)*";
89
+
90
+ // 3. Post to GitHub
91
+ console.log("Posting review to GitHub PR...");
92
+ const ghResponse = await fetch(`https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}/reviews`, {
93
+ method: 'POST',
94
+ headers: {
95
+ 'Authorization': `Bearer ${GITHUB_TOKEN}`,
96
+ 'Accept': 'application/vnd.github.v3+json',
97
+ 'X-GitHub-Api-Version': '2022-11-28'
98
+ },
99
+ body: JSON.stringify({
100
+ body: reviewComment,
101
+ event: 'COMMENT'
102
+ })
103
+ });
104
+
105
+ if (!ghResponse.ok) {
106
+ const ghError = await ghResponse.text();
107
+ console.error(`GitHub API Error: ${ghResponse.status} - ${ghError}`);
108
+ process.exit(1);
109
+ }
110
+
111
+ console.log("Review successfully posted!");
112
+
113
+ } catch (error) {
114
+ console.error("Error during AI review:", error);
115
+ process.exit(1);
116
+ }
117
+}
118
+
119
+run();
0
120