Skip to content

Commit ed4900b

Browse files
feat: Review Droid
0 parents  commit ed4900b

File tree

3 files changed

+348
-0
lines changed

3 files changed

+348
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Factory
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Factory AI Droid Review
2+
3+
An AI-powered code review GitHub Action using Factory AI's Droid to analyze code changes and identify critical issues. This action automatically reviews pull requests, focusing on bugs, security vulnerabilities, and critical code problems.
4+
5+
## Features
6+
7+
- **AI-Powered Analysis**: Uses Factory AI's Droid CLI to analyze code changes and identify critical issues
8+
- **PR Context Fetching**: Automatically fetches complete PR context including diff, metadata, and existing comments
9+
- **Inline Code Comments**: Posts targeted review comments directly on specific lines with optional fix suggestions
10+
- **Duplicate Prevention**: Checks existing comments to avoid redundant feedback
11+
- **Focused Review Scope**: Prioritizes critical bugs and security issues over style concerns
12+
- **Debug Artifacts**: Uploads diagnostic logs on failure for troubleshooting
13+
14+
## Quick Start
15+
16+
Add this to your repository's `.github/workflows/review-droid.yml`:
17+
18+
```yaml
19+
name: Droid Review
20+
21+
permissions:
22+
pull-requests: write # Needed for leaving PR comments
23+
contents: read
24+
issues: write
25+
26+
on:
27+
pull_request:
28+
types: [opened, synchronize, reopened, ready_for_review]
29+
30+
# Cancel previous runs for the same PR
31+
concurrency:
32+
group: review-droid-${{ github.event.pull_request.number }}
33+
cancel-in-progress: true
34+
35+
jobs:
36+
code-review:
37+
runs-on: ubuntu-latest
38+
timeout-minutes: 15
39+
# Skip draft PRs
40+
if: github.event.pull_request.draft == false
41+
42+
steps:
43+
- uses: Factory-AI/droid-review@main
44+
with:
45+
factory-api-key: ${{ secrets.FACTORY_API_KEY }}
46+
pr-number: ${{ github.event.pull_request.number }}
47+
pr-head-sha: ${{ github.event.pull_request.head.sha }}
48+
```
49+
50+
## Configuration Options
51+
52+
### Action Inputs
53+
54+
| Input | Description | Default | Required |
55+
|-------|-------------|---------|----------|
56+
| `factory-api-key` | The API key to run Droid Exec which powers the Review Droid. | None | Yes |
57+
| `pr-number` | The Pull Request number being reviewed | None | Yes |
58+
| `pr-head-sha` | The commit SHA of the PR head branch for correct checkout | None | Yes |
59+
60+
## Code Review Capabilities
61+
62+
### Issues Actively Detected
63+
64+
- **Dead/Unreachable Code**: Code after return/throw/break, if(false) blocks
65+
- **Control Flow Bugs**: Missing break statements, switch fallthrough issues
66+
- **Async/Await Errors**: Missing await, unhandled promise rejections, incorrect promise handling
67+
- **React-Specific Issues**: State mutations, useEffect dependency problems
68+
- **Operator Mistakes**: Wrong equality operators (== vs ===), assignment in conditions
69+
- **Array/Loop Errors**: Off-by-one errors, incorrect indexing
70+
- **Type Coercion Issues**: Problematic type conversions affecting behavior
71+
- **Null/Undefined Errors**: Potential null dereferences, missing checks
72+
- **Resource Management**: Unclosed files/connections, memory leaks
73+
- **Security Vulnerabilities**: SQL/XSS injection risks, unvalidated environment variables
74+
- **Concurrency Problems**: Race conditions, synchronization issues
75+
- **Error Handling**: Missing error handling for critical operations
76+
- **Recursion Issues**: Missing base cases, stack overflow risks
77+
- **Regex Problems**: Catastrophic backtracking vulnerabilities
78+
79+
### What's NOT Reviewed
80+
81+
The action intentionally skips:
82+
- Code style, formatting, or naming conventions
83+
- Minor performance optimizations
84+
- Architectural decisions or design patterns
85+
- Feature completeness or functionality (unless broken)
86+
- Test coverage (unless tests are clearly broken)
87+
88+
## Installation & Setup
89+
90+
### GitHub Actions
91+
92+
Follow the Quick Start guide above. The action will:
93+
1. Install Factory AI's Droid CLI using the official installer
94+
2. Configure the CLI with your API key
95+
3. Execute the code review analysis
96+
97+
### Creating the Secret
98+
99+
1. Go to your repository **Settings** → **Secrets and variables** → **Actions**
100+
2. Create a new repository secret named `FACTORY_API_KEY`
101+
3. Paste your Factory AI API key (get one at [factory.ai](https://app.factory.ai/settings/api-keys))
102+
103+
## Technical Implementation
104+
105+
### How It Works
106+
107+
1. **Checkout**: Action checks out the repository at the PR head commit with full history
108+
2. **CLI Installation**: Downloads and installs Droid CLI from https://app.factory.ai/cli
109+
3. **PR Analysis**: Fetches complete PR data from GitHub URL (diff, comments, metadata)
110+
4. **Code Review**: Runs `droid exec --skip-permissions-unsafe` with the review prompt
111+
5. **Comment Submission**: Posts inline comments via GitHub REST API using curl
112+
6. **Error Handling**: Uploads debug artifacts if the review fails
113+
114+
### Review Limits
115+
116+
- Maximum of 10 inline comments per review (prioritizing most critical issues)
117+
- Comments only on modified lines in the PR
118+
- Suggestions provided only when fixes are certain
119+
- No PR approval/rejection to avoid blocking merge workflows
120+
121+
### Debug Information
122+
123+
On failure, the action uploads:
124+
- The review prompt used
125+
- Droid execution logs
126+
- Console output logs
127+
128+
These artifacts are retained for 7 days to help diagnose issues.
129+
130+
## Support
131+
132+
For issues or questions:
133+
- Open an issue in this repository
134+
- Visit [Factory AI Documentation](https://docs.factory.ai) for API details
135+
136+
## License
137+
138+
MIT License - see [LICENSE](LICENSE) file for details

action.yml

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
name: Factory Droid Review
2+
description: Automated Code Review for GitHub Pull Requests.
3+
author: Factory
4+
5+
inputs:
6+
factory-api-key:
7+
description: "The API key to run Droid Exec which powers the Review Droid (required)."
8+
required: true
9+
pr-number:
10+
description: "The Pull Request number being reviewed."
11+
required: true
12+
pr-head-sha:
13+
description: "The commit SHA of the Pull Request head branch for correct checkout."
14+
required: true
15+
16+
runs:
17+
using: "composite"
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
ref: ${{ inputs.pr-head-sha }}
24+
token: ${{ github.token }}
25+
26+
- name: Install Droid CLI
27+
shell: bash
28+
run: |
29+
curl -fsSL https://app.factory.ai/cli | sh
30+
echo "$HOME/.local/bin" >> $GITHUB_PATH
31+
"$HOME/.local/bin/droid" --version
32+
33+
- name: Perform automated code review
34+
shell: bash
35+
env:
36+
FACTORY_API_KEY: ${{ inputs.factory-api-key }}
37+
GH_TOKEN: ${{ github.token }}
38+
run: |
39+
set -euo pipefail
40+
41+
cat > prompt.txt << 'EOF'
42+
You are an automated code review system. Review the PR diff and identify clear issues that need to be fixed.
43+
44+
First, use the Web Fetch tool to get the full PR context from: https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}
45+
46+
This will provide you with:
47+
- PR title, description, and metadata
48+
- Complete diff with line numbers for positioning comments
49+
- Changed files with their status
50+
- Existing comments and review threads (to avoid duplicates)
51+
52+
Task: After fetching the PR data, review the code changes and directly submit your findings using the GitHub CLI.
53+
54+
Focus on these types of issues:
55+
- Dead/unreachable code (if (false), while (false), code after return/throw/break)
56+
- Broken control flow (missing break in switch, fallthrough bugs)
57+
- Async/await mistakes (missing await, .then without return, unhandled promise rejections)
58+
- Array/object mutations in React components or reducers
59+
- UseEffect dependency array problems (missing deps, incorrect deps)
60+
- Incorrect operator usage (== vs ===, && vs ||, = in conditions)
61+
- Off-by-one errors in loops or array indexing
62+
- Integer overflow/underflow in calculations
63+
- Regex catastrophic backtracking vulnerabilities
64+
- Missing base cases in recursive functions
65+
- Incorrect type coercion that changes behavior
66+
- Environment variable access without defaults or validation
67+
- Null/undefined dereferences
68+
- Resource leaks (unclosed files or connections)
69+
- SQL/XSS injection vulnerabilities
70+
- Concurrency/race conditions
71+
- Missing error handling for critical operations
72+
73+
Comment format for inline reviews:
74+
- Describe the issue clearly: "This async function is missing await"
75+
- Explain the impact: "Without await, the promise won't resolve before continuing"
76+
- ONLY provide commit suggestions when you are confident about the exact fix
77+
- When uncertain, just identify the problem without suggesting code changes
78+
- Example WITHOUT suggestion (when fix needs context):
79+
"Potential null dereference here. Consider adding a null check, but the exact approach depends on the business logic."
80+
81+
CRITICAL: GitHub suggestion format requirements:
82+
- Single-line suggestions: Use simple ```suggestion block
83+
- Multi-line suggestions: MUST include the complete replacement code
84+
- The suggestion block replaces EVERYTHING from the start_line to end_line in the comment
85+
- Never split multi-line suggestions - include ALL lines that need to be replaced
86+
87+
- Example single-line suggestion (when fix is certain):
88+
```suggestion
89+
const result = await fetchData();
90+
```
91+
92+
- Example multi-line suggestion (replaces entire block from start to end):
93+
```suggestion
94+
if (value != null) {
95+
return processValue(value);
96+
}
97+
return defaultValue;
98+
```
99+
100+
- Be specific and actionable, no emojis or decorative formatting
101+
102+
Skip commenting on:
103+
- Code style, formatting, or naming conventions
104+
- Minor performance optimizations
105+
- Architectural decisions or design patterns
106+
- Features or functionality (unless broken)
107+
- Test coverage (unless tests are clearly broken)
108+
109+
You may open and read other repository files beyond those listed above when it helps you understand the change, but keep all review comments anchored to the modified lines in this PR.
110+
111+
Use the fetched PR data to:
112+
- Avoid repeating resolved issues unless the problem still exists.
113+
- Reference ongoing discussions when adding a follow-up or clarification.
114+
- Prefer replying to existing open threads instead of creating duplicates when addressing the same code.
115+
- Ignore your own previous comments that have been resolved unless the underlying issue is still present.
116+
117+
Position calculation:
118+
- Use the line position from the diff data fetched from the PR
119+
- This is the line number in the diff, not the absolute file line
120+
- Comments must align with exact changed lines only
121+
122+
How to submit your review:
123+
124+
IMPORTANT REQUIREMENTS:
125+
- DO NOT request changes or approve the PR as this blocks the merge workflow
126+
- DO NOT use review statuses that affect the PR's mergeable state
127+
- PRIORITIZE inline comments on specific lines (suggestions are optional)
128+
- USE curl with the GitHub REST API to submit reviews with inline comments
129+
130+
To submit a review with inline comments:
131+
1. Create a JSON payload with your review data (event: "COMMENT", comments array with paths and positions)
132+
2. Use curl to POST to: https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews
133+
3. Include the GitHub token in the Authorization header: "Bearer $GH_TOKEN"
134+
4. When confident about fixes, include markdown ```suggestion blocks (but these are OPTIONAL)
135+
5. Use line positions from the PR diff (not absolute file line numbers)
136+
6. For multi-line comments: set start_line and line (end line) to define the range
137+
138+
Example single-line suggestion format in comment body:
139+
```
140+
Missing await on async call. This will cause the promise to not resolve properly.
141+
\`\`\`suggestion
142+
const data = await fetchUserData(userId);
143+
\`\`\`
144+
```
145+
146+
Example multi-line suggestion format (CRITICAL - include ALL replacement lines):
147+
```json
148+
{
149+
"path": "path/to/file.js",
150+
"start_line": 10, // Line number in the diff where comment starts (for multi-line comments)
151+
"line": 13, // Line number in the diff where comment ends (required for all comments)
152+
"start_side": "RIGHT", // Side of diff where comment starts (LEFT=old code, RIGHT=new code)
153+
"side": "RIGHT", // Side of diff where comment ends (usually same as start_side)
154+
"body": "Missing null check will cause runtime error.\n\`\`\`suggestion\nif (value != null) {\n return processValue(value);\n}\nreturn defaultValue;\n\`\`\`"
155+
}
156+
```
157+
158+
IMPORTANT for multi-line suggestions:
159+
- The suggestion block must contain the COMPLETE replacement for lines start_line through line
160+
- GitHub will replace ALL lines in that range with your suggestion content
161+
- If you only include the last line, GitHub will still replace the entire range, breaking the code
162+
163+
For simple status updates without inline comments:
164+
- You may use 'gh pr comment' to post a general comment
165+
- Example: "Code review complete. No issues found." or "Found X issues requiring attention."
166+
167+
Review guidelines:
168+
- Focus on inline comments identifying specific issues
169+
- Submit at most 10 inline comments, prioritizing the most critical issues
170+
- Include commit suggestions ONLY when you're confident about the exact fix
171+
- When uncertain about the fix, describe the problem without a code suggestion
172+
- Check for existing comments to avoid duplicates
173+
- Group related inline comments in a single review submission when efficient
174+
EOF
175+
176+
echo "Running code review analysis..."
177+
droid exec --skip-permissions-unsafe -f prompt.txt
178+
179+
- name: Upload debug artifacts on failure
180+
if: ${{ failure() }}
181+
uses: actions/upload-artifact@v4
182+
with:
183+
name: droid-review-debug-${{ github.run_id }}
184+
path: |
185+
prompt.txt
186+
${{ runner.home }}/.factory/logs/droid-log-single.log
187+
${{ runner.home }}/.factory/logs/console.log
188+
if-no-files-found: ignore
189+
retention-days: 7

0 commit comments

Comments
 (0)