chore(release): promote next to production (2025-12-23) #401
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Claude Code Review | |
| # Automated PR code reviews using Claude AI | |
| # | |
| # This workflow provides: | |
| # - Formal GitHub reviews (APPROVE/REQUEST_CHANGES/COMMENT) | |
| # - Inline comments on specific lines of code | |
| # - Auto-resolution of fixed issues on subsequent reviews | |
| # - Patch-ID based caching to skip rebases (no duplicate reviews) | |
| # - Iterative review tracking of previous comments | |
| # | |
| # TRIGGERS: | |
| # - Automatic: On PR open, synchronize (push), or ready_for_review | |
| # - Comment: Add a comment containing "@request-claude-review" to trigger a fresh review | |
| # - Manual: Via workflow_dispatch with PR number and optional force_review flag | |
| # | |
| # TRIGGERING A NEW REVIEW WITHOUT CODE CHANGES: | |
| # The easiest way is to add a comment on the PR containing "@request-claude-review". | |
| # This bypasses the cache and forces a fresh review. | |
| # | |
| # Alternatively, use the manual workflow_dispatch trigger via the Actions tab or CLI: | |
| # gh workflow run "Claude Code Review" -f pr_number=123 | |
| # | |
| # NOTE: Re-requesting a review from github-actions[bot] via the GitHub UI | |
| # does NOT work. GitHub does not fire events when re-requesting reviews | |
| # from bot accounts. Use the comment trigger or manual dispatch instead. | |
| # | |
| # The review uses the default prompt from this repository's | |
| # .github/prompts/default-pr-review.md | |
| on: | |
| pull_request: | |
| types: | |
| - opened | |
| - synchronize | |
| - ready_for_review | |
| # Comment trigger: Add "@request-claude-review" to any PR comment to force a fresh review | |
| # Works with both regular PR comments and inline review comments | |
| issue_comment: | |
| types: [created] | |
| # Pull request review comments (inline comments on diffs) | |
| pull_request_review_comment: | |
| types: [created] | |
| # Manual trigger for forcing a new review without code changes | |
| workflow_dispatch: | |
| inputs: | |
| pr_number: | |
| description: "Pull request number to review" | |
| required: true | |
| type: string | |
| force_review: | |
| description: "Force a full review even if the code hasn't changed (bypasses cache)" | |
| required: false | |
| type: boolean | |
| default: true | |
| # Ensure only one review runs at a time per PR | |
| # New reviews wait for the current one to complete before starting | |
| concurrency: | |
| group: claude-review-${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }} | |
| cancel-in-progress: false | |
| jobs: | |
| # Fetch PR details for manual triggers (workflow_dispatch) and comment triggers | |
| # This job retrieves base_ref which isn't available in these event contexts | |
| get-pr-info: | |
| if: github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| base_ref: ${{ steps.pr-info.outputs.base_ref }} | |
| head_ref: ${{ steps.pr-info.outputs.head_ref }} | |
| pr_number: ${{ steps.pr-info.outputs.pr_number }} | |
| should_run: ${{ steps.pr-info.outputs.should_run }} | |
| steps: | |
| - name: Get PR Info | |
| id: pr-info | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| INPUT_PR_NUMBER: ${{ inputs.pr_number }} | |
| ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| PR_NUMBER_FROM_REVIEW: ${{ github.event.pull_request.number }} | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| COMMENT_AUTHOR: ${{ github.event.comment.user.login }} | |
| IS_PR: ${{ github.event.issue.pull_request && 'true' || 'false' }} | |
| run: | | |
| # Determine PR number based on event type | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| PR_NUMBER="$INPUT_PR_NUMBER" | |
| SHOULD_RUN="true" | |
| elif [ "$EVENT_NAME" = "pull_request_review_comment" ]; then | |
| PR_NUMBER="$PR_NUMBER_FROM_REVIEW" | |
| # Ignore comments from github-actions[bot] to prevent self-triggering | |
| if [ "$COMMENT_AUTHOR" = "github-actions[bot]" ]; then | |
| echo "ℹ️ Ignoring comment from github-actions[bot] to prevent self-triggering" | |
| echo "should_run=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Check if comment contains the trigger phrase | |
| if echo "$COMMENT_BODY" | grep -qi "@request-claude-review"; then | |
| SHOULD_RUN="true" | |
| echo "🔍 Found @request-claude-review in review comment" | |
| else | |
| SHOULD_RUN="false" | |
| echo "ℹ️ Comment does not contain @request-claude-review, skipping" | |
| fi | |
| elif [ "$EVENT_NAME" = "issue_comment" ]; then | |
| # issue_comment fires for both issues and PRs | |
| # Ignore comments from github-actions[bot] to prevent self-triggering | |
| if [ "$COMMENT_AUTHOR" = "github-actions[bot]" ]; then | |
| echo "ℹ️ Ignoring comment from github-actions[bot] to prevent self-triggering" | |
| echo "should_run=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Check if this is a PR (has pull_request field) | |
| if [ "$IS_PR" != "true" ]; then | |
| echo "ℹ️ Comment is on an issue, not a PR. Skipping." | |
| echo "should_run=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| PR_NUMBER="$ISSUE_NUMBER" | |
| # Check if comment contains the trigger phrase | |
| if echo "$COMMENT_BODY" | grep -qi "@request-claude-review"; then | |
| SHOULD_RUN="true" | |
| echo "🔍 Found @request-claude-review in PR comment" | |
| else | |
| SHOULD_RUN="false" | |
| echo "ℹ️ Comment does not contain @request-claude-review, skipping" | |
| fi | |
| fi | |
| echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT | |
| echo "should_run=$SHOULD_RUN" >> $GITHUB_OUTPUT | |
| if [ "$SHOULD_RUN" != "true" ]; then | |
| echo "ℹ️ Skipping - trigger condition not met" | |
| exit 0 | |
| fi | |
| echo "ℹ️ Fetching PR #$PR_NUMBER info..." | |
| PR_DATA=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER) | |
| BASE_REF=$(echo "$PR_DATA" | jq -r '.base.ref') | |
| HEAD_REF=$(echo "$PR_DATA" | jq -r '.head.ref') | |
| echo "base_ref=$BASE_REF" >> $GITHUB_OUTPUT | |
| echo "head_ref=$HEAD_REF" >> $GITHUB_OUTPUT | |
| echo "✅ PR #$PR_NUMBER: base=$BASE_REF, head=$HEAD_REF" | |
| # Automatic review triggered by pull_request events (opened, synchronize, ready_for_review) | |
| claude-review-auto: | |
| if: | | |
| github.event_name == 'pull_request' && | |
| !contains(github.head_ref, 'gh-readonly-queue/') && | |
| !startsWith(github.head_ref, 'release/next-to-main-') && | |
| ( | |
| github.event.pull_request.draft == false || | |
| github.event.action == 'ready_for_review' || | |
| github.event.pull_request.user.login == 'claude[bot]' | |
| ) | |
| uses: ./.github/workflows/_claude-code-review.yml | |
| with: | |
| pr_number: ${{ github.event.pull_request.number }} | |
| base_ref: ${{ github.base_ref }} | |
| force_review: false | |
| # Use Opus 4.5 for comprehensive reviews with reliable inline comments | |
| # Opus is more capable at following complex multi-step instructions | |
| # Uncomment one of these to use a different model for reviews: | |
| # model: 'claude-haiku-4-5' | |
| # model: 'claude-sonnet-4-5' | |
| model: "claude-opus-4-5" | |
| # Allow standard review conversation turns | |
| # max_turns: 15 | |
| # Standard timeout for most PRs | |
| timeout_minutes: 20 | |
| # Optional: Restrict tools for read-only reviews (uncomment to enable) | |
| # allowed_tools: 'Read,Grep,Glob,Bash(git log),Bash(git diff),Bash(git show)' | |
| secrets: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} | |
| # Manual review triggered by workflow_dispatch | |
| # This runs after get-pr-info to have access to PR metadata | |
| claude-review-manual: | |
| needs: get-pr-info | |
| if: | | |
| github.event_name == 'workflow_dispatch' && | |
| needs.get-pr-info.outputs.should_run == 'true' && | |
| !contains(needs.get-pr-info.outputs.head_ref, 'gh-readonly-queue/') && | |
| !startsWith(needs.get-pr-info.outputs.head_ref, 'release/next-to-main-') | |
| uses: ./.github/workflows/_claude-code-review.yml | |
| with: | |
| pr_number: ${{ needs.get-pr-info.outputs.pr_number }} | |
| base_ref: ${{ needs.get-pr-info.outputs.base_ref }} | |
| force_review: ${{ inputs.force_review }} | |
| # Use Opus 4.5 for comprehensive reviews with reliable inline comments | |
| model: "claude-opus-4-5" | |
| # Standard timeout for most PRs | |
| timeout_minutes: 20 | |
| secrets: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} | |
| # Comment-triggered review: Add "@request-claude-review" to any PR comment | |
| # This forces a fresh review, bypassing the cache | |
| claude-review-comment: | |
| needs: get-pr-info | |
| if: | | |
| (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && | |
| needs.get-pr-info.outputs.should_run == 'true' && | |
| !contains(needs.get-pr-info.outputs.head_ref, 'gh-readonly-queue/') && | |
| !startsWith(needs.get-pr-info.outputs.head_ref, 'release/next-to-main-') | |
| uses: ./.github/workflows/_claude-code-review.yml | |
| with: | |
| pr_number: ${{ needs.get-pr-info.outputs.pr_number }} | |
| base_ref: ${{ needs.get-pr-info.outputs.base_ref }} | |
| force_review: true # Always force when triggered by comment | |
| # Use Opus 4.5 for comprehensive reviews with reliable inline comments | |
| model: "claude-opus-4-5" | |
| # Standard timeout for most PRs | |
| timeout_minutes: 20 | |
| secrets: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| WORKFLOW_PAT: ${{ secrets.WORKFLOW_PAT }} | |
| # Auto-merge Dependabot security updates after successful review | |
| # Only runs for pull_request events (dependabot only triggers pull_request) | |
| auto-merge-dependabot: | |
| needs: claude-review-auto | |
| if: | | |
| github.event_name == 'pull_request' && | |
| github.actor == 'dependabot[bot]' && | |
| (contains(github.event.pull_request.title, 'security') || contains(github.event.pull_request.title, 'Bump')) && | |
| needs.claude-review-auto.result == 'success' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Enable auto-merge for Dependabot updates | |
| run: gh pr merge --auto --squash "$PR_NUMBER" | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} |