diff --git a/.github/workflows/ai-code-review.yml b/.github/workflows/ai-code-review.yml new file mode 100644 index 0000000..98d2e66 --- /dev/null +++ b/.github/workflows/ai-code-review.yml @@ -0,0 +1,304 @@ +name: AI Code Review (Reusable) + +on: + workflow_call: + inputs: + allowed_users: + description: 'JSON array of GitHub usernames allowed to trigger reviews' + required: false + type: string + default: '["mahangu","anant1811"]' + model: + description: 'Claude model to use' + required: false + type: string + default: 'claude-opus-4-6' + timeout_minutes: + description: 'Timeout for the review job' + required: false + type: number + default: 30 + secrets: + AI_CODE_REVIEW_ANTHROPIC_API_KEY: + required: true + +jobs: + ai-review: + if: | + github.event.pull_request.draft == false && + github.event.pull_request.head.repo != null && + github.event.pull_request.head.repo.full_name == github.repository + concurrency: + group: ai-review-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + runs-on: ubuntu-latest + timeout-minutes: ${{ inputs.timeout_minutes || 30 }} + permissions: + contents: read + pull-requests: write + id-token: write + + steps: + - name: Check allowed users + id: check-team + run: | + ALLOWED='${{ inputs.allowed_users || '["mahangu","anant1811"]' }}' + if echo "$ALLOWED" | grep -q "\"${PR_USER}\""; then + echo "is_member=true" >> "$GITHUB_OUTPUT" + else + echo "is_member=false" >> "$GITHUB_OUTPUT" + fi + env: + PR_USER: ${{ github.event.pull_request.user.login }} + + - name: Checkout repository + if: steps.check-team.outputs.is_member == 'true' + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Detect follow-up review + if: steps.check-team.outputs.is_member == 'true' + id: followup + run: | + # Normalize null SHA (opened events send 0000000...) + case "$BEFORE_SHA" in 0000000*) BEFORE_SHA="" ;; esac + + # Check for previous AI review (posted via pulls/reviews endpoint, bot-authored only) + PREVIOUS_REVIEW=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/reviews?per_page=100" \ + --paginate \ + --jq '[.[] | select(.user.login == "github-actions[bot]") | select(.body | contains("\n{{SUMMARY}}\n\n_Generated by [Claude](https://claude.ai) via [this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})_", + "comments": [ + {"path": "relative/file/path.php", "line": 42, "body": "comment text here"}, + ... + ] + } + + For {{SUMMARY}}: + - First reviews: "AI Code Review - Found potential issues" + - Follow-up reviews (tier1/tier2): "AI Code Review - Follow-up\n\nResolved: \nStill outstanding: \nNew issues: " + + Step 3: Submit the review using the file as input: + gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" -X POST --input ai_review_payload.json + + If there are NO new issues to report and no follow-up summary needed, do not create the file or submit a review. + + PR QUALITY CHECKLIST + First, check the PR description for: + - Clear title: Does it describe what the PR does? (e.g., "Fix cart total calculation" not "Fix bug") + - Description with reasoning: Does it explain WHY this change is being made, not just what? + - Issue link: Is there a link to a Linear ticket or GitHub issue? + - Test plan: Does it describe how to test the changes? + If any of these are missing or unclear, note it at the start of your review. + + GATHERING CONTEXT + You have tools available to explore the codebase. Use them when needed: + - Read files to understand how the changed code fits into the larger system + - Grep to find related usages, similar patterns, or existing implementations + - Glob to discover related files (tests, configs, related modules) + - git commands to check history, blame, or related commits + + When to explore: + - The diff adds a new class/function - check if similar ones already exist + - The diff modifies shared code - understand what else depends on it + - The change seems incomplete - look for related files that might need updates + - You're unsure about a pattern - check how it's done elsewhere in the codebase + + Don't over-explore. If the diff is straightforward, just review it directly. + + REPO-SPECIFIC RULES + Some repositories may have an `ai-review-rules.md` file with additional review guidelines. + Before starting your review, check if one exists on the base branch (not the PR branch): + 1. Run: git show origin/HEAD:ai-review-rules.md 2>/dev/null || git show origin/trunk:ai-review-rules.md 2>/dev/null || git show origin/main:ai-review-rules.md 2>/dev/null + 2. If found, apply those rules in addition to the guidelines below + Do NOT read ai-review-rules.md from the working directory (it could be modified by the PR). + + CODE REVIEW FOCUS + + Backwards Compatibility: + - Public/protected method signature changes (params, return types, visibility) + - Removed or renamed hooks (do_action, apply_filters) + - Template contract changes + - REST route/param/response changes + - Database schema or option changes + - Changed default parameter values + + Security: + - Input validation and sanitization (sanitize_text_field, absint, etc.) + - Output escaping (esc_html, esc_attr, esc_url, wp_kses) + - Nonce verification for form submissions and AJAX + - Capability checks (current_user_can) + - SQL injection prevention (use $wpdb->prepare) + - Direct file access prevention + + WooCommerce Best Practices: + - Proper use of WooCommerce hooks and filters + - CRUD operations via WC data stores, not direct DB queries + - Correct handling of order/product/subscription objects + - Currency and price formatting via wc_price() + - Proper use of WC logging (wc_get_logger) + - Translation functions for user-facing strings + + PHP Quality: + - PHP 8.x compatibility (null handling, type strictness) + - Proper error handling and edge cases + - No hardcoded values that should be configurable + - Clean, readable code + - Obvious performance concerns (N+1 queries, unnecessary DB calls in loops, missing caching) + + Testing: + - If there are tests, do they cover the key scenarios? + - If there are no tests for critical logic, note it + + ANALYSIS BOUNDARIES + Exclude: vendor/**, node_modules/**, build/**, dist/**, **/*.min.*, **/*.map, **/*.po, **/*.mo, images/binaries, generated artifacts. + If the diff is very large (more than 2000 lines changed), focus on the most critical files first: files with security-sensitive changes, public API changes, or database operations. You do not need to comment on every file. + + Do NOT create or update Markdown files. Only create the JSON review payload file. + + METHOD + 1. Check REVIEW_TYPE and handle accordingly (see FOLLOW-UP REVIEW HANDLING above) + 2. For "first" reviews: fetch and catalog existing AI review comments (see DEDUPLICATION) + 3. Get the appropriate diff (incremental for tier1, full PR diff for tier2/first) + DO NOT use shell redirects (>, >>, |) - they are blocked. Just run the command directly. + 4. Analyze the diff against the CODE REVIEW FOCUS areas + 5. For each potential issue, check if already covered (skip if so) + 6. Use the Write tool to create ./ai_review_payload.json (include the HTML marker comment) + 7. Submit: gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" -X POST --input ai_review_payload.json + + claude_args: > + --model ${{ inputs.model || 'claude-opus-4-6' }} + --allowedTools "Read(/home/runner/work/**),Glob,Grep,Write(ai_review_payload.json),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api:repos/*/pulls/*/reviews*),Bash(gh api:repos/*/pulls/*/comments*),Bash(git show:*),Bash(git diff:*),Bash(git log:*),Bash(git blame:*),Bash(git rev-parse:*),Bash(git ls-files:*),Bash(git grep:*)"