Claude Code Review (run) #343
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 (run) | |
| # Stage 2 (privileged): triggered when the "Claude Code Review" workflow above | |
| # completes. workflow_run executes in the base-repo context, so it has access | |
| # to secrets and id-token even for PRs from forks. This is what makes reviewing | |
| # external contributors' PRs possible. | |
| # | |
| # Security: the triggering workflow file is fork-controlled, so the uploaded | |
| # artifact is untrusted. We validate the PR number is purely numeric before | |
| # using it, and we only check out the base ref (never the PR head into the | |
| # workspace root) so untrusted code is never executed here. See | |
| # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ | |
| on: | |
| workflow_run: | |
| workflows: ["Claude Code Review"] | |
| types: [completed] | |
| jobs: | |
| gate: | |
| # Only react to PR runs that finished successfully. | |
| if: > | |
| github.event.workflow_run.event == 'pull_request' && | |
| github.event.workflow_run.conclusion == 'success' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| outputs: | |
| number: ${{ steps.read.outputs.number }} | |
| found: ${{ steps.check.outputs.found }} | |
| steps: | |
| - name: Check for PR-number artifact | |
| id: check | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7 | |
| with: | |
| script: | | |
| const { data } = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.payload.workflow_run.id, | |
| }); | |
| core.setOutput('found', data.artifacts.some(a => a.name === 'pr-number') ? 'true' : 'false'); | |
| - name: Download PR number | |
| if: steps.check.outputs.found == 'true' | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v4 | |
| with: | |
| name: pr-number | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Read and validate PR number | |
| id: read | |
| if: steps.check.outputs.found == 'true' | |
| run: | | |
| num="$(cat pr-number.txt)" | |
| # Artifact comes from the fork-controlled trigger workflow β never trust it blindly. | |
| if ! [[ "$num" =~ ^[0-9]+$ ]]; then | |
| echo "Refusing to proceed: PR number is not numeric ($num)" >&2 | |
| exit 1 | |
| fi | |
| echo "number=$num" >> "$GITHUB_OUTPUT" | |
| claude-review: | |
| needs: gate | |
| if: needs.gate.outputs.found == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write # post the review on the PR | |
| issues: read | |
| id-token: write | |
| statuses: write # mirror the review result onto the PR head commit | |
| concurrency: | |
| group: claude-review-run-${{ needs.gate.outputs.number }} | |
| cancel-in-progress: true | |
| steps: | |
| # workflow_run runs are detached and never show up in the PR's checks, so | |
| # publish a commit status on the PR head SHA. This surfaces a "Claude Code | |
| # Review" check next to the regular CI jobs. | |
| - name: Report review pending | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7 | |
| with: | |
| script: | | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: context.payload.workflow_run.head_sha, | |
| context: 'Claude Code Review', | |
| state: 'pending', | |
| description: 'Review in progress', | |
| target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, | |
| }); | |
| # Default checkout (base ref) only β do NOT check out the untrusted PR head. | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 | |
| with: | |
| fetch-depth: 1 | |
| - name: Run Claude Code Review | |
| id: review | |
| continue-on-error: true # always report status below, even on failure | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| # Provided explicitly so the review can post on fork PRs; also required | |
| # for allowed_non_write_users to take effect. | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| # Review PRs from external contributors who lack write access. The diff | |
| # is fetched read-only and never executed; the subprocess env is scrubbed. | |
| allowed_non_write_users: "*" | |
| # review dependency bumps opened by Dependabot | |
| allowed_bots: "dependabot[bot]" | |
| plugin_marketplaces: "https://github.com/anthropics/claude-code.git" | |
| plugins: "code-review@claude-code-plugins" | |
| prompt: "/code-review:code-review ${{ github.repository }}/pull/${{ needs.gate.outputs.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 | |
| - name: Report review result | |
| if: always() | |
| env: | |
| OUTCOME: ${{ steps.review.outcome }} | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7 | |
| with: | |
| script: | | |
| const ok = process.env.OUTCOME === 'success'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: context.payload.workflow_run.head_sha, | |
| context: 'Claude Code Review', | |
| state: ok ? 'success' : 'failure', | |
| description: ok ? 'Review complete' : 'Review failed', | |
| target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, | |
| }); | |
| if (!ok) core.setFailed('Claude Code Review failed'); |