-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
137 lines (129 loc) Β· 5.82 KB
/
Copy pathclaude-code-review-run.yml
File metadata and controls
137 lines (129 loc) Β· 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
name: Claude Code Review (run)
# Stage 2 (privileged): triggered when the "Claude Code Review" workflow above
# completes. workflow_run executes in the base-repo context, so it has access
# to secrets and id-token even for PRs from forks. This is what makes reviewing
# external contributors' PRs possible.
#
# Security: the triggering workflow file is fork-controlled, so the uploaded
# artifact is untrusted. We validate the PR number is purely numeric before
# using it, and we only check out the base ref (never the PR head into the
# workspace root) so untrusted code is never executed here. See
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
on:
workflow_run:
workflows: ["Claude Code Review"]
types: [completed]
jobs:
gate:
# Only react to PR runs that finished successfully.
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
outputs:
number: ${{ steps.read.outputs.number }}
found: ${{ steps.check.outputs.found }}
steps:
- name: Check for PR-number artifact
id: check
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7
with:
script: |
const { data } = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
core.setOutput('found', data.artifacts.some(a => a.name === 'pr-number') ? 'true' : 'false');
- name: Download PR number
if: steps.check.outputs.found == 'true'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v4
with:
name: pr-number
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Read and validate PR number
id: read
if: steps.check.outputs.found == 'true'
run: |
num="$(cat pr-number.txt)"
# Artifact comes from the fork-controlled trigger workflow β never trust it blindly.
if ! [[ "$num" =~ ^[0-9]+$ ]]; then
echo "Refusing to proceed: PR number is not numeric ($num)" >&2
exit 1
fi
echo "number=$num" >> "$GITHUB_OUTPUT"
claude-review:
needs: gate
if: needs.gate.outputs.found == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # post the review on the PR
issues: read
id-token: write
statuses: write # mirror the review result onto the PR head commit
concurrency:
group: claude-review-run-${{ needs.gate.outputs.number }}
cancel-in-progress: true
steps:
# workflow_run runs are detached and never show up in the PR's checks, so
# publish a commit status on the PR head SHA. This surfaces a "Claude Code
# Review" check next to the regular CI jobs.
- name: Report review pending
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.workflow_run.head_sha,
context: 'Claude Code Review',
state: 'pending',
description: 'Review in progress',
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
});
# Default checkout (base ref) only β do NOT check out the untrusted PR head.
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
with:
fetch-depth: 1
- name: Run Claude Code Review
id: review
continue-on-error: true # always report status below, even on failure
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Provided explicitly so the review can post on fork PRs; also required
# for allowed_non_write_users to take effect.
github_token: ${{ secrets.GITHUB_TOKEN }}
# Review PRs from external contributors who lack write access. The diff
# is fetched read-only and never executed; the subprocess env is scrubbed.
allowed_non_write_users: "*"
# review dependency bumps opened by Dependabot
allowed_bots: "dependabot[bot]"
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review ${{ github.repository }}/pull/${{ needs.gate.outputs.number }}"
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options
- name: Report review result
if: always()
env:
OUTCOME: ${{ steps.review.outcome }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7
with:
script: |
const ok = process.env.OUTCOME === 'success';
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.workflow_run.head_sha,
context: 'Claude Code Review',
state: ok ? 'success' : 'failure',
description: ok ? 'Review complete' : 'Review failed',
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
});
if (!ok) core.setFailed('Claude Code Review failed');