1212 types : [created]
1313 pull_request :
1414 types : [ready_for_review, opened]
15+ workflow_run :
16+ workflows : ["PR Review Trigger"]
17+ types : [completed]
1518
1619permissions :
1720 contents : read
1821 pull-requests : write
1922 issues : write
2023 checks : write
24+ actions : read
2125
2226jobs :
2327 # ==========================================================================
2428 # AUTOMATIC REVIEW FOR ORG MEMBERS
2529 # Triggers when a PR is marked ready for review or opened (non-draft)
26- # Only runs for members of the docker org; auto-reviews same-repo PRs from org members
27- # Only runs for same-repo PRs (fork PRs don't have access to secrets with pull_request trigger)
28- # Fork PRs use the /review command instead
30+ # Supports two trigger paths:
31+ # 1. pull_request event (same-repo branches only — fork PRs lack secret access)
32+ # 2. workflow_run event (fork PRs via pr-review-trigger.yml → workflow_run pattern)
33+ # Only runs for members of the docker org.
2934 # ==========================================================================
3035 auto-review :
3136 if : |
32- github.event_name == 'pull_request' &&
33- !github.event.pull_request.draft &&
34- github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name
37+ (
38+ github.event_name == 'pull_request' &&
39+ !github.event.pull_request.draft &&
40+ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name
41+ ) || (
42+ github.event_name == 'workflow_run' &&
43+ github.event.workflow_run.name == 'PR Review Trigger' &&
44+ github.event.workflow_run.conclusion == 'success' &&
45+ github.event.workflow_run.head_repository.full_name != github.repository
46+ )
3547 runs-on : ubuntu-latest
3648 env :
3749 HAS_APP_SECRETS : ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }}
3850
3951 steps :
52+ # For workflow_run events (fork PRs), download the artifact saved by pr-review-trigger.yml
53+ # to get the PR number. For pull_request events, read it directly from the event payload.
54+ - name : Get PR number
55+ id : get-pr
56+ shell : bash
57+ env :
58+ GH_TOKEN : ${{ github.token }}
59+ EVENT_NAME : ${{ github.event_name }}
60+ EVENT_PR_NUMBER : ${{ github.event.pull_request.number }}
61+ WORKFLOW_RUN_ID : ${{ github.event.workflow_run.id }}
62+ REPO : ${{ github.repository }}
63+ run : |
64+ if [ "$EVENT_NAME" = "pull_request" ]; then
65+ echo "pr-number=$EVENT_PR_NUMBER" >> $GITHUB_OUTPUT
66+ exit 0
67+ fi
68+
69+ mkdir -p /tmp/trigger-metadata
70+
71+ if ! gh run download "$WORKFLOW_RUN_ID" -n "pr-review-trigger-metadata-$WORKFLOW_RUN_ID" -D /tmp/trigger-metadata --repo "$REPO" 2>/tmp/download_error.log; then
72+ if grep -qi "no artifact" /tmp/download_error.log; then
73+ echo "⏭️ No trigger metadata artifact found — skipping"
74+ else
75+ echo "::warning::Failed to download trigger metadata: $(cat /tmp/download_error.log)"
76+ fi
77+ echo "pr-number=" >> $GITHUB_OUTPUT
78+ exit 0
79+ fi
80+
81+ PR_NUMBER=$(cat /tmp/trigger-metadata/pr-number | tr -d '[:space:]')
82+ if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]] || [ "$PR_NUMBER" -lt 1 ]; then
83+ echo "::error::Invalid PR number: $PR_NUMBER"
84+ exit 1
85+ fi
86+
87+ echo "pr-number=$PR_NUMBER" >> $GITHUB_OUTPUT
88+ echo "✅ Got PR number from trigger metadata: #$PR_NUMBER"
89+
4090 - name : Check if PR author is org member
4191 id : membership
4292 uses : actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
4393 with :
4494 github-token : ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN }}
4595 script : |
4696 const org = 'docker';
47- const username = context.payload.pull_request.user.login;
97+
98+ // For workflow_run events, fetch PR author and draft status via API
99+ // since github.event.pull_request is not available in that context
100+ let username;
101+ if (context.eventName === 'workflow_run') {
102+ const prNumber = parseInt('${{ steps.get-pr.outputs.pr-number }}', 10);
103+ if (!prNumber) {
104+ core.setOutput('is_member', 'false');
105+ console.log('⏭️ No PR number — skipping auto-review');
106+ return;
107+ }
108+ const { data: pr } = await github.rest.pulls.get({
109+ owner: context.repo.owner,
110+ repo: context.repo.repo,
111+ pull_number: prNumber
112+ });
113+ if (pr.draft) {
114+ core.setOutput('is_member', 'false');
115+ console.log(`⏭️ PR #${prNumber} is a draft — skipping auto-review`);
116+ return;
117+ }
118+ username = pr.user.login;
119+ } else {
120+ username = context.payload.pull_request.user.login;
121+ }
48122
49123 try {
50124 await github.rest.orgs.checkMembershipForUser({
@@ -70,13 +144,12 @@ jobs:
70144 }
71145 }
72146
73- # With pull_request, checking out the PR head is standard behavior
74147 - name : Checkout PR head
75148 if : steps.membership.outputs.is_member == 'true'
76149 uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
77150 with :
78151 fetch-depth : 0
79- ref : refs/pull/${{ github.event.pull_request. number }}/head
152+ ref : refs/pull/${{ steps.get-pr.outputs.pr- number }}/head
80153
81154 # Generate GitHub App token for custom app identity (optional - falls back to github.token)
82155 - name : Generate GitHub App token
94167 continue-on-error : true # Don't fail the calling workflow if the review errors
95168 uses : ./review-pr
96169 with :
97- pr-number : ${{ github.event.pull_request. number }}
170+ pr-number : ${{ steps.get-pr.outputs.pr- number }}
98171 github-token : ${{ steps.app-token.outputs.token || github.token }}
99172 anthropic-api-key : ${{ secrets.ANTHROPIC_API_KEY }}
100173 openai-api-key : ${{ secrets.OPENAI_API_KEY }}
0 commit comments