Skip to content

Commit e9d9b7d

Browse files
authored
Create droid-code-review.yml
1 parent 059b91c commit e9d9b7d

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
name: Droid Code Review
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, ready_for_review]
6+
7+
concurrency:
8+
group: droid-review-${{ github.event.pull_request.number }}
9+
cancel-in-progress: true
10+
11+
permissions:
12+
pull-requests: write
13+
contents: read
14+
issues: write
15+
16+
jobs:
17+
code-review:
18+
runs-on: ubuntu-latest
19+
timeout-minutes: 15
20+
# Skip automated code review for draft PRs
21+
if: github.event.pull_request.draft == false
22+
23+
steps:
24+
- name: Checkout repository
25+
uses: actions/checkout@v4
26+
with:
27+
fetch-depth: 0
28+
ref: ${{ github.event.pull_request.head.sha }}
29+
30+
- name: Install Droid CLI
31+
run: |
32+
curl -fsSL https://app.factory.ai/cli | sh
33+
echo "$HOME/.local/bin" >> $GITHUB_PATH
34+
"$HOME/.local/bin/droid" --version
35+
36+
- name: Configure git identity
37+
run: |
38+
git config user.name "Droid Agent"
39+
git config user.email "droidagent@factory.ai"
40+
41+
- name: Prepare review context
42+
run: |
43+
# Get the PR diff
44+
git fetch origin ${{ github.event.pull_request.base.ref }}
45+
git diff origin/${{ github.event.pull_request.base.ref }}...${{ github.event.pull_request.head.sha }} > diff.txt
46+
47+
# Get existing comments using GitHub API
48+
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
49+
-H "Accept: application/vnd.github.v3+json" \
50+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \
51+
> existing_comments.json
52+
53+
# Get changed files with patches for positioning
54+
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
55+
-H "Accept: application/vnd.github.v3+json" \
56+
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \
57+
| jq '[.[] | {filename: .filename, patch: .patch}]' > files.json
58+
59+
- name: Perform automated code review
60+
env:
61+
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
62+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63+
run: |
64+
cat > prompt.txt << 'EOF'
65+
You are an automated code review system. Review the PR diff and identify clear issues that need to be fixed.
66+
67+
Input files (already in current directory):
68+
- diff.txt: the code changes to review
69+
- files.json: file patches with line numbers for positioning comments
70+
- existing_comments.json: skip issues already mentioned here
71+
72+
Task: Create a file called comments.json with this exact format:
73+
[{ "path": "path/to/file.js", "position": 42, "body": "Your comment here" }]
74+
75+
Focus on these types of issues:
76+
- Dead/unreachable code (if (false), while (false), code after return/throw/break)
77+
- Broken control flow (missing break in switch, fallthrough bugs)
78+
- Async/await mistakes (missing await, .then without return, unhandled promise rejections)
79+
- Array/object mutations in React components or reducers
80+
- UseEffect dependency array problems (missing deps, incorrect deps)
81+
- Incorrect operator usage (== vs ===, && vs ||, = in conditions)
82+
- Off-by-one errors in loops or array indexing
83+
- Integer overflow/underflow in calculations
84+
- Regex catastrophic backtracking vulnerabilities
85+
- Missing base cases in recursive functions
86+
- Incorrect type coercion that changes behavior
87+
- Environment variable access without defaults or validation
88+
- Null/undefined dereferences
89+
- Resource leaks (unclosed files or connections)
90+
- SQL/XSS injection vulnerabilities
91+
- Concurrency/race conditions
92+
- Missing error handling for critical operations
93+
94+
Comment format:
95+
- Clearly describe the issue: "This code block is unreachable due to the if (false) condition"
96+
- Provide a concrete fix: "Remove this entire if block as it will never execute"
97+
- When possible, suggest the exact code change:
98+
```suggestion
99+
// Remove the unreachable code
100+
```
101+
- Be specific about why it's a problem: "This will cause a TypeError if input is null"
102+
- No emojis, just clear technical language
103+
104+
Skip commenting on:
105+
- Code style, formatting, or naming conventions
106+
- Minor performance optimizations
107+
- Architectural decisions or design patterns
108+
- Features or functionality (unless broken)
109+
- Test coverage (unless tests are clearly broken)
110+
111+
Position calculation:
112+
- Use the "position" field from files.json patches
113+
- This is the line number in the diff, not the file
114+
- Comments must align with exact changed lines only
115+
116+
Output:
117+
- Empty array [] if no issues found
118+
- Otherwise array of comment objects with path, position, body
119+
- Each comment should be actionable and clear about what needs to be fixed
120+
- Maximum 10 comments total; prioritize the most critical issues
121+
EOF
122+
123+
# Run droid exec with the prompt
124+
echo "Running code review analysis..."
125+
droid exec --auto high -f prompt.txt
126+
127+
# Check if comments.json was created
128+
if [ ! -f comments.json ]; then
129+
echo "❌ ERROR: droid exec did not create comments.json"
130+
echo "This usually indicates the review run failed (e.g. missing FACTORY_API_KEY or runtime error)."
131+
exit 1
132+
fi
133+
134+
echo "=== Review Results ==="
135+
cat comments.json
136+
137+
- name: Submit inline review comments
138+
uses: actions/github-script@v7
139+
with:
140+
script: |
141+
const fs = require('fs');
142+
const prNumber = context.payload.pull_request.number;
143+
144+
if (!fs.existsSync('comments.json')) {
145+
core.info('comments.json missing; skipping review submission');
146+
return;
147+
}
148+
149+
const comments = JSON.parse(fs.readFileSync('comments.json', 'utf8'));
150+
151+
if (!Array.isArray(comments) || comments.length === 0) {
152+
// Check if we already have a "no issues" comment
153+
const existing = await github.paginate(github.rest.issues.listComments, {
154+
owner: context.repo.owner,
155+
repo: context.repo.repo,
156+
issue_number: prNumber,
157+
per_page: 100
158+
});
159+
160+
const hasNoIssuesComment = existing.some(c =>
161+
c.user.login.includes('[bot]') &&
162+
/no issues found|lgtm|✅/i.test(c.body || '')
163+
);
164+
165+
if (!hasNoIssuesComment) {
166+
await github.rest.pulls.createReview({
167+
owner: context.repo.owner,
168+
repo: context.repo.repo,
169+
pull_number: prNumber,
170+
event: 'COMMENT',
171+
body: '✅ No issues found in the current changes.'
172+
});
173+
}
174+
return;
175+
}
176+
177+
// Submit review with inline comments
178+
const summary = `Found ${comments.length} potential issue${comments.length === 1 ? '' : 's'} that should be addressed.`;
179+
180+
await github.rest.pulls.createReview({
181+
owner: context.repo.owner,
182+
repo: context.repo.repo,
183+
pull_number: prNumber,
184+
event: 'COMMENT',
185+
body: summary,
186+
comments: comments
187+
});
188+
189+
core.info(`Submitted review with ${comments.length} inline comments`);
190+
191+
- name: Upload debug artifacts on failure
192+
if: ${{ failure() }}
193+
uses: actions/upload-artifact@v4
194+
with:
195+
name: droid-review-debug-${{ github.run_id }}
196+
path: |
197+
diff.txt
198+
files.json
199+
existing_comments.json
200+
prompt.txt
201+
comments.json
202+
${{ runner.home }}/.factory/logs/droid-log-single.log
203+
${{ runner.home }}/.factory/logs/console.log
204+
if-no-files-found: ignore
205+
retention-days: 7

0 commit comments

Comments
 (0)