Skip to content

Commit aa2384b

Browse files
committed
Allow docker agent to request reviews
Signed-off-by: Derek Misler <derek.misler@docker.com>
1 parent f94d60e commit aa2384b

File tree

4 files changed

+57
-22
lines changed

4 files changed

+57
-22
lines changed

.github/workflows/review-pr.yml

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,25 @@ jobs:
207207
exit-code: ${{ steps.run-review.outputs.exit-code }}
208208

209209
steps:
210+
# Generate GitHub App token first so the check run is created under the app's identity
211+
# (prevents GitHub from nesting it under unrelated pull_request-triggered workflows)
212+
- name: Generate GitHub App token
213+
if: env.HAS_APP_SECRETS == 'true'
214+
id: app-token
215+
continue-on-error: true # Don't fail workflow if token generation fails
216+
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2
217+
with:
218+
app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }}
219+
private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }}
220+
210221
- name: Create check run
211222
id: create-check
212223
continue-on-error: true # Don't fail if caller didn't grant checks: write
213224
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
214225
env:
215226
PR_NUMBER: ${{ inputs.pr-number || github.event.issue.number }}
216227
with:
228+
github-token: ${{ steps.app-token.outputs.token || github.token }}
217229
script: |
218230
const prNumber = parseInt(process.env.PR_NUMBER, 10);
219231
const { data: pr } = await github.rest.pulls.get({
@@ -241,16 +253,6 @@ jobs:
241253
fetch-depth: 0
242254
ref: refs/pull/${{ github.event.issue.number }}/head
243255

244-
# Generate GitHub App token for custom app identity (optional - falls back to github.token)
245-
- name: Generate GitHub App token
246-
if: env.HAS_APP_SECRETS == 'true'
247-
id: app-token
248-
continue-on-error: true # Don't fail workflow if token generation fails
249-
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2
250-
with:
251-
app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }}
252-
private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }}
253-
254256
- name: Run PR Review
255257
id: run-review
256258
continue-on-error: true # Don't fail the calling workflow if the review errors
@@ -262,6 +264,7 @@ jobs:
262264
add-prompt-files: ${{ inputs.add-prompt-files }}
263265
model: ${{ inputs.model }}
264266
github-token: ${{ steps.app-token.outputs.token || github.token }}
267+
trusted-bot-app-id: ${{ secrets.CAGENT_REVIEWER_APP_ID }}
265268
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
266269
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
267270
google-api-key: ${{ secrets.GOOGLE_API_KEY }}
@@ -277,6 +280,7 @@ jobs:
277280
CHECK_ID: ${{ steps.create-check.outputs.check-id }}
278281
JOB_STATUS: ${{ job.status }}
279282
with:
283+
github-token: ${{ steps.app-token.outputs.token || github.token }}
280284
script: |
281285
const conclusion = process.env.JOB_STATUS === 'cancelled' ? 'cancelled' : process.env.JOB_STATUS === 'success' ? 'success' : 'failure';
282286
try {

.github/workflows/self-review-pr.yml

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,25 @@ jobs:
112112
HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }}
113113

