From 5d223ff468a29908bfa63988d474f356c8f1274c Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 6 Mar 2026 15:06:36 +0100 Subject: [PATCH] ci: Make claude action review PRs only and fix the instructions Turns out the claude code action has issues reviewing PRs from forks (https://github.com/anthropics/claude-code-action/issues/939). Let's reuse the approach from https://github.com/pzmarzly/demo--claude-bot-reviews instead (which I've explicitly asked permission for to reuse). Unlike the linked demo, we still insist on a comment by a maintainer before claude reviews the PR. --- .github/workflows/claude-review.yml | 151 ++++++++++++++++++++++++++++ .github/workflows/claude.yml | 62 ------------ 2 files changed, 151 insertions(+), 62 deletions(-) create mode 100644 .github/workflows/claude-review.yml delete mode 100644 .github/workflows/claude.yml diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml new file mode 100644 index 00000000000..bba7bdd1b57 --- /dev/null +++ b/.github/workflows/claude-review.yml @@ -0,0 +1,151 @@ +# Integrates Claude Code as an AI assistant for reviewing pull requests. +# Mention @claude in any PR review to request a review. Claude authenticates +# via AWS Bedrock using OIDC — no long-lived API keys required. + +name: Claude Review + +on: + pull_request_review_comment: + types: [created] + pull_request_review: + types: [submitted] +concurrency: + group: claude-review-${{ github.event.pull_request.number }} + cancel-in-progress: true +jobs: + claude-review: + runs-on: ubuntu-latest + + if: | + github.repository_owner == 'systemd' && + (github.event_name == 'pull_request_review_comment' && + contains(github.event.comment.body, '@claude') && + contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association)) || + (github.event_name == 'pull_request_review' && + contains(github.event.review.body, '@claude') && + contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.review.author_association)) + + permissions: + contents: read # Read repository contents + pull-requests: write # Post comments and reviews on PRs + id-token: write # Authenticate with AWS via OIDC + actions: read # Access workflow run metadata + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }} + role-session-name: GitHubActions-Claude-${{ github.run_id }} + aws-region: us-east-1 + + - name: Run Claude Code + uses: anthropics/claude-code-action@1fc90f3ed982521116d8ff6d85b948c9b12cae3e + with: + use_bedrock: "true" + github_token: ${{ secrets.GITHUB_TOKEN }} + claude_args: | + --model us.anthropic.claude-opus-4-6-v1 + --max-turns 100 + --allowedTools " + Read,Write,Edit,MultiEdit,LS,Grep,Glob,Task, + Bash(cat:*),Bash(test:*),Bash(printf:*),Bash(jq:*),Bash(head:*),Bash(git:*),Bash(gh:*), + mcp__github_inline_comment__create_inline_comment, + " + prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.pull_request.number }} + HEAD SHA: ${{ github.event.pull_request.head.sha }} + + Review this pull request. + You are in the upstream repo without the patch applied. Do not apply it. + + ## Phase 1: Gather context + + Fetch the patch, PR title/body, and list of existing comments (top-level, inline, and reviews): + - `gh pr diff ${{ github.event.pull_request.number }} --patch` + - `gh pr view ${{ github.event.pull_request.number }} --json title,body` + - `gh api --paginate repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments` + - `gh api --paginate repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments` + - `gh api --paginate repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews` + + ## Phase 2: Parallel review subagents + + Review: + - Code quality, style, and best practices + - Potential bugs, issues, incorrect logic + - Security implications + - CLAUDE.md - compliance + + For every category, launch subagents to review them in parallel. Group related sections + as needed — use 2-4 subagents based on PR size and scope. + + Give each subagent the PR title, description, full patch, and the list of changed files. + + Each subagent must return a JSON array of issues: + `[{"file": "path", "line": , "severity": "must-fix|suggestion|nit", "title": "...", "body": "..."}]` + + Subagents must ONLY return the JSON array — they must NOT post comments, + call `gh`, or use `mcp__github_inline_comment__create_inline_comment`. + All posting happens in Phase 3. + + Each subagent MUST verify its findings before returning them: + - For style/convention claims, check at least 3 existing examples in the codebase to confirm + the pattern actually exists before flagging a violation. + - For "use X instead of Y" suggestions, confirm X actually exists and works for this case. + - If unsure, don't include the issue. + + ## Phase 3: Collect and post + + After ALL subagents complete: + 1. Collect all issues. Merge duplicates (same file, lines within 3 of each other, same problem). + 2. Drop low-confidence findings. + 3. For CLAUDE.md violations that appear in 3+ existing places in the codebase, do NOT post inline comments. + Instead, add them to the 'CLAUDE.md improvements' section of the tracking comment + 4. Check existing inline review comments (fetched in Phase 1). Do NOT post an inline comment if + one already exists on the same file+line about the same problem. + 5. Check for author replies that dismiss or reject a previous comment. Do NOT re-raise an issue + the PR author has already responded to disagreeing with. + 6. Post new inline comments with `mcp__github_inline_comment__create_inline_comment`. + + Prefix ALL comments with "Claude: ". + Link format: https://github.com/${{ github.repository }}/blob/${{ github.event.pull_request.head.sha }}/README.md#L10-L15 + + Then maintain a single top-level "tracking comment" listing ALL issues as checkboxes. + Use a hidden HTML marker to find it: ``. + Look through the top-level comments fetched in Phase 1 for one containing that marker. + + **If no tracking comment exists (first run):** + Create one with `gh pr comment ${{ github.event.pull_request.number }} --body "..."` using this format: + ``` + Claude: review of # () + + + + ### Must fix + - [ ] **title** — `file:line` — short explanation + + ### Suggestions + - [ ] **title** — `file:line` — short explanation + + ### Nits + - [ ] **title** — `file:line` — short explanation + + ### CLAUDE.md improvements + - improvement suggestion + ``` + Omit empty sections. + + **If a tracking comment already exists (subsequent run):** + 1. Parse the existing checkboxes. For each old issue, check if the current patch still has + that problem (re-check the relevant lines in the new diff). If fixed, mark it `- [x]`. + If the author dismissed it, mark it `- [x] ~~title~~ (dismissed)`. + 2. Append any NEW issues found in this run that aren't already listed. + 3. Update the HEAD SHA in the header line. + 4. Edit the comment in-place. + ``` + printf '%s' "$BODY" > pr-review-body.txt + gh api --method PATCH repos/${{ github.repository }}/issues/comments/ -F body=@pr-review-body.txt + ``` diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index 79762a5d7c2..00000000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,62 +0,0 @@ -# Integrates Claude Code as an AI assistant for issues and pull requests. -# Mention @claude in any issue comment, PR review comment, or PR review to -# interact with it, or assign the "claude" user to an issue. Claude -# authenticates via AWS Bedrock using OIDC — no long-lived API keys required. - -name: Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - runs-on: ubuntu-latest - - if: | - github.repository_owner == 'systemd' && - ((github.event_name == 'issue_comment' && - contains(github.event.comment.body, '@claude') && - contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association)) || - (github.event_name == 'pull_request_review_comment' && - contains(github.event.comment.body, '@claude') && - contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association)) || - (github.event_name == 'pull_request_review' && - contains(github.event.review.body, '@claude') && - contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.review.author_association)) || - (github.event_name == 'issues' && - github.event.action == 'assigned' && - github.event.assignee.login == 'claude')) - - permissions: - contents: read # Read repository contents - issues: write # Post comments on issues - pull-requests: write # Post comments and reviews on PRs - id-token: write # Authenticate with AWS via OIDC - actions: read # Access workflow run metadata - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - with: - fetch-depth: 1 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 - with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }} - role-session-name: GitHubActions-Claude-${{ github.run_id }} - aws-region: us-east-1 - - - name: Run Claude Code - uses: anthropics/claude-code-action@1fc90f3ed982521116d8ff6d85b948c9b12cae3e - with: - use_bedrock: "true" - github_token: ${{ secrets.GITHUB_TOKEN }} - claude_args: | - --model us.anthropic.claude-opus-4-6-v1 -- 2.47.3