Use comment-only PR reviews, never approve or request changes #219
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Dogfoods the PR review workflow on this repo's own PRs. | |
| # Instead of calling the reusable review-pr.yml (which uses the published | |
| # composite action), this workflow inlines the jobs and points at ./review-pr | |
| # so that PRs changing the review logic test themselves. | |
| name: Self PR Review | |
| on: | |
| issue_comment: | |
| types: [created] | |
| pull_request_review_comment: | |
| types: [created] | |
| pull_request_target: | |
| types: [ready_for_review, opened] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| # ========================================================================== | |
| # AUTOMATIC REVIEW FOR ORG MEMBERS | |
| # Triggers when a PR is marked ready for review or opened (non-draft) | |
| # Only runs for members of the docker org (supports fork-based workflow) | |
| # ========================================================================== | |
| auto-review: | |
| if: | | |
| github.event_name == 'pull_request_target' && | |
| !github.event.pull_request.draft | |
| runs-on: ubuntu-latest | |
| env: | |
| HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} | |
| steps: | |
| - name: Check if PR author is org member | |
| id: membership | |
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 | |
| with: | |
| github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN }} | |
| script: | | |
| const org = 'docker'; | |
| const username = context.payload.pull_request.user.login; | |
| try { | |
| await github.rest.orgs.checkMembershipForUser({ | |
| org: org, | |
| username: username | |
| }); | |
| core.setOutput('is_member', 'true'); | |
| console.log(`✅ ${username} is a ${org} org member - proceeding with auto-review`); | |
| } catch (error) { | |
| if (error.status === 404 || error.status === 302) { | |
| core.setOutput('is_member', 'false'); | |
| console.log(`⏭️ ${username} is not a ${org} org member - skipping auto-review`); | |
| } else if (error.status === 401) { | |
| core.setFailed( | |
| '❌ CAGENT_ORG_MEMBERSHIP_TOKEN secret is missing or invalid.\n\n' + | |
| `This secret is required to check ${org} org membership for auto-reviews.\n\n` + | |
| 'To fix this:\n' + | |
| '1. Create a classic PAT with read:org scope at https://github.com/settings/tokens/new\n' + | |
| '2. Add it as an org secret named CAGENT_ORG_MEMBERSHIP_TOKEN' | |
| ); | |
| } else { | |
| core.setFailed(`Failed to check org membership: ${error.message}`); | |
| } | |
| } | |
| # Safe to checkout PR head because review-pr only READS files (no code execution) | |
| - name: Checkout PR head | |
| if: steps.membership.outputs.is_member == 'true' | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| with: | |
| fetch-depth: 0 | |
| ref: refs/pull/${{ github.event.pull_request.number }}/head | |
| # Generate GitHub App token for custom app identity (optional - falls back to github.token) | |
| - name: Generate GitHub App token | |
| if: steps.membership.outputs.is_member == 'true' && env.HAS_APP_SECRETS == 'true' | |
| id: app-token | |
| continue-on-error: true # Don't fail workflow if token generation fails | |
| uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 | |
| with: | |
| app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} | |
| private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} | |
| - name: Run PR Review | |
| if: steps.membership.outputs.is_member == 'true' | |
| id: run-review | |
| continue-on-error: true # Don't fail the calling workflow if the review errors | |
| uses: ./review-pr | |
| with: | |
| pr-number: ${{ github.event.pull_request.number }} | |
| github-token: ${{ steps.app-token.outputs.token || github.token }} | |
| anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| openai-api-key: ${{ secrets.OPENAI_API_KEY }} | |
| google-api-key: ${{ secrets.GOOGLE_API_KEY }} | |
| aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} | |
| xai-api-key: ${{ secrets.XAI_API_KEY }} | |
| nebius-api-key: ${{ secrets.NEBIUS_API_KEY }} | |
| mistral-api-key: ${{ secrets.MISTRAL_API_KEY }} | |
| # ========================================================================== | |
| # MANUAL REVIEW PIPELINE | |
| # Triggers when someone comments /review on a PR | |
| # ========================================================================== | |
| manual-review: | |
| if: github.event.issue.pull_request && contains(github.event.comment.body, '/review') | |
| runs-on: ubuntu-latest | |
| env: | |
| HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} | |
| steps: | |
| # Checkout PR head (not default branch) | |
| # Note: Authorization is handled by the composite action's built-in check | |
| - name: Checkout PR head | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| with: | |
| fetch-depth: 0 | |
| ref: refs/pull/${{ github.event.issue.number }}/head | |
| # Generate GitHub App token for custom app identity (optional - falls back to github.token) | |
| - name: Generate GitHub App token | |
| if: env.HAS_APP_SECRETS == 'true' | |
| id: app-token | |
| continue-on-error: true # Don't fail workflow if token generation fails | |
| uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 | |
| with: | |
| app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} | |
| private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} | |
| - name: Run PR Review | |
| id: run-review | |
| continue-on-error: true # Don't fail the calling workflow if the review errors | |
| uses: ./review-pr | |
| with: | |
| pr-number: ${{ github.event.issue.number }} | |
| comment-id: ${{ github.event.comment.id }} | |
| github-token: ${{ steps.app-token.outputs.token || github.token }} | |
| anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| openai-api-key: ${{ secrets.OPENAI_API_KEY }} | |
| google-api-key: ${{ secrets.GOOGLE_API_KEY }} | |
| aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} | |
| xai-api-key: ${{ secrets.XAI_API_KEY }} | |
| nebius-api-key: ${{ secrets.NEBIUS_API_KEY }} | |
| mistral-api-key: ${{ secrets.MISTRAL_API_KEY }} | |
| # ========================================================================== | |
| # CAPTURE FEEDBACK | |
| # Saves feedback data as an artifact for lazy processing. This job | |
| # intentionally avoids using secrets so it works for fork PRs in public | |
| # repos. The actual AI processing happens during the next review run, | |
| # which has full secret access via pull_request_target or issue_comment. | |
| # ========================================================================== | |
| capture-feedback: | |
| if: github.event_name == 'pull_request_review_comment' && github.event.comment.in_reply_to_id | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check if reply is to agent comment | |
| id: check | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PARENT_ID: ${{ github.event.comment.in_reply_to_id }} | |
| run: | | |
| if [ -z "$PARENT_ID" ]; then | |
| echo "is_agent=false" >> $GITHUB_OUTPUT | |
| echo "⏭️ Not a reply comment, skipping" | |
| exit 0 | |
| fi | |
| parent=$(gh api repos/${{ github.repository }}/pulls/comments/$PARENT_ID 2>/dev/null || echo "{}") | |
| if echo "$parent" | jq -r '.body // ""' | grep -q "<!-- cagent-review -->"; then | |
| echo "is_agent=true" >> $GITHUB_OUTPUT | |
| echo "✅ Reply is to an agent review comment" | |
| else | |
| echo "is_agent=false" >> $GITHUB_OUTPUT | |
| echo "⏭️ Not a reply to agent comment, skipping" | |
| fi | |
| - name: Save feedback data | |
| if: steps.check.outputs.is_agent == 'true' | |
| shell: bash | |
| env: | |
| COMMENT_JSON: ${{ toJSON(github.event.comment) }} | |
| run: | | |
| mkdir -p feedback | |
| echo "$COMMENT_JSON" > feedback/feedback.json | |
| echo "📦 Saved feedback data for async processing" | |
| - name: Upload feedback artifact | |
| if: steps.check.outputs.is_agent == 'true' | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: pr-review-feedback | |
| path: feedback/ | |
| retention-days: 90 |