Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/self-review-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
if: |
github.event_name == 'issue_comment' ||
github.event.workflow_run.conclusion == 'success'
uses: docker/cagent-action/.github/workflows/review-pr.yml@f208610469d69f20983cad64c577949a132caa33 # v1.5.3
uses: ./.github/workflows/review-pr.yml
permissions:
contents: read # Read repository files and PR diffs
pull-requests: write # Post review comments
Expand Down
291 changes: 291 additions & 0 deletions .github/workflows/test-e2e-reviewer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
name: Test Reviewer E2E (Manual)

on:
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to run the scenario against'
required: true
scenario:
description: 'Scenario to test'
required: true
type: choice
options:
- full-review
- top-level-mention
- inline-mention
default: full-review

permissions:
contents: read

jobs:
full-review:
name: Full Review E2E
if: inputs.scenario == 'full-review'
uses: ./.github/workflows/review-pr.yml
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
checks: write
actions: read
with:
pr-number: ${{ inputs.pr_number }}
secrets:
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 }}

top-level-mention:
name: Top-Level Mention E2E
if: inputs.scenario == 'top-level-mention'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Check if fork PR
id: fork-check
run: |
HEAD_REPO="${{ github.event.pull_request.head.repo.full_name || '' }}"
if [[ "${{ github.event_name }}" == "pull_request" && "$HEAD_REPO" != "${{ github.repository }}" && -n "$HEAD_REPO" ]]; then
echo "⏭️ Skipping - fork PR (secrets not available)"
echo "is_fork=true" >> $GITHUB_OUTPUT
else
echo "is_fork=false" >> $GITHUB_OUTPUT
fi

- name: Checkout code
if: steps.fork-check.outputs.is_fork != 'true'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup pnpm
if: steps.fork-check.outputs.is_fork != 'true'
uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5
with:
run_install: false

- name: Setup Node.js
if: steps.fork-check.outputs.is_fork != 'true'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm

- name: Build action
if: steps.fork-check.outputs.is_fork != 'true'
run: pnpm install --frozen-lockfile && pnpm build

- name: Setup credentials
if: steps.fork-check.outputs.is_fork != 'true'
uses: docker/cagent-action/setup-credentials@2a43a3882401f45e3114df7f6d66eca184993a90 # v1.5.2

- name: Write synthetic issue_comment event
if: steps.fork-check.outputs.is_fork != 'true'
run: |
jq -n \
--arg actor "${{ github.actor }}" \
--argjson pr_number "${{ inputs.pr_number }}" \
'{
"action": "created",
"issue": {
"number": $pr_number,
"pull_request": { "url": ("https://api.github.com/repos/docker/cagent-action/pulls/" + ($pr_number | tostring)) }
},
"comment": {
"id": 9999999901,
"body": "@docker-agent this is a manual e2e test — please reply with a brief acknowledgement.",
"user": { "login": $actor, "type": "User" }
},
"repository": {
"owner": { "login": "docker" },
"name": "cagent-action"
},
"sender": { "login": $actor, "type": "User" }
}' > /tmp/test-event-toplevel.json

- name: Run mention-reply handler
if: steps.fork-check.outputs.is_fork != 'true'
id: mention-handler
uses: ./.github/actions/mention-reply
env:
GITHUB_EVENT_PATH: /tmp/test-event-toplevel.json
GITHUB_EVENT_NAME: issue_comment
with:
github-token: ${{ env.GITHUB_APP_TOKEN || github.token }}
org-membership-token: ${{ env.ORG_MEMBERSHIP_TOKEN || github.token }}

- name: Run mention reply
if: steps.fork-check.outputs.is_fork != 'true' && steps.mention-handler.outputs.should-reply == 'true'
id: run-reply
uses: ./review-pr/mention-reply
with:
mention-context: ${{ steps.mention-handler.outputs.prompt }}
owner: ${{ steps.mention-handler.outputs.owner }}
repo: ${{ steps.mention-handler.outputs.repo }}
pr-number: ${{ steps.mention-handler.outputs.pr-number }}
is-inline: ${{ steps.mention-handler.outputs.is-inline }}
in-reply-to-id: ${{ steps.mention-handler.outputs.in-reply-to-id }}
anthropic-api-key: ${{ env.ANTHROPIC_API_KEY_FROM_SSM || secrets.ANTHROPIC_API_KEY }}
openai-api-key: ${{ env.OPENAI_API_KEY_FROM_SSM || secrets.OPENAI_API_KEY }}
github-token: ${{ env.GITHUB_APP_TOKEN || github.token }}
skip-auth: "true"

- name: Report outcome
if: steps.fork-check.outputs.is_fork != 'true'
run: |
echo "should-reply: ${{ steps.mention-handler.outputs.should-reply }}"
echo "owner: ${{ steps.mention-handler.outputs.owner }}"
echo "repo: ${{ steps.mention-handler.outputs.repo }}"
echo "pr-number: ${{ steps.mention-handler.outputs.pr-number }}"
echo "is-inline: ${{ steps.mention-handler.outputs.is-inline }}"
echo "✅ Top-level mention scenario completed (manual run — no assertion)"

inline-mention:
name: Inline Mention E2E
if: inputs.scenario == 'inline-mention'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Check if fork PR
id: fork-check
run: |
HEAD_REPO="${{ github.event.pull_request.head.repo.full_name || '' }}"
if [[ "${{ github.event_name }}" == "pull_request" && "$HEAD_REPO" != "${{ github.repository }}" && -n "$HEAD_REPO" ]]; then
echo "⏭️ Skipping - fork PR (secrets not available)"
echo "is_fork=true" >> $GITHUB_OUTPUT
else
echo "is_fork=false" >> $GITHUB_OUTPUT
fi

