-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
148 lines (139 loc) Β· 6.54 KB
/
Copy pathclaude-code-review-run.yml
File metadata and controls
148 lines (139 loc) Β· 6.54 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
138
139
140
141
142
143
144
145
146
147
148
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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v4
with:
fetch-depth: 1
- name: Run Claude Code Review
id: review
continue-on-error: true # always report status below, even on failure
# pinned: v1 (Claude Code 2.1.216) fails every Bash call with
# "bwrap: Can't create file at /home/.mcp.json: Permission denied"
uses: anthropics/claude-code-action@a874e9ecd7bb36efdad65429c6b35815f5a08f10 # v1 @ 2.1.215
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"
# --comment lets the plugin post to the PR; without it the review only
# prints to the hidden CI log. By default the plugin also posts a "No
# issues found" summary on clean reviews - the override below drops that
# so a comment appears only when there are actual findings.
prompt: |
/code-review:code-review ${{ github.repository }}/pull/${{ needs.gate.outputs.number }} --comment
Only post a PR comment if you find issues. If the review is clean with
no findings, do not post any comment - skip the "No issues found"
summary and stop silently.
# 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');