rustwright-core 0.1.1

Rust CDP core for a Python Playwright-compatible automation API
name: Claude Code Review

# Automated PR review by Claude, on every opened/updated pull request.
# Requires a repository secret CLAUDE_CODE_OAUTH_TOKEN (create with `claude setup-token`).
# If the secret is not set, the review is skipped (the job still succeeds) instead of failing.
# The review prompt is loaded from the BASE branch so untrusted PRs cannot alter
# review behavior by editing the prompt file in the same PR.

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  claude-review:
    # Skip PRs opened by Claude itself or by GitHub Actions to avoid review loops.
    if: |
      !contains(fromJSON('["claude","claude[bot]","github-actions[bot]"]'), github.event.pull_request.user.login)
    runs-on: blacksmith-2vcpu-ubuntu-2404
    permissions:
      contents: read
      pull-requests: write
      issues: read
      id-token: write
    steps:
      - name: Check for Claude token
        id: gate
        env:
          CLAUDE_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
        run: |
          if [ -n "$CLAUDE_TOKEN" ]; then
            echo "enabled=true" >> "$GITHUB_OUTPUT"
          else
            echo "enabled=false" >> "$GITHUB_OUTPUT"
            echo "::notice::CLAUDE_CODE_OAUTH_TOKEN is not set — skipping Claude review. Add it with 'gh secret set CLAUDE_CODE_OAUTH_TOKEN'."
          fi

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

      - name: Delete previous Claude review comment
        if: steps.gate.outputs.enabled == 'true'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Remove any prior review comment (identified by the marker) so each
          # push replaces the review instead of stacking comments.
          gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \
            --jq '.[] | select(.body | contains("<!-- claude-code-review -->")) | .id' \
          | while read -r id; do
              if [ -n "$id" ]; then
                echo "Deleting previous Claude review comment: $id"
                gh api "repos/${{ github.repository }}/issues/comments/$id" -X DELETE
              fi
            done

      - name: Load review prompt from base branch
        if: steps.gate.outputs.enabled == 'true'
        id: prompt
        run: |
          # Load the prompt from the PR's BASE commit (trusted), not the PR head.
          git fetch --no-tags --depth=1 origin "${{ github.event.pull_request.base.sha }}"
          BASE="${{ github.event.pull_request.base.sha }}"
          PROMPT_PATH=".github/prompts/code-review.md"
          PROMPT_REF="$BASE"
          if ! git cat-file -e "${BASE}:${PROMPT_PATH}"; then
            # The repository bootstrap PR predates the prompt on main. Permit
            # only the exact reviewed prompt; later PRs always use the base copy.
            EXPECTED_SHA256="2f3f2cc67e26dabbc9752d879505b998c9f6131c69414492cf4cbb2fdf0b1297"
            ACTUAL_SHA256=$(git show "HEAD:${PROMPT_PATH}" | sha256sum | awk '{print $1}')
            if [ "$ACTUAL_SHA256" != "$EXPECTED_SHA256" ]; then
              echo "::error::Bootstrap review prompt hash mismatch"
              exit 1
            fi
            PROMPT_REF="HEAD"
          fi
          DELIM="PROMPT_EOF_$(openssl rand -hex 16)"
          {
            printf 'content<<%s\n' "$DELIM"
            git show "${PROMPT_REF}:${PROMPT_PATH}"
            printf '%s\n' "$DELIM"
          } >> "$GITHUB_OUTPUT"

      - name: Run Claude Code Review
        if: steps.gate.outputs.enabled == 'true'
        uses: anthropics/claude-code-action@ff9acae5886d41a99ed4ec14b7dc147d55834722 # v1.0.77
        with:
          claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
          allowed_bots: "dependabot[bot],github-actions[bot],renovate[bot]"
          prompt: |
            REPO: ${{ github.repository }}
            PR NUMBER: ${{ github.event.pull_request.number }}

            ${{ steps.prompt.outputs.content }}
          # Read-only review: Claude may inspect the repo and the PR, and post one comment.
          claude_args: >-
            --max-turns 30
            --allowed-tools "Read,Glob,Grep,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh issue view:*)"