Repository navigation
104 lines (98 loc) · 5.78 KB
/
Copy pathclaude-code-review.yml
File metadata and controls
104 lines (98 loc) · 5.78 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
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"
jobs:
claude-review:
# Skip PRs we shouldn't (or can't) review:
# - Dependabot / forks: no CLAUDE_CODE_OAUTH_TOKEN, so the action would fail.
# - release-please release PRs: just version bumps + changelog — nothing to
# review, and it must never block a release.
# - Back-merge PRs (production -> dev, opened by release-please.yml): only
# carry code already reviewed on its way through dev, and the action
# rejects their bot actor ("Workflow initiated by non-human actor"),
# which blocks the merge since claude-review is a required check.
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && !startsWith(github.head_ref, 'release-please--') && !startsWith(github.head_ref, 'chore/back-merge-') }}
runs-on: ubuntu-latest
permissions:
contents: read
# write so the review agent can post its inline comments — with read it
# reviews successfully but every posting attempt is denied and the PR
# gets nothing (the action still exits 0).
pull-requests: write
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1
- name: Record start time
run: echo "REVIEW_STARTED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins'
# --comment is required for the review to be posted to the PR: the
# code-review plugin's spec says "If --comment was NOT provided,
# stop here. Do not post any GitHub comments." Without it the review
# runs and is discarded (the log doesn't even show it unless
# show_full_output is enabled).
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }} --comment'
show_full_output: true
# Explicitly allow the posting and reading tools. In headless mode the
# plugin's allowed-tools frontmatter doesn't reach them: PR #9's review
# hit 46 permission denials and posted nothing despite finding issues.
#
# The appended prompt keeps the review in the foreground. Headless runs
# often backgrounded the reviewer subagents and ended the turn to
# "wait" for them, which ends the session: PR #223's review stopped
# after 10 turns and posted nothing. Same fix as dcd's workflow.
claude_args: '--model claude-opus-5-5 --allowedTools "mcp__github_comment__update_claude_comment,mcp__github_inline_comment__create_inline_comment,Read,Glob,Grep,WebFetch,Bash(gh:*),Bash(git diff:*),Bash(git log:*),Bash(git show:*)" --append-system-prompt "You are running non-interactively. Never run subagents in the background and never schedule wakeups: ending your turn ends the session. Wait for every reviewer to finish, then post the review comment before you stop. If you stop early because the PR is trivial or needs no review, still post one short PR comment that says so and why. Do not post anything if you stop because you already commented on this PR."'
# 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
# The action exits green even when Claude ends its turn before posting
# anything. Fail loudly instead of leaving a silent green check.
#
# The code-review plugin also skips, on purpose, any PR Claude has already
# commented on. So a push after the first review posts nothing, and that
# is not a failure. Only fail when Claude has never posted on the PR.
#
# The plugin also stops early on PRs it judges trivial. The prompt above
# tells Claude to post a one-line note in that case, so a deliberate skip
# leaves a comment and only a real failure trips this check.
- name: Check a review comment was posted
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
run: |
# Prints the time of every claude[bot] comment, inline comment and review.
claude_posts() {
gh api "repos/$REPO/issues/$PR/comments" --paginate \
--jq '.[] | select(.user.login == "claude[bot]") | .created_at'
gh api "repos/$REPO/pulls/$PR/comments" --paginate \
--jq '.[] | select(.user.login == "claude[bot]") | .created_at'
gh api "repos/$REPO/pulls/$PR/reviews" --paginate \
--jq '.[] | select(.user.login == "claude[bot]") | .submitted_at'
}
posts=$(claude_posts)
new=$(printf '%s\n' "$posts" | awk -v since="$REVIEW_STARTED_AT" 'NF && $0 >= since' | wc -l | tr -d ' ')
total=$(printf '%s\n' "$posts" | awk 'NF' | wc -l | tr -d ' ')
if [ "$new" -gt 0 ]; then
echo "Claude posted $new comment(s)."
elif [ "$total" -gt 0 ]; then
echo "::notice::No new comment. Claude already reviewed this PR, and the code-review plugin reviews each PR once."
else
echo "::error::Claude finished without posting a review comment on this PR."
exit 1
fi