chore: update cagent to v1.32.4 #426
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 | |
| checks: 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 && | |
| startsWith(github.event.comment.body, '/review') && | |
| (github.event.comment.user.type != 'Bot' || github.event.comment.user.login == 'docker-agent[bot]') | |
| runs-on: ubuntu-latest | |
| env: | |
| HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} | |
| steps: | |
| # Generate GitHub App token first so the check run is created under the app's identity | |
| # (prevents GitHub from nesting it under unrelated pull_request-triggered workflows) | |
| - 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: Create check run | |
| id: create-check | |
| continue-on-error: true # Don't fail if checks: write permission is missing | |
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 | |
| env: | |
| APP_TOKEN: ${{ steps.app-token.outputs.token }} | |
| with: | |
| github-token: ${{ steps.app-token.outputs.token || github.token }} | |
| script: | | |
| const prNumber = context.issue.number; | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber | |
| }); | |
| const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| const { data: check } = await github.rest.checks.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: 'PR Review', | |
| head_sha: pr.head.sha, | |
| status: 'in_progress', | |
| started_at: new Date().toISOString(), | |
| details_url: runUrl | |
| }); | |
| core.setOutput('check-id', check.id); | |
| # 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 | |
| - 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 }} | |
| trusted-bot-app-id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} | |
| 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 }} | |
| - name: Update check run | |
| if: always() && steps.create-check.outputs.check-id != '' | |
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 | |
| env: | |
| CHECK_ID: ${{ steps.create-check.outputs.check-id }} | |
| JOB_STATUS: ${{ job.status }} | |
| with: | |
| github-token: ${{ steps.app-token.outputs.token || github.token }} | |
| script: | | |
| const conclusion = process.env.JOB_STATUS === 'cancelled' ? 'cancelled' : process.env.JOB_STATUS === 'success' ? 'success' : 'failure'; | |
| try { | |
| await github.rest.checks.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| check_run_id: parseInt(process.env.CHECK_ID, 10), | |
| status: 'completed', | |
| conclusion: conclusion, | |
| completed_at: new Date().toISOString() | |
| }); | |
| } catch (error) { | |
| core.warning(`Failed to update check run: ${error.message}`); | |
| } | |
| # ========================================================================== | |
| # CAPTURE FEEDBACK | |
| # Saves feedback data as an artifact for the workflow_run-triggered | |
| # reply-to-feedback workflow. This job intentionally avoids using secrets | |
| # so it works for fork PRs in public repos. The reply-to-feedback.yml | |
| # workflow runs via workflow_run in the base repo context with full | |
| # permissions and secrets. | |
| # ========================================================================== | |
| 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 }} | |
| REPO: ${{ github.repository }} | |
| 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/$REPO/pulls/comments/$PARENT_ID" 2>/dev/null || echo "{}") | |
| body=$(echo "$parent" | jq -r '.body // ""') | |
| parent_user_type=$(echo "$parent" | jq -r '.user.type // ""') | |
| # Defense-in-depth: verify the root comment was posted by a Bot (agent) AND | |
| # contains the review marker but NOT the reply marker. | |
| if [ "$parent_user_type" = "Bot" ] && \ | |
| echo "$body" | grep -q "<!-- cagent-review -->" && \ | |
| ! echo "$body" | grep -q "<!-- cagent-review-reply -->"; then | |
| echo "is_agent=true" >> $GITHUB_OUTPUT | |
| # Extract file path and line from the root comment for context | |
| echo "file_path=$(echo "$parent" | jq -r '.path // ""')" >> $GITHUB_OUTPUT | |
| echo "line=$(echo "$parent" | jq -r '.line // .original_line // ""')" >> $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 and metadata | |
| if: steps.check.outputs.is_agent == 'true' | |
| shell: bash | |
| env: | |
| COMMENT_JSON: ${{ toJSON(github.event.comment) }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| REPO: ${{ github.repository }} | |
| PARENT_ID: ${{ github.event.comment.in_reply_to_id }} | |
| COMMENT_ID: ${{ github.event.comment.id }} | |
| AUTHOR: ${{ github.event.comment.user.login }} | |
| AUTHOR_TYPE: ${{ github.event.comment.user.type }} | |
| FILE_PATH: ${{ steps.check.outputs.file_path }} | |
| LINE: ${{ steps.check.outputs.line }} | |
| run: | | |
| mkdir -p feedback | |
| echo "$COMMENT_JSON" > feedback/feedback.json | |
| # Save metadata for the workflow_run reply job | |
| jq -n \ | |
| --arg pr_number "$PR_NUMBER" \ | |
| --arg repo "$REPO" \ | |
| --arg parent_comment_id "$PARENT_ID" \ | |
| --arg comment_id "$COMMENT_ID" \ | |
| --arg author "$AUTHOR" \ | |
| --arg author_type "$AUTHOR_TYPE" \ | |
| --arg file_path "$FILE_PATH" \ | |
| --arg line "$LINE" \ | |
| --argjson is_agent_comment true \ | |
| '{ | |
| pr_number: $pr_number, | |
| repo: $repo, | |
| parent_comment_id: $parent_comment_id, | |
| comment_id: $comment_id, | |
| author: $author, | |
| author_type: $author_type, | |
| file_path: $file_path, | |
| line: $line, | |
| is_agent_comment: $is_agent_comment | |
| }' > feedback/metadata.json | |
| echo "📦 Saved feedback data and metadata for workflow_run 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 | |