|
| 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