114114
steps:
115+
# Generate GitHub App token first so the check run is created under the app's identity
116+
# (prevents GitHub from nesting it under unrelated pull_request-triggered workflows)
117+
- name: Generate GitHub App token
118+
if: env.HAS_APP_SECRETS == 'true'
119+
id: app-token
120+
continue-on-error: true # Don't fail workflow if token generation fails
121+
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2
122+
with:
123+
app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }}
124+
private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }}
125+
115126
- name: Create check run
116127
id: create-check
117128
continue-on-error: true # Don't fail if checks: write permission is missing
118129
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
130+
env:
131+
APP_TOKEN: ${{ steps.app-token.outputs.token }}
119132
with:
133+
github-token: ${{ steps.app-token.outputs.token || github.token }}
120134
script: |
121135
const prNumber = context.issue.number;
122136
const { data: pr } = await github.rest.pulls.get({
@@ -144,16 +158,6 @@ jobs:
144158
fetch-depth: 0
145159
ref: refs/pull/${{ github.event.issue.number }}/head
146160

147-
# Generate GitHub App token for custom app identity (optional - falls back to github.token)
148-
- name: Generate GitHub App token
149-
if: env.HAS_APP_SECRETS == 'true'
150-
id: app-token
151-
continue-on-error: true # Don't fail workflow if token generation fails
152-
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2
153-
with:
154-
app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }}
155-
private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }}
156-
157161
- name: Run PR Review
158162
id: run-review
159163
continue-on-error: true # Don't fail the calling workflow if the review errors
@@ -177,6 +181,7 @@ jobs:
177181
CHECK_ID: ${{ steps.create-check.outputs.check-id }}
178182
JOB_STATUS: ${{ job.status }}
179183
with:
184+
github-token: ${{ steps.app-token.outputs.token || github.token }}
180185
script: |
181186
const conclusion = process.env.JOB_STATUS === 'cancelled' ? 'cancelled' : process.env.JOB_STATUS === 'success' ? 'success' : 'failure';
182187
try {

action.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ inputs:
7878
description: "Additional arguments to pass to cagent run"
7979
required: false
8080
default: ""
81+
trusted-bot-app-id:
82+
description: "GitHub App ID of a trusted bot that can bypass comment-based auth checks (e.g., for self-review triggers)"
83+
required: false
84+
default: ""
8185
add-prompt-files:
8286
description: "Comma-separated list of files to append to the prompt (e.g., 'AGENTS.md,CLAUDE.md')"
8387
required: false
@@ -190,10 +194,12 @@ runs:
190194
shell: bash
191195
env:
192196
ACTION_PATH: ${{ github.action_path }}
193-
# Get author_association from comment events (the main risk)
194-
COMMENT_ASSOCIATION: ${{ github.event.comment.author_association }}
197+
TRUSTED_BOT_APP_ID: ${{ inputs.trusted-bot-app-id }}
195198
DEBUG: ${{ inputs.debug }}
196199
run: |
200+
# Read comment fields directly from the event payload (cannot be overridden by workflow env vars)
201+
COMMENT_ASSOCIATION=$(jq -r '.comment.author_association // empty' "$GITHUB_EVENT_PATH")
202+
197203
# Only enforce auth for comment-triggered events
198204
# This prevents abuse via /commands while allowing PR-triggered workflows to run
199205
if [ -z "$COMMENT_ASSOCIATION" ]; then
@@ -202,6 +208,20 @@ runs:
202208
exit 0
203209
fi
204210
211+
# Allow a trusted GitHub App bot to bypass auth (e.g., auto-triage posts /review).
212+
# Verified via user type + app ID from the event payload to prevent spoofing.
213+
if [ -n "$TRUSTED_BOT_APP_ID" ]; then
214+
COMMENT_USER_TYPE=$(jq -r '.comment.user.type // empty' "$GITHUB_EVENT_PATH")
215+
COMMENT_APP_ID=$(jq -r '.comment.performed_via_github_app.id // empty' "$GITHUB_EVENT_PATH")
216+
217+
if [ "$COMMENT_USER_TYPE" = "Bot" ] && [ -n "$COMMENT_APP_ID" ] && [ "$COMMENT_APP_ID" = "$TRUSTED_BOT_APP_ID" ]; then
218+
COMMENT_USER_LOGIN=$(jq -r '.comment.user.login // empty' "$GITHUB_EVENT_PATH")
219+
echo "ℹ️ Skipping auth check (trusted bot: $COMMENT_USER_LOGIN, app_id: $COMMENT_APP_ID)"
220+
echo "authorized=bot" >> $GITHUB_OUTPUT
221+
exit 0
222+
fi
223+
fi
224+
205225
echo "Using comment author_association: $COMMENT_ASSOCIATION"
206226
207227
# Allowed roles (hardcoded for security - cannot be overridden)

review-pr/action.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ inputs:
5353
description: "Comma-separated list of files to append to the prompt (e.g., 'AGENTS.md,CLAUDE.md')"
5454
required: false
5555
default: ""
56+
trusted-bot-app-id:
57+
description: "GitHub App ID of a trusted bot that can bypass comment-based auth checks"
58+
required: false
59+
default: ""
5660

5761
outputs:
5862
exit-code:
@@ -473,6 +477,7 @@ runs:
473477
nebius-api-key: ${{ inputs.nebius-api-key }}
474478
mistral-api-key: ${{ inputs.mistral-api-key }}
475479
github-token: ${{ steps.resolve-token.outputs.token }}
480+
trusted-bot-app-id: ${{ inputs.trusted-bot-app-id }}
476481
extra-args: ${{ inputs.model && format('--model={0}', inputs.model) || '' }}
477482

478483
# ========================================
@@ -560,6 +565,7 @@ runs:
560565
nebius-api-key: ${{ inputs.nebius-api-key }}
561566
mistral-api-key: ${{ inputs.mistral-api-key }}
562567
github-token: ${{ steps.resolve-token.outputs.token }}
568+
trusted-bot-app-id: ${{ inputs.trusted-bot-app-id }}
563569
extra-args: ${{ inputs.model && format('--model={0}', inputs.model) || '' }}
564570
add-prompt-files: ${{ inputs.add-prompt-files }}
565571
max-retries: "0" # Disable retries — the review agent recovers internally (root falls back when sub-agents fail), so retrying the pipeline produces duplicate reviews

0 commit comments

Comments
 (0)