-
Notifications
You must be signed in to change notification settings - Fork 1
180 lines (155 loc) · 7.26 KB
/
ai-code-reviewer.yml
File metadata and controls
180 lines (155 loc) · 7.26 KB
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
name: AI Code Reviewer
on:
pull_request:
types: [labeled]
jobs:
comprehensive-review:
name: AI Code Review
runs-on: ubuntu-latest
if: github.event.action == 'labeled' && github.event.label.name == 'ai_code_review'
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y jq
- name: Get PR diff
run: |
# Fetch the base branch (works for any target branch, not just main)
git fetch origin ${{ github.event.pull_request.base.ref }}
git diff origin/${{ github.event.pull_request.base.ref }}...HEAD --no-color > diff.txt
- name: AI Code Review
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
AI_MODEL: ${{ vars.AI_MODEL || 'moonshotai/kimi-k2-thinking' }}
AI_TEMPERATURE: ${{ vars.AI_TEMPERATURE || '0.1' }}
AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '64000' }}
MAX_DIFF_SIZE: ${{ vars.MAX_DIFF_SIZE || '800000' }}
EXCLUDE_FILE_PATTERNS: ${{ vars.EXCLUDE_FILE_PATTERNS || '*.lock,*.min.js,*.min.css,package-lock.json,yarn.lock' }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_FULL_NAME: ${{ github.repository }}
FAIL_ON_REQUESTED_CHANGES: ${{ vars.FAIL_ON_REQUESTED_CHANGES || 'false' }}
DEBUG_MODE: ${{ vars.DEBUG_MODE || 'false' }}
run: |
# Run the AI reviewer and save response to file
echo "Running AI code review..."
# Temporarily disable errexit to capture errors properly
set +e
AI_RESPONSE=$(cat diff.txt | bash ai-reviewer.sh 2>&1)
EXIT_CODE=$?
set -e
# Output raw response for debugging (only in debug mode)
if [ "$DEBUG_MODE" = "true" ]; then
echo "=== RAW AI RESPONSE FOR DEBUG ==="
echo "Exit code: $EXIT_CODE"
echo "$AI_RESPONSE"
echo "=== END RAW AI RESPONSE ==="
fi
# Check if the script failed
if [ $EXIT_CODE -ne 0 ]; then
echo "❌ AI reviewer script failed with exit code $EXIT_CODE"
echo ""
echo "Error output:"
echo "$AI_RESPONSE"
exit 1
fi
# Check if AI_RESPONSE is empty
if [ -z "$AI_RESPONSE" ]; then
echo "❌ AI response is empty"
exit 1
fi
# Extract only the JSON part (find the last valid JSON object in output)
# This handles debug output being mixed with the JSON response
# Try to find JSON by looking for lines starting with { and ending with }
AI_JSON=$(echo "$AI_RESPONSE" | awk '/^{$/,/^}$/{print}' | tail -n +1)
# If that didn't work, try extracting just the last line that looks like JSON
if [ -z "$AI_JSON" ] || ! echo "$AI_JSON" | jq . >/dev/null 2>&1; then
# Try to extract the last complete JSON object using perl
AI_JSON=$(echo "$AI_RESPONSE" | perl -0777 -ne 'print $1 if /(\{(?:[^{}]|(?R))*\})[^\{]*$/s' | tail -c 1000000)
fi
if [ -z "$AI_JSON" ]; then
echo "⚠️ Could not extract JSON from AI response."
if [ "$DEBUG_MODE" = "true" ]; then
echo "=== DEBUG: Full response (first 3000 chars) ==="
echo "$AI_RESPONSE" | head -c 3000
echo "=== END DEBUG ==="
fi
exit 1
fi
# Parse JSON response
if ! echo "$AI_JSON" | jq . >/dev/null 2>&1; then
echo "⚠️ AI response is not valid JSON. Cannot process review."
# Log raw response for debugging (redact sensitive info)
echo "=== DEBUG: Raw AI Response (first 2000 chars) ==="
echo "$AI_RESPONSE" | head -c 2000
echo ""
echo "=== END DEBUG ==="
# Try to extract JSON from thinking model response
if echo "$AI_RESPONSE" | grep -q '"content"'; then
echo "=== DEBUG: Attempting to extract content from response ==="
EXTRACTED_CONTENT=$(echo "$AI_RESPONSE" | jq -r '.choices[0].message.content // empty' 2>/dev/null || echo "")
if [ -n "$EXTRACTED_CONTENT" ]; then
echo "Extracted content (first 1000 chars):"
echo "$EXTRACTED_CONTENT" | head -c 1000
echo ""
fi
fi
# Don't post raw response as it may contain sensitive data
echo "AI response could not be processed. Please check the workflow logs for debugging information." | gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} -F -
exit 1
fi
# Extract fields from JSON
REVIEW=$(echo "$AI_JSON" | jq -r '.review // "No review provided"')
DECISION=$(echo "$AI_JSON" | jq -r '.fail_pass_workflow // "uncertain"' | tr -d '\n\r' | xargs)
LABELS=$(echo "$AI_JSON" | jq -r '.labels_added[]? // empty')
# Post the review as PR comment
echo "$REVIEW" | gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} -F -
# Add labels to PR
echo "Labels to add: $LABELS"
for label in $LABELS; do
echo "Adding label: $label"
if gh label create "$label" --color "0366d6" --description "Auto-created by AI reviewer" 2>/dev/null; then
echo "Created new label: $label"
else
echo "Label already exists: $label"
fi
if gh issue edit ${{ github.event.pull_request.number }} --add-label "$label" 2>/dev/null; then
echo "Successfully added label: $label"
else
echo "Failed to add label: $label"
fi
done
# Handle workflow decision (after posting review and labels)
echo "AI decision: $DECISION"
# Store the decision for later use
echo "DECISION=$DECISION" >> "$GITHUB_ENV"
# Remove the ai_code_review label to make re-triggering easier
echo "Removing ai_code_review label to allow easy re-triggering"
if gh issue edit ${{ github.event.pull_request.number }} --remove-label "ai_code_review" 2>/dev/null; then
echo "Successfully removed ai_code_review label"
else
echo "Failed to remove ai_code_review label (may have been already removed)"
fi
echo "✅ AI review completed successfully"
- name: Fail Workflow if Requested
if: env.FAIL_ON_REQUESTED_CHANGES == 'true' && env.DECISION == 'fail'
run: |
echo "❌ AI requested changes and FAIL_ON_REQUESTED_CHANGES is enabled. Failing workflow."
echo "The review has been posted above. Please address the requested changes."
exit 1
- name: Cleanup
if: always()
run: |
rm -f diff.txt
# Only remove ai_response.txt if it exists (only created in debug mode)
if [ -f "ai_response.txt" ]; then
rm -f ai_response.txt
fi