-
Notifications
You must be signed in to change notification settings - Fork 6
166 lines (141 loc) · 7.39 KB
/
Copy pathclaude-code-review.yml
File metadata and controls
166 lines (141 loc) · 7.39 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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# A cross-repo PR gets no secrets, so CLAUDE_CODE_OAUTH_TOKEN is empty there and
# the action can only fail. Skip instead, and let review-gate report that.
# Compare full_name rather than reading `head.repo.fork`: inside a fork of this
# repo its own PRs are not cross-repo and do have secrets.
claude-review:
if: >-
${{ !github.event.pull_request.draft &&
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'dependabot[bot]' }}
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: Clean up stale review comments
env:
GH_TOKEN: ${{ github.token }}
run: |
PR=${{ github.event.pull_request.number }}
REPO=${{ github.repository }}
# Dismiss all previous claude[bot] reviews
gh api "repos/${REPO}/pulls/${PR}/reviews" \
--paginate --jq '.[] | select(.user.login == "claude[bot]" and (.state == "CHANGES_REQUESTED" or .state == "COMMENTED")) | .id' \
| while read -r review_id; do
gh api -X PUT "repos/${REPO}/pulls/${PR}/reviews/${review_id}/dismissals" \
-f message="Superseded by new review" -f event="DISMISS" 2>/dev/null || true
done
# Delete all previous claude[bot] inline review comments
gh api "repos/${REPO}/pulls/${PR}/comments" \
--paginate --jq '.[] | select(.user.login == "claude[bot]") | .id' \
| while read -r comment_id; do
gh api -X DELETE "repos/${REPO}/pulls/comments/${comment_id}" 2>/dev/null || true
done
# Delete all previous claude[bot] issue comments (sticky comment is managed by the action)
gh api "repos/${REPO}/issues/${PR}/comments" \
--paginate --jq '.[] | select(.user.login == "claude[bot]") | .id' \
| while read -r comment_id; do
gh api -X DELETE "repos/${REPO}/issues/comments/${comment_id}" 2>/dev/null || true
done
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@be7b93b1907a4abad570368f3c74b6fe3807510b # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
use_sticky_comment: true
claude_args: "--allowedTools Bash,Read,Glob,Grep"
prompt: |
Review PR #${{ github.event.pull_request.number }} in ${{ github.repository }}.
Read `REVIEW.md` for the project context, review dimensions, severity scale, and
skip rules. Follow those guidelines strictly.
## CRITICAL SCOPE RULE
**ONLY review lines that were actually added or modified in this PR's diff.**
You MUST NOT comment on:
- Pre-existing code that was not changed in this PR
- Context lines shown in the diff that were not added/modified (lines without + prefix)
- Issues in unchanged files or unchanged sections of changed files
- Pre-existing patterns, naming, style, or technical debt in surrounding code
You may read full files for context to understand the changes, but every single
finding you report MUST point to a line that was added or modified in the diff.
If a line was not changed by this PR, it is OUT OF SCOPE, no matter how wrong it looks.
Before submitting each finding, verify: "Was this line actually changed in the PR diff?"
If the answer is no, drop the finding.
Skip dimensions that have no findings. Be precise and actionable — no "consider"
or "you might want to" comments.
## Output format
Structure your review as a single comment with sections for each dimension that has
findings. Use this severity scale:
- 🔴 **Critical** — wrong legal outcome, data loss, runtime crash, security vulnerability
- 🟠 **Significant** — likely bug, broken reference, missing edge case, lost jobs
- 🟡 **Minor** — code quality, style, non-blocking improvement
Also leave inline comments on specific lines where possible.
If there are no issues, say so briefly. Do not pad the review with praise or filler.
# The merge gate. Unlike claude-review this job has no `if:` — it must run and
# report on every PR, cross-repo and dependabot included, because a required
# check that never reports blocks such a PR forever.
review-gate:
name: Claude review completed
runs-on: ubuntu-latest
# Ruim boven de ~10 minuten die de review kost, zodat een trage run niet
# onterecht rood wordt; het script stopt zelf eerder met een duidelijke melding.
timeout-minutes: 40
permissions:
actions: read
contents: read
issues: read
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
# De poort mag niet draaien op de versie van zichzelf die in de PR staat:
# een PR die het script naar `exit 0` verandert zou anders per definitie
# groen zijn. Draai daarom de versie van de base-branch, en blokkeer als
# die niet te krijgen is — terugvallen op de PR-versie is precies de
# bypass die deze stap moet dichten.
- name: Take the gate script from the base branch
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
GATE: script/await-claude-review.sh
run: |
set -uo pipefail
fail() {
echo "::error title=Claude review gate::$1"
exit 1
}
git fetch --depth=1 origin "$BASE_SHA" ||
fail "base-commit ${BASE_SHA} is niet op te halen, dus de versie van de poort is niet vast te stellen. Draai deze job opnieuw."
git cat-file -e "${BASE_SHA}:${GATE}" 2>/dev/null ||
fail "${GATE} bestaat niet op de base-branch (${BASE_SHA}). De poort kan dan niet voor zichzelf instaan; deze PR moet met de hand gereviewd worden."
git checkout "$BASE_SHA" -- "$GATE" ||
fail "${GATE} is niet uit ${BASE_SHA} te checkouten, dus de poort zou de versie uit deze PR draaien. Draai deze job opnieuw."
echo "Poort draait de versie van ${BASE_SHA}."
- name: Wait for the Claude review of this commit
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
IS_CROSS_REPO: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
IS_DRAFT: ${{ github.event.pull_request.draft }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
JOB_NAME: claude-review
MAX_WAIT_SECONDS: '2100'
POLL_SECONDS: '20'
run: script/await-claude-review.sh