Skip to content

Commit 9671d79

Browse files
committed
Add AI code review workflow
1 parent ac565a8 commit 9671d79

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
name: AI Code Review
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
workflow_dispatch:
7+
8+
permissions:
9+
pull-requests: write
10+
contents: read
11+
12+
jobs:
13+
code-review:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout PR code
17+
uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 0
20+
21+
- name: Get PR diff
22+
id: diff
23+
run: |
24+
git diff origin/${{ github.base_ref }}...HEAD > pr.diff
25+
MAX_SIZE=50000
26+
if [ $(wc -c < pr.diff) -gt $MAX_SIZE ]; then
27+
head -c $MAX_SIZE pr.diff > pr_truncated.diff
28+
mv pr_truncated.diff pr.diff
29+
echo "warning=Diff truncated to ${MAX_SIZE} bytes" >> $GITHUB_OUTPUT
30+
fi
31+
FILES_CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | wc -l)
32+
echo "files_changed=$FILES_CHANGED" >> $GITHUB_OUTPUT
33+
34+
- name: Code Review
35+
id: review
36+
env:
37+
API_KEY: ${{ secrets.AI_PROVIDER_API_KEY }}
38+
API_ENDPOINT: ${{ secrets.AI_PROVIDER_API_ENDPOINT }}
39+
MODEL: ${{ secrets.AI_PROVIDER_MODEL }}
40+
run: |
41+
set +x # Disable verbose mode to prevent secret leakage in logs
42+
DIFF=$(cat pr.diff | jq -Rs .)
43+
44+
# Retry logic with exponential backoff
45+
MAX_RETRIES=3
46+
RETRY_COUNT=0
47+
HTTP_CODE=0
48+
49+
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
50+
HTTP_CODE=$(curl -s -o response.json -w "%{http_code}" $API_ENDPOINT \
51+
-H "Authorization: Bearer $API_KEY" \
52+
-H "Content-Type: application/json" \
53+
-H "Accept-Language: en-US,en" \
54+
-d "{
55+
\"model\": \"$MODEL\",
56+
\"messages\": [
57+
{
58+
\"role\": \"system\",
59+
\"content\": \"You are an expert code reviewer. Analyze the PR diff and provide:\\n\\n1. **Summary**: Brief overview\\n2. **Issues**: Bugs or problems (if any)\\n3. **Security**: Security concerns (if any)\\n4. **Performance**: Performance considerations (if any)\\n5. **Best Practices**: Swift 6, concurrency, architecture (if any)\\n6. **Suggestions**: Optional improvements\\n\\nBe concise but thorough. Use markdown formatting.\"
60+
},
61+
{
62+
\"role\": \"user\",
63+
\"content\": ${DIFF}
64+
}
65+
],
66+
\"temperature\": 0.3,
67+
\"max_tokens\": 4096
68+
}")
69+
70+
if [ "$HTTP_CODE" -eq 200 ]; then
71+
break
72+
fi
73+
74+
RETRY_COUNT=$((RETRY_COUNT + 1))
75+
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
76+
WAIT_TIME=$((2 ** RETRY_COUNT))
77+
echo "API returned $HTTP_CODE. Retrying in ${WAIT_TIME}s... (Attempt $RETRY_COUNT/$MAX_RETRIES)"
78+
sleep $WAIT_TIME
79+
fi
80+
done
81+
82+
if [ "$HTTP_CODE" -ne 200 ]; then
83+
echo "Error: API returned $HTTP_CODE after $MAX_RETRIES attempts"
84+
cat response.json
85+
exit 1
86+
fi
87+
88+
RESPONSE=$(cat response.json)
89+
REVIEW=$(echo "$RESPONSE" | jq -r '.choices[0].message.content')
90+
91+
# Check if review was extracted successfully
92+
if [ -z "$REVIEW" ] || [ "$REVIEW" == "null" ]; then
93+
echo "Error: Failed to extract review content from API response"
94+
echo "Response: $RESPONSE"
95+
exit 1
96+
fi
97+
98+
echo "$REVIEW" > review.md
99+
100+
cat > review_with_header.md << EOF
101+
### 🧠 AI Code Review
102+
103+
$REVIEW
104+
EOF
105+
106+
if [ -n "${{ steps.diff.outputs.warning }}" ]; then
107+
echo "" >> review_with_header.md
108+
echo "⚠️ **Note**: ${{ steps.diff.outputs.warning }}" >> review_with_header.md
109+
fi
110+
111+
- name: Post review as PR comment
112+
uses: actions/github-script@v7
113+
with:
114+
script: |
115+
const fs = require('fs');
116+
const review = fs.readFileSync('review_with_header.md', 'utf8');
117+
118+
const { data: comments } = await github.rest.issues.listComments({
119+
owner: context.repo.owner,
120+
repo: context.repo.repo,
121+
issue_number: context.issue.number,
122+
});
123+
124+
const botComment = comments.find(comment =>
125+
comment.user.type === 'Bot' &&
126+
comment.body.includes('AI Code Review')
127+
);
128+
129+
if (botComment) {
130+
await github.rest.issues.updateComment({
131+
owner: context.repo.owner,
132+
repo: context.repo.repo,
133+
comment_id: botComment.id,
134+
body: review
135+
});
136+
} else {
137+
await github.rest.issues.createComment({
138+
owner: context.repo.owner,
139+
repo: context.repo.repo,
140+
issue_number: context.issue.number,
141+
body: review
142+
});
143+
}

0 commit comments

Comments
 (0)