summaryrefslogtreecommitdiff
path: root/.github/workflows/claude.yml
blob: 66e36897c066d410fc0c8abbdc19b4224fae911e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
name: Claude Code

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    # only "opened" — an issue's author_association gates the summon below;
    # "assigned" would gate on the issue author, not the assigner, so a
    # maintainer assigning an outsider's issue would be wrongly skipped.
    types: [opened]
  pull_request_review:
    types: [submitted]

jobs:
  claude:
    # Only trusted actors (repo owner/member/collaborator) may summon @claude, so the
    # write-scoped token and OAuth secret are never issued for an outside contributor's
    # comment on this public repo. Defense-in-depth on top of the action's own check.
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') &&
        contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') &&
        contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
      (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') &&
        contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association)) ||
      (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
        contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association))
    runs-on: ubuntu-latest
    permissions:
      contents: write       # allow Claude to push commits/branches when asked
      pull-requests: write  # allow Claude to comment on / update PRs
      issues: write         # allow Claude to comment on / update issues
      id-token: write
      actions: read # Required for Claude to read CI results on PRs
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 1

      - name: Run Claude Code
        id: claude
        uses: anthropics/claude-code-action@v1
        with:
          claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

          # This is an optional setting that allows Claude to read CI results on PRs
          additional_permissions: |
            actions: read

          # Sign the bot's commits so they show as "Verified". The action commits
          # automatically — on a PR comment it pushes to that PR's branch; on an
          # issue comment it opens a new claude/* branch + PR with the fix.
          use_commit_signing: true

          # No custom prompt: Claude performs the instructions in the @claude comment.

          # Deliberately NO Bash in the tool allowlist. @claude can be summoned on a
          # fork PR (claude-code-review.yml even directs fork PRs here), and this job
          # holds the OAuth secret + a write token. Any build/interpreter command
          # (python -c, cmake/make custom targets, etc.) run against attacker-
          # controlled PR content is arbitrary code + network execution, so no
          # command allowlist can safely contain it. Claude still edits files and
          # the action commits/opens the PR; the resulting commit is verified by the
          # repo's CircleCI matrix. --max-turns gives room to investigate + fix.
          claude_args: '--max-turns 30'