]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ci: Make claude action review PRs only and fix the instructions 40971/head
authorDaan De Meyer <daan@amutable.com>
Fri, 6 Mar 2026 14:06:36 +0000 (15:06 +0100)
committerDaan De Meyer <daan@amutable.com>
Fri, 6 Mar 2026 14:11:54 +0000 (15:11 +0100)
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 [new file with mode: 0644]
.github/workflows/claude.yml [deleted file]

diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml
new file mode 100644 (file)
index 0000000..bba7bdd
--- /dev/null
@@ -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": <number or null>, "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: `<!-- claude-pr-review -->`.
+              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 <REPO> #<PR NUMBER> (<HEAD SHA>)
+
+              <!-- claude-pr-review -->
+
+              ### 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/<comment-id> -F body=@pr-review-body.txt
+                  ```
diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml
deleted file mode 100644 (file)
index 79762a5..0000000
+++ /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