Skip to content

fix: make interrupted and failed cycles recoverable #5

fix: make interrupted and failed cycles recoverable

fix: make interrupted and failed cycles recoverable #5

Workflow file for this run

name: Claude PR Review
# Triggers:
# - pull_request opened/reopened/ready_for_review: fires once when a PR is
# created (or unmarked draft). Intentionally does NOT include synchronize,
# so subsequent commits do not re-trigger and burn Max quota.
# - issue_comment created: lets a trusted commenter (OWNER, MEMBER, or
# COLLABORATOR) say `@claude review` in any PR comment to request a fresh
# pass on demand. The author_association gate prevents drive-by triggers.
on:
pull_request:
types: [opened, reopened, ready_for_review]
issue_comment:
types: [created]
jobs:
claude-review:
if: |
(github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'issue_comment' && github.event.issue.pull_request != null && contains(github.event.comment.body, '@claude review') && (github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR'))
runs-on: ubuntu-latest
timeout-minutes: 15
# contents: read means the action cannot push commits or modify files even
# if it tried. Comment writes are explicit. id-token: write is required
# so the action can fetch an OIDC token and exchange it for the GitHub
# App installation token used to post review comments.
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
# If you push twice in quick succession (or comment twice), cancel the
# in-progress run so only the latest review lands.
concurrency:
group: claude-review-${{ github.event.pull_request.number || github.event.issue.number }}
cancel-in-progress: true
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
# For comment-triggered reviews:
# 1. Refuse cross-repo (fork) PRs. issue_comment runs in the base-repo
# context with full secrets; feeding an attacker-controlled diff
# into the agent (which has `gh pr comment` allowed) is a known
# prompt-injection -> env-exfiltration path. Refusing here blocks
# it cleanly, regardless of how the agent consumes the diff.
# 2. For same-repo PRs, switch the working tree to the PR's actual
# base branch (not the repo default). PRs targeting release or
# backport branches would otherwise have Claude inspecting
# unrelated default-branch code.
- name: Prepare working tree for comment-triggered reviews
if: github.event_name == 'issue_comment'
id: prep
env:
GH_TOKEN: ${{ github.token }}
run: |
INFO=$(gh pr view ${{ github.event.issue.number }} --json isCrossRepository,baseRefName)
IS_CROSS=$(echo "$INFO" | jq -r '.isCrossRepository')
BASE=$(echo "$INFO" | jq -r '.baseRefName')
if [ "$IS_CROSS" = "true" ]; then
# Set the skip flag first so the decision is recorded even if the
# explanatory comment fails to post; treat the comment as best-effort.
echo "skip=true" >> "$GITHUB_OUTPUT"
gh pr comment ${{ github.event.issue.number }} -b "Cannot run \`@claude review\` on fork PRs: doing so would expose this workflow's secrets to attacker-controlled diff content. Open a branch in the base repo or have a maintainer cherry-pick the changes." || true
exit 0
fi
git fetch origin "$BASE"
git checkout "$BASE"
echo "skip=false" >> "$GITHUB_OUTPUT"
- name: Claude Review
if: success() && steps.prep.outputs.skip != 'true'
uses: anthropics/claude-code-action@c3d45e8e941e1b2ad7b278c57482d9c5bf1f35b3 # v1.0.99
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
trigger_phrase: "@claude review"
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
Review this pull request. Focus on:
- Correctness, edge cases, regressions
- Test coverage gaps
- Security or data-integrity risks
- Style and naming consistent with the surrounding code
Inspect the PR's changes using `gh pr diff ${{ github.event.pull_request.number || github.event.issue.number }}`.
Do not assume the working tree reflects the PR. For pull_request
events the working tree is the merge ref; for issue_comment events
it is the PR's base branch. Either way, the diff command is
authoritative.
Use `gh pr comment` for the top-level summary.
Use `mcp__github_inline_comment__create_inline_comment` (with `confirmed: true`)
for specific code-line issues. Only post GitHub comments; do not
submit review text as chat messages.
Be concise. If nothing material is found, say so in one line.
claude_args: |
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
# Quota cap or transient failure should not turn the PR check red.
continue-on-error: true