Skip to content
Merged
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
55 changes: 54 additions & 1 deletion .github/workflows/claude-blocking-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ on:
type: string
required: false
default: ''
max_turns:
description: 'Maximum Claude API turns (prevents runaway exploration). Default: 6.'
type: number
required: false
default: 6
timeout_minutes:
description: 'Hard timeout for the Claude review step in minutes. Default: 4.'
type: number
required: false
default: 4
model:
description: 'Claude model ID. Default: claude-sonnet-4-6.'
type: string
required: false
default: 'claude-sonnet-4-6'
secrets:
claude_oauth_token:
description: 'Claude Code OAuth token (CLAUDE_CODE_OAUTH_TOKEN secret)'
Expand All @@ -53,8 +68,39 @@ jobs:
with:
fetch-depth: 1

- name: Validate inputs
env:
MODEL: ${{ inputs.model }}
MAX_TURNS: ${{ inputs.max_turns }}
TIMEOUT: ${{ inputs.timeout_minutes }}
run: |
# Validate model: alphanumeric, dots, hyphens only
if ! echo "$MODEL" | grep -qE '^[a-zA-Z0-9._-]+$'; then
echo "::error::Invalid model name. Must match [a-zA-Z0-9._-]+"
exit 1
fi
# Validate max_turns: positive integer, capped at 20
if ! echo "$MAX_TURNS" | grep -qE '^[0-9]+$'; then
echo "::error::max_turns must be a positive integer"
exit 1
fi
if [ "$MAX_TURNS" -lt 1 ] || [ "$MAX_TURNS" -gt 20 ]; then
echo "::error::max_turns must be between 1 and 20"
exit 1
fi
# Validate timeout_minutes: between 1 and 15
if ! echo "$TIMEOUT" | grep -qE '^[0-9]+$'; then
echo "::error::timeout_minutes must be a positive integer"
exit 1
fi
if [ "$TIMEOUT" -lt 1 ] || [ "$TIMEOUT" -gt 15 ]; then
echo "::error::timeout_minutes must be between 1 and 15"
exit 1
fi

- name: Run Claude Code Review
id: claude-review
timeout-minutes: ${{ inputs.timeout_minutes }}
continue-on-error: true # infrastructure failure must not block merges
uses: anthropics/claude-code-action@v1
with:
Expand All @@ -63,6 +109,13 @@ jobs:
REPO: ${{ github.repository }}
PR NUMBER: ${{ inputs.pr_number }}

SCOPE CONSTRAINTS — follow these strictly:
- Read the PR diff using `gh pr diff`
- Read changed files for immediate context around modified lines
- Do NOT explore the broader codebase, run tests, or investigate unrelated files
- Focus your review on the diff — do not review unchanged code
- Complete your review in as few steps as possible

Please review this pull request and provide feedback on:
- Code quality and best practices
- Potential bugs or logic errors
Expand Down Expand Up @@ -145,7 +198,7 @@ jobs:
The verdict file (Step 4) is the primary signal read by CI. The verdict line
appended to the comment (Step 2) is the fallback. Both must match.

claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(echo *),Bash(cat *),Bash(tee *)"'
claude_args: '--max-turns ${{ inputs.max_turns }} --model ${{ inputs.model }} --allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(echo *),Bash(cat *),Bash(tee *)"'

- name: Check review verdict
if: always()
Expand Down
64 changes: 28 additions & 36 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
@@ -1,44 +1,36 @@
name: Claude Code Review

# Example caller workflow for claude-blocking-review.yml.
# Copy this file into your repository's .github/workflows/ directory,
# configure the secret, and update the org/owner below if needed.
#
# NOTE: The 'uses:' reference below points to smartwatermelon/github-workflows
# which is the canonical source. Update the org name if you forked this repo.
#
# Prerequisites:
# 1. CLAUDE_CODE_OAUTH_TOKEN secret available (repo or org level)
# 2. (Optional) Branch protection rule requiring status check:
# "Claude Code Review / claude-review / run-review"

permissions:
contents: read
pull-requests: write
issues: write
id-token: write

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:
# Optional: Filter by PR author
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
# github.event.pull_request.user.login == 'new-developer' ||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'

runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- 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'
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
# 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

uses: smartwatermelon/github-workflows/.github/workflows/claude-blocking-review.yml@v1
with:
pr_number: ${{ github.event.pull_request.number }}
# extra_instructions: |
# Repo-specific guidance here.
# max_turns: 6 # default: 6
# timeout_minutes: 4 # default: 4
# model: claude-sonnet-4-6 # default: claude-sonnet-4-6
secrets:
claude_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
Comment thread Fixed
Loading