claude-code-review.yml: add missing --comment flag #11
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 | |
| # Runs on PRs INTO this repo. We use pull_request_target (not pull_request) so | |
| # that PRs from a fork can access CLAUDE_CODE_OAUTH_TOKEN — GitHub withholds | |
| # secrets from `pull_request` runs triggered by forks, which is why the plain | |
| # `pull_request` version never worked for fork PRs. | |
| # | |
| # SECURITY: pull_request_target runs in the BASE repo with secrets and a | |
| # write-capable token. The job is gated to PRs from the trusted `jnasbyupgrade` | |
| # fork only — an arbitrary external fork can never trigger this secret-bearing | |
| # job. The workflow file always comes from the base branch (master), so a PR | |
| # cannot modify the reviewer that runs on it. We check out the PR head only for | |
| # read context (persist-credentials: false) and never build or execute PR code. | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| concurrency: | |
| group: claude-review-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| claude-review: | |
| # Trusted fork only, and skip drafts (don't spend API/CI on unfinished PRs). | |
| # To add more trusted owners, extend the head-owner check. | |
| if: >- | |
| github.event.pull_request.draft == false && | |
| github.event.pull_request.head.repo.owner.login == 'jnasbyupgrade' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| permissions: | |
| contents: read | |
| pull-requests: write # post the review comments | |
| checks: read # read sibling check-runs for the cost gate | |
| steps: | |
| # COST GATE: the paid Claude review is the last thing to run. Wait for the | |
| # PR head's OTHER check-runs to finish and only proceed if they are clean. | |
| # If any sibling check failed we skip the review to avoid spending money | |
| # reviewing a PR that is already known-broken. Uniform across all repos: | |
| # it discovers sibling checks dynamically (no per-repo workflow names). | |
| # - decision=run : all sibling checks completed with a good conclusion, | |
| # OR no sibling checks exist after a short grace window | |
| # (nothing to gate on), OR the poll timed out is treated | |
| # as skip (see below). | |
| # - decision=skip : at least one sibling check failed/cancelled/etc, or | |
| # we timed out waiting for still-pending checks. | |
| # We exclude this workflow's own check-run (job name `claude-review`) so the | |
| # gate never waits on or fails because of itself. | |
| - name: Wait for CI; skip the paid review if any check failed | |
| id: gate | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPO: ${{ github.repository }} | |
| SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| decision=skip | |
| for i in $(seq 1 72); do # ~24 min max | |
| json=$(gh api "repos/$REPO/commits/$SHA/check-runs" --paginate \ | |
| --jq '[.check_runs[] | select(.name != "claude-review")]' 2>/dev/null) || json='' | |
| [ -z "$json" ] && { sleep 20; continue; } | |
| total=$(jq 'length' <<<"$json") | |
| if [ "$total" -eq 0 ]; then | |
| [ "$i" -ge 9 ] && { decision=run; break; } # ~3 min grace: nothing to gate on | |
| sleep 20; continue | |
| fi | |
| pending=$(jq '[.[]|select(.status!="completed")]|length' <<<"$json") | |
| if [ "$pending" -eq 0 ]; then | |
| bad=$(jq '[.[]|select((.conclusion//"")|test("^(failure|cancelled|timed_out|action_required|stale)$"))]|length' <<<"$json") | |
| [ "$bad" -eq 0 ] && decision=run || decision=skip | |
| break | |
| fi | |
| sleep 20 | |
| done | |
| echo "decision=$decision" >> "$GITHUB_OUTPUT" | |
| echo "gate decision: $decision" | |
| - name: Check out PR head (read-only context) | |
| if: steps.gate.outputs.decision == 'run' | |
| # Intentionally tracks the major-version tag (not a pinned SHA) so | |
| # upstream fixes are picked up automatically. | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Run Claude Code Review | |
| if: steps.gate.outputs.decision == 'run' | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| # Provide github_token so the action uses it directly for GitHub API | |
| # calls instead of the OIDC->GitHub-App-token exchange, which 401s under | |
| # pull_request_target. GITHUB_TOKEN is repo/workflow-scoped (independent | |
| # of the actor's role) and has pull-requests: write here. | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| # NOTE: plugin_marketplaces can't be pinned — it tracks the | |
| # marketplace repo's default branch (upstream anthropics/claude-code). | |
| plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' | |
| plugins: 'code-review@claude-code-plugins' | |
| # --comment is required: without it, the code-review plugin only | |
| # prints its findings to the job log and never posts anything to | |
| # the PR (confirmed by capturing the hidden SDK transcript on a | |
| # canary PR in pgxntool-test: the review correctly found an | |
| # injected bug but ended with "No `--comment` argument was | |
| # provided, so no GitHub comments were posted"). Every review run | |
| # before this fix has been silently invisible on GitHub. | |
| prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }} --comment' |