Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,35 @@ jobs:
permissions:
contents: read
pull-requests: write
id-token: write

id-token: write # Required by claude-code-action for GitHub App token exchange.
steps:
- name: Check workflow changes
id: workflow-changes
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
WORKFLOW_PATH: .github/workflows/claude-code-review.yml
run: |
set -euo pipefail
if gh pr diff "$PR_NUMBER" --repo "$REPOSITORY" --name-only | grep -Fx "$WORKFLOW_PATH"; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Skipping Claude review because this PR changes ${WORKFLOW_PATH}; Claude app token validation requires the workflow to match the default branch."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: Checkout repository
uses: actions/checkout@v7
if: steps.workflow-changes.outputs.skip != 'true'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 1
persist-credentials: false

- name: Run Claude Code Review
if: steps.workflow-changes.outputs.skip != 'true'
id: claude-review
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@4d7e1f0cd85743fdc93b1c8040ab54395da024e2 # v1.0.149
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
track_progress: true
Expand All @@ -53,5 +71,3 @@ jobs:

claude_args: |
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*)"


78 changes: 59 additions & 19 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,86 @@ on:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened]
pull_request_review:
types: [submitted]

jobs:
claude:
# Only run when @claude is mentioned AND the author is a trusted
# org member/collaborator. This is defense-in-depth on top of the
# action's built-in write-access check.
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
(
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body || '', '@claude'))
) &&
(
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR' ||
github.event.review.author_association == 'OWNER' ||
github.event.review.author_association == 'MEMBER' ||
github.event.review.author_association == 'COLLABORATOR'
)
runs-on: ubuntu-latest
permissions:
contents: write
contents: write # Downgrade to `read` if Claude should not push commits/branches
pull-requests: write
issues: write
id-token: write
actions: read # Required for Claude to read CI results on PRs
id-token: write # Required by claude-code-action for GitHub App token exchange.
steps:
# issue_comment payloads identify PR comments, but do not include the PR
# head repo. Query the PR before allowing Claude to run on fork PRs.
- name: Check PR origin
id: pr-origin
env:
GH_TOKEN: ${{ github.token }}
EVENT_NAME: ${{ github.event_name }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_PULL_REQUEST_URL: ${{ github.event.issue.pull_request.url }}
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "issue_comment" ] && [ -z "$ISSUE_PULL_REQUEST_URL" ]; then
echo "is_internal=true" >> "$GITHUB_OUTPUT"
exit 0
fi

pr_number="${PULL_REQUEST_NUMBER:-$ISSUE_NUMBER}"
if ! head_repo="$(gh api "repos/${REPOSITORY}/pulls/${pr_number}" --jq '.head.repo.full_name')"; then
echo "Unable to determine PR head repository for #${pr_number}; skipping Claude."
echo "is_internal=false" >> "$GITHUB_OUTPUT"
exit 0
fi

if [ "$head_repo" = "$REPOSITORY" ]; then
echo "is_internal=true" >> "$GITHUB_OUTPUT"
else
echo "is_internal=false" >> "$GITHUB_OUTPUT"
echo "Skipping Claude for external PR from ${head_repo:-unknown}."
fi

- name: Checkout repository
uses: actions/checkout@v7
if: steps.pr-origin.outputs.is_internal == 'true'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 1

- name: Run Claude Code
if: steps.pr-origin.outputs.is_internal == 'true'
id: claude
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@4d7e1f0cd85743fdc93b1c8040ab54395da024e2 # v1.0.149
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}

# This is an optional setting that allows Claude to read CI results on PRs
# Allows Claude to read CI results on PRs
additional_permissions: |
actions: read

# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'

# Optional: Add claude_args to customize behavior and configuration
# 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
# claude_args: '--allowed-tools Bash(gh pr *)'

# Tools are intentionally left at the action's tag-mode defaults: file edits
# are confined to the workspace (acceptEdits) and arbitrary Bash is blocked.
# Do NOT add Edit/Write to a custom --allowedTools — the action omits them on
# purpose; listing them grants blanket write access to the whole runner.