Skip to content

chore: update cagent to v1.32.4 #426

chore: update cagent to v1.32.4

chore: update cagent to v1.32.4 #426

# 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