- name: Checkout code
if: steps.fork-check.outputs.is_fork != 'true'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup pnpm
if: steps.fork-check.outputs.is_fork != 'true'
uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5
with:
run_install: false

- name: Setup Node.js
if: steps.fork-check.outputs.is_fork != 'true'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm

- name: Build action
if: steps.fork-check.outputs.is_fork != 'true'
run: pnpm install --frozen-lockfile && pnpm build

- name: Setup credentials
if: steps.fork-check.outputs.is_fork != 'true'
uses: docker/cagent-action/setup-credentials@2a43a3882401f45e3114df7f6d66eca184993a90 # v1.5.2

- name: Create anchor review comment
if: steps.fork-check.outputs.is_fork != 'true'
id: create-anchor
env:
GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }}
PR_NUMBER: ${{ inputs.pr_number }}
run: |
HEAD_SHA=$(gh api "repos/docker/cagent-action/pulls/$PR_NUMBER" --jq '.head.sha')
DIFF_FILE=$(gh api "repos/docker/cagent-action/pulls/$PR_NUMBER/files" --jq '.[0].filename')
echo "Using diff file: $DIFF_FILE"
COMMENT_ID=$(gh api "repos/docker/cagent-action/pulls/$PR_NUMBER/comments" \
-X POST \
--input - <<< $(jq -n \
--arg sha "$HEAD_SHA" \
--arg path "$DIFF_FILE" \
'{"body": "manual e2e test anchor comment — safe to delete", "commit_id": $sha, "path": $path, "side": "RIGHT", "position": 1}') \
--jq '.id')
echo "Created anchor comment ID: $COMMENT_ID"
echo "test_comment_id=$COMMENT_ID" >> $GITHUB_OUTPUT

- name: Write synthetic pull_request_review_comment event
if: steps.fork-check.outputs.is_fork != 'true'
run: |
COMMENT_ID="${{ steps.create-anchor.outputs.test_comment_id }}"
jq -n \
--arg actor "${{ github.actor }}" \
--argjson comment_id "$COMMENT_ID" \
--argjson pr_number "${{ inputs.pr_number }}" \
'{
"action": "created",
"pull_request": { "number": $pr_number },
"comment": {
"id": $comment_id,
"body": "@docker-agent this is a manual e2e test of the inline mention path.",
"path": "README.md",
"line": 1,
"original_line": 1,
"diff_hunk": "@@ -1,1 +1,1 @@\n-old\n+new",
"user": { "login": $actor, "type": "User" }
},
"repository": {
"owner": { "login": "docker" },
"name": "cagent-action"
},
"sender": { "login": $actor, "type": "User" }
}' > /tmp/test-event-inline.json

- name: Run mention-reply handler
if: steps.fork-check.outputs.is_fork != 'true'
id: mention-handler
uses: ./.github/actions/mention-reply
env:
GITHUB_EVENT_PATH: /tmp/test-event-inline.json
GITHUB_EVENT_NAME: pull_request_review_comment
with:
github-token: ${{ env.GITHUB_APP_TOKEN || github.token }}
org-membership-token: ${{ env.ORG_MEMBERSHIP_TOKEN || github.token }}

- name: Run mention reply
if: steps.fork-check.outputs.is_fork != 'true' && steps.mention-handler.outputs.should-reply == 'true'
id: run-reply
uses: ./review-pr/mention-reply
with:
mention-context: ${{ steps.mention-handler.outputs.prompt }}
owner: ${{ steps.mention-handler.outputs.owner }}
repo: ${{ steps.mention-handler.outputs.repo }}
pr-number: ${{ steps.mention-handler.outputs.pr-number }}
is-inline: ${{ steps.mention-handler.outputs.is-inline }}
in-reply-to-id: ${{ steps.mention-handler.outputs.in-reply-to-id }}
anthropic-api-key: ${{ env.ANTHROPIC_API_KEY_FROM_SSM || secrets.ANTHROPIC_API_KEY }}
openai-api-key: ${{ env.OPENAI_API_KEY_FROM_SSM || secrets.OPENAI_API_KEY }}
github-token: ${{ env.GITHUB_APP_TOKEN || github.token }}
skip-auth: "true"

- name: Report outcome
Comment thread
derekmisler marked this conversation as resolved.
if: steps.fork-check.outputs.is_fork != 'true'
run: |
echo "should-reply: ${{ steps.mention-handler.outputs.should-reply }}"
echo "is-inline: ${{ steps.mention-handler.outputs.is-inline }}"
echo "in-reply-to-id: ${{ steps.mention-handler.outputs.in-reply-to-id }}"
echo "✅ Inline mention scenario completed (manual run — no assertion)"

- name: Cleanup anchor and replies
if: always() && steps.fork-check.outputs.is_fork != 'true'
continue-on-error: true
env:
GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }}
ANCHOR_ID: ${{ steps.create-anchor.outputs.test_comment_id }}
run: |
if [ -z "$ANCHOR_ID" ]; then exit 0; fi
# Delete thread replies first
gh api "repos/docker/cagent-action/pulls/${{ inputs.pr_number }}/comments" \
| jq --argjson id "$ANCHOR_ID" '[.[] | select(.in_reply_to_id == $id)] | .[].id' \
| while read -r reply_id; do
gh api "repos/docker/cagent-action/pulls/comments/$reply_id" -X DELETE || true
done
# Delete anchor
gh api "repos/docker/cagent-action/pulls/comments/$ANCHOR_ID" -X DELETE || true
Loading
Loading