ci: add Claude Code on-demand workflow for PRs#818
Conversation
…tance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new GitHub Actions workflow Changes
Sequence Diagram(s)sequenceDiagram
participant Commenter
participant GitHub as "GitHub Events"
participant Actions as "GitHub Actions (claude)"
participant Claude as "Anthropic Claude API"
participant Repo as "Repository"
Commenter->>GitHub: Post comment / review (may mention `@claude`)
GitHub->>Actions: Trigger workflow (issue_comment / pr events)
Actions->>Actions: Evaluate conditions (author association, contains "@claude", PR context, resolve head SHA)
Actions->>Repo: Checkout repo at resolved ref
Actions->>Claude: Invoke anthropics/claude-code-action (with CLAUDE token + claude_args)
Claude-->>Actions: Return analysis / actions
Actions->>Repo: Post comments or run allowed gh/pr commands per response
Actions-->>GitHub: Update workflow status
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the dual-job setup (deprecated security-review action + on-demand) with a single job using claude-code-action@v1, matching the official docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop unused permissions (issues, id-token) and add author_association guard so only OWNER/MEMBER/COLLABORATOR can trigger Claude. Also filter out standalone issue comments to limit usage to PRs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Downgrade contents permission from write to read and whitelist only review-related tools (Read, Glob, Grep, inline comments, PR comments) so Claude cannot modify code or push changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @.github/workflows/claude.yml:
- Around line 22-23: The checkout step currently uses actions/checkout@v4
without an explicit ref so the workflow reads the default branch; update the
checkout step (actions/checkout@v4) to pass the PR head SHA as ref by resolving
it from the event payload (e.g. set ref: ${{ github.event.pull_request.head.sha
|| github.event.issue.pull_request.head.sha }} or fall back to github.sha) and
include fetch-depth: 0 so the PR commit is fetched; this ensures Claude reads
the actual PR branch instead of the default branch.
🧹 Nitpick comments (1)
.github/workflows/claude.yml (1)
25-29: Pin the third-party action to a full commit SHA instead of a mutable tag.
anthropics/claude-code-action@v1uses a mutable tag that could be moved or force-pushed to point to different code. Since this action receivessecrets.ANTHROPIC_API_KEY, pinning to a specific commit SHA is a security best practice that mitigates supply chain risk. Include the version as a comment for clarity during future upgrades.Example
- - uses: anthropics/claude-code-action@v1 + - uses: anthropics/claude-code-action@<full-commit-sha> # v1.x.x
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
.github/workflows/claude.yml (1)
3-13: Trigger scope is broader than the stated PR-only objective.The PR title/description frames this as a "PR workflow," but the
issuestrigger (lines 10–11) means@claudecan also be invoked on standalone issues. If this is intentional, no action needed — just noting the discrepancy with the PR description.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/claude.yml around lines 3 - 13, The workflow's trigger scope includes the 'issues' event but the PR says it should be a PR-only workflow; remove or restrict the 'issues' trigger under the top-level on: block (the 'issues:' entry and its types) so only pull_request-related events (e.g., the existing 'pull_request', 'pull_request_review', 'pull_request_review_comment', and 'issue_comment' if intended) remain; update or delete the 'issues' stanza to align triggers with the stated PR-only objective.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/claude.yml:
- Line 1: Run Prettier to fix the formatting violation in the GitHub Actions
workflow that declares "name: Claude Code": run the project's Prettier formatter
(e.g., bun run prettier --write .) to reformat the workflow file, verify the
resulting changes include the corrected formatting for the "name: Claude Code"
entry, and commit the formatted file(s) to unblock CI.
- Around line 43-44: The workflow's --allowedTools entry currently permits broad
shell patterns Bash(gh api:*) and Bash(git:*) which, combined with
pull-requests: write and issues: write, allow destructive write operations;
change the --allowedTools string to restrict these to read-only operations
(e.g., replace Bash(gh api:*) with explicit read-only gh calls or GET-only
patterns like Bash(gh api:GET * ) and replace Bash(git:*) with specific safe git
commands (e.g., Bash(git:status), Bash(git:log) or a whitelist of read-only
subcommands) or remove them and use specific gh pr/gh issue read subcommands
instead so the --allowedTools flag no longer enables arbitrary gh api or git
write actions.
---
Nitpick comments:
In @.github/workflows/claude.yml:
- Around line 3-13: The workflow's trigger scope includes the 'issues' event but
the PR says it should be a PR-only workflow; remove or restrict the 'issues'
trigger under the top-level on: block (the 'issues:' entry and its types) so
only pull_request-related events (e.g., the existing 'pull_request',
'pull_request_review', 'pull_request_review_comment', and 'issue_comment' if
intended) remain; update or delete the 'issues' stanza to align triggers with
the stated PR-only objective.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Restrict --allowedTools to read-only git subcommands and remove broad gh api:* - Add author_association check to limit triggers to OWNER/MEMBER/COLLABORATOR - Fix issue_comment checkout to use PR head SHA instead of default branch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
.github/workflows/claude.yml (1)
46-48: ModernizeBash()allowedTools patterns to use space separator instead of deprecated colon syntax.The colon syntax (e.g.,
Bash(gh pr comment:*)) is deprecated but still functional and equivalent to the space-based format (e.g.,Bash(gh pr comment *)). Update to the current recommended syntax for consistency with Claude Code documentation:Proposed modernization
- --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(git diff:*),Bash(git log:*),Bash(git blame:*),Bash(git show:*),Bash(git status:*),Read,Glob,Grep,WebFetch,WebSearch" + --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment *),Bash(gh pr diff *),Bash(gh pr view *),Bash(git diff *),Bash(git log *),Bash(git blame *),Bash(git show *),Bash(git status *),Read,Glob,Grep,WebFetch,WebSearch"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/claude.yml around lines 46 - 48, Update the allowedTools string in the claude_args block to use the modern space-separated Bash(...) pattern instead of the deprecated colon syntax: in the allowedTools value inside claude_args replace occurrences like Bash(gh pr comment:*), Bash(gh pr diff:*), Bash(gh pr view:*), Bash(git diff:*), Bash(git log:*), Bash(git blame:*), Bash(git show:*), Bash(git status:*) with Bash(gh pr comment *), Bash(gh pr diff *), Bash(gh pr view *), Bash(git diff *), Bash(git log *), Bash(git blame *), Bash(git show *), Bash(git status *) respectively, keeping the rest of the allowedTools entries (Read,Glob,Grep,WebFetch,WebSearch) unchanged so claude_args and allowedTools formatting and semantics remain identical but use the current recommended syntax.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/claude.yml:
- Around line 13-16: The issue_comment branch in the workflow if-condition
currently triggers for any issue comment; update the issue_comment clause to
require the event is a PR by adding a PR-only guard that checks for
github.event.issue.pull_request (e.g., require the pull_request field exists) so
the group handling issue_comment (the first OR clause in the if expression) only
proceeds for comments on pull requests and will not run for standalone issues.
- Around line 12-17: The claude job lacks a timeout setting which can allow
runaway workflows to consume runner minutes; add a timeout-minutes property to
the claude job definition (the job named "claude") with a reasonable value
(e.g., 10 or 30) to cap execution time and prevent long-running or stuck
LLM-driven reviews from running indefinitely; update the job block to include
timeout-minutes: <minutes> so the workflow respects the new limit.
- Around line 18-23: Remove the unnecessary "issues: write" permission from the
workflow permissions block and keep "pull-requests: write" (and other existing
needed entries like "contents: read", "id-token: write", "actions: read") so the
workflow retains least privilege while still allowing gh pr comment and inline
review operations; update the permissions mapping by deleting the "issues:
write" line and leave the rest unchanged.
- Around line 25-37: The workflow only resolves the PR head SHA for
issue_comment events (step id pr-sha) so checkout uses github.sha (the merge
ref) for pull_request_review_comment and pull_request_review events; update the
workflow so the PR head SHA is used for those events by either expanding the
pr-sha step to run for pull_request_review and pull_request_review_comment (and
fetch the head SHA from github.event.pull_request.head.sha) or change the
checkout ref expression to prefer: steps.pr-sha.outputs.sha ||
github.event.pull_request.head.sha || github.sha, ensuring the Checkout
repository step (uses: actions/checkout@v4) receives the actual PR head commit
when available.
---
Nitpick comments:
In @.github/workflows/claude.yml:
- Around line 46-48: Update the allowedTools string in the claude_args block to
use the modern space-separated Bash(...) pattern instead of the deprecated colon
syntax: in the allowedTools value inside claude_args replace occurrences like
Bash(gh pr comment:*), Bash(gh pr diff:*), Bash(gh pr view:*), Bash(git diff:*),
Bash(git log:*), Bash(git blame:*), Bash(git show:*), Bash(git status:*) with
Bash(gh pr comment *), Bash(gh pr diff *), Bash(gh pr view *), Bash(git diff *),
Bash(git log *), Bash(git blame *), Bash(git show *), Bash(git status *)
respectively, keeping the rest of the allowedTools entries
(Read,Glob,Grep,WebFetch,WebSearch) unchanged so claude_args and allowedTools
formatting and semantics remain identical but use the current recommended
syntax.
| claude: | ||
| if: | | ||
| (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || | ||
| (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || | ||
| (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.review.author_association)) | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
Add timeout-minutes to prevent runaway jobs.
The job has no timeout-minutes. GitHub Actions defaults to 360 minutes (6 hours). An LLM-driven review that gets stuck or loops could burn significant runner minutes. Add a reasonable timeout.
Proposed fix
claude:
if: |
...
runs-on: ubuntu-latest
+ timeout-minutes: 30
permissions:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/claude.yml around lines 12 - 17, The claude job lacks a
timeout setting which can allow runaway workflows to consume runner minutes; add
a timeout-minutes property to the claude job definition (the job named "claude")
with a reasonable value (e.g., 10 or 30) to cap execution time and prevent
long-running or stuck LLM-driven reviews from running indefinitely; update the
job block to include timeout-minutes: <minutes> so the workflow respects the new
limit.
| if: | | ||
| (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || | ||
| (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || | ||
| (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.review.author_association)) |
There was a problem hiding this comment.
Missing PR-only guard for issue_comment events.
The issue_comment event fires for both PR comments and standalone issue comments. The guard on line 14 doesn't check github.event.issue.pull_request, so an @claude comment on a regular issue will start the job, then fail at the "Resolve PR head SHA" step when gh api .../pulls/{issue_number} returns a 404.
Add the standard PR-only filter:
Proposed fix
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') &&
+ github.event.issue.pull_request &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/claude.yml around lines 13 - 16, The issue_comment branch
in the workflow if-condition currently triggers for any issue comment; update
the issue_comment clause to require the event is a PR by adding a PR-only guard
that checks for github.event.issue.pull_request (e.g., require the pull_request
field exists) so the group handling issue_comment (the first OR clause in the if
expression) only proceeds for comments on pull requests and will not run for
standalone issues.
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| issues: write | ||
| id-token: write | ||
| actions: read |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the full workflow file to understand what commands are used
cat -n .github/workflows/claude.ymlRepository: wormhole-foundation/native-token-transfers
Length of output: 8043
🏁 Script executed:
# Search for any documentation or PR objectives related to this workflow
git log --oneline --all -- .github/workflows/claude.yml | head -5Repository: wormhole-foundation/native-token-transfers
Length of output: 145
🏁 Script executed:
# Check if other workflows use issues: write and for what purpose
rg -B 3 -A 3 'issues:\s*write' .github/workflows/Repository: wormhole-foundation/native-token-transfers
Length of output: 426
Remove issues: write permission — PR comments only require pull-requests: write.
The workflow only posts comments on pull requests via gh pr comment and inline review comments. The pull-requests: write permission is sufficient for both operations. The issues: write permission grants unnecessary write access to issues and violates the principle of least privilege.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/claude.yml around lines 18 - 23, Remove the unnecessary
"issues: write" permission from the workflow permissions block and keep
"pull-requests: write" (and other existing needed entries like "contents: read",
"id-token: write", "actions: read") so the workflow retains least privilege
while still allowing gh pr comment and inline review operations; update the
permissions mapping by deleting the "issues: write" line and leave the rest
unchanged.
| - name: Resolve PR head SHA for issue_comment events | ||
| id: pr-sha | ||
| if: github.event_name == 'issue_comment' | ||
| run: | | ||
| sha=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} --jq '.head.sha') | ||
| echo "sha=$sha" >> $GITHUB_OUTPUT | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
|
|
||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event_name == 'issue_comment' && steps.pr-sha.outputs.sha || github.sha }} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
GitHub Actions github.sha value pull_request_review_comment pull_request_review events
💡 Result:
For workflows triggered by pull_request_review or pull_request_review_comment, ${{ github.sha }} (aka GITHUB_SHA) is not the PR’s head commit.
- For both events,
github.sha/GITHUB_SHA= the “last merge commit” for the PR’s merge ref (the synthetic merge commit GitHub creates for the PR), andgithub.ref=refs/pull/<number>/merge. [1] - If you need the PR’s actual branch (head) commit SHA, use the event payload instead, e.g.
${{ github.event.pull_request.head.sha }}(available because these events include the PR object in the webhook payload). [1] - Related: this “merge ref” execution context is also why some policy checks for PR-family events evaluate against
refs/pull/<number>/merge(as opposed topull_request_target, which runs in the base repo context). [2]
Sources: GitHub “Events that trigger workflows” docs (tables for pull_request_review and pull_request_review_comment). [1] GitHub Changelog note about PR-family events using refs/pull/<number>/merge. [2]
🌐 Web query:
GitHub Actions github.event.pull_request context pull_request_review_comment pull_request_review
💡 Result:
In GitHub Actions, github.event is the full webhook payload for the event that triggered the workflow. For both pull_request_review and pull_request_review_comment, the payload includes a top-level pull_request object, so github.event.pull_request is the right place to read PR metadata (number, head/base, etc.). [1][2]
pull_request_review (a review submission/edit/dismiss)
- Trigger:
on: pull_request_review(submitted,edited,dismissed). [3] - Key payload objects:
github.event.pull_request(the PR)github.event.review(the review, including its state likeapproved,changes_requested, etc.) [3][1]
pull_request_review_comment (a diff/line comment)
- Trigger:
on: pull_request_review_comment(created,edited,deleted). [3] - Key payload objects:
github.event.pull_request(the PR)github.event.comment(the diff comment) [2][1]
Common gotcha: which commit SHA you’re on
For these PR-family events, GITHUB_SHA is the PR merge ref commit; if you want the PR’s head commit, use:
github.event.pull_request.head.sha[3]
Sources: GitHub webhook payload docs for pull_request_review / pull_request_review_comment [1][2] and GitHub Actions “events that trigger workflows” (notes on these events + head.sha) [3].
PR head SHA is not resolved for pull_request_review_comment and pull_request_review events — Claude will analyze the wrong code.
The SHA resolution step (lines 25-32) only runs for issue_comment events. For pull_request_review_comment and pull_request_review events, the checkout falls back to github.sha, which points to the PR merge ref (synthetic merge commit), not the PR head. Claude will then read code from the wrong commit.
Both events populate github.event.pull_request.head.sha in their webhook payload, so extend the ref selection to include it:
Proposed fix
- name: Checkout repository
uses: actions/checkout@v4
with:
- ref: ${{ github.event_name == 'issue_comment' && steps.pr-sha.outputs.sha || github.sha }}
+ ref: ${{ github.event_name == 'issue_comment' && steps.pr-sha.outputs.sha || github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0The final github.sha fallback handles non-PR events.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/claude.yml around lines 25 - 37, The workflow only
resolves the PR head SHA for issue_comment events (step id pr-sha) so checkout
uses github.sha (the merge ref) for pull_request_review_comment and
pull_request_review events; update the workflow so the PR head SHA is used for
those events by either expanding the pr-sha step to run for pull_request_review
and pull_request_review_comment (and fetch the head SHA from
github.event.pull_request.head.sha) or change the checkout ref expression to
prefer: steps.pr-sha.outputs.sha || github.event.pull_request.head.sha ||
github.sha, ensuring the Checkout repository step (uses: actions/checkout@v4)
receives the actual PR head commit when available.
johnsaigle
left a comment
There was a problem hiding this comment.
Do you mind updating this PR with the suggestions from the monorepo PR?
wormhole-foundation/wormhole#4686
Pin action versions to specific SHAs, add persist-credentials: false, and align system prompt with wormhole repo (security-focused review). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Hub OIDC Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove id-token: write to verify it's not needed with API key auth - Add pull_request trigger temporarily for testing
Summary
Adds a GitHub Actions workflow that lets repo collaborators invoke Claude Code on pull requests by commenting
@claude. Claude operates in read-only review mode — it can read code and post comments but cannot modify files or push changes.How it works
The workflow triggers on PR comments (both regular and inline review comments). Three guard conditions must all pass before a runner spins up:
OWNER,MEMBER, orCOLLABORATORcan trigger Claude, preventing abuse from forks or outsiders.@claude. This is an optimization to avoid wasting runner minutes, since the action also filters internally.issue_commentfor both issues and PRs. This condition ensures standalone issue comments are ignored while still catching regular PR comments.Permissions
Locked down for a production repository:
contents: read— read-only access to code (no push/commit)pull-requests: write— only so Claude can post review commentsRead,Glob,Grep, inline comments, andgh prread commands. All other tools (Write, Edit, Bash, git push, etc.) are blocked.Authentication
Uses an Anthropic API key (
ANTHROPIC_API_KEYsecret) for authentication.Current test
Testing removal of
id-token: writepermission.@claude please review