Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 41 additions & 10 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
name: Claude Code

# @claude runs only when a maintainer (OWNER/MEMBER/COLLABORATOR) explicitly mentions the
# bot in an issue, an issue comment, a PR review, or a PR review comment. It deliberately
# does NOT trigger on `pull_request_target`:
# - Anthropic's OIDC exchange is broken for that event (401 "Invalid OIDC token";
# anthropics/claude-code-action#713), and
# - a PR-body `@claude` check self-triggered it, because bot-authored PR descriptions
# carry attribution text like "Generated by the @claude workflow". Attribution text
# must never be interpreted as a command.
# If automatic PR-open reviews are wanted later, add a dedicated workflow with a fixed
# prompt rather than reading a PR body for `@claude`.
#
# Separate known limitation — CI does not auto-run on @claude-authored PRs. The bot pushes
# its branch with GITHUB_TOKEN, GitHub does not fire `push` workflows for GITHUB_TOKEN
# pushes, and test.yml is push-only, so a claude/** branch never runs the test suite and
# the PR looks mergeable while unvalidated. The setup steps below let @claude self-verify
# in-run (partial mitigation); still verify @claude PRs locally, or re-push the branch with
# a human token to trigger test.yml, before merging. Proper fix: push with a PAT/App token
# as the action's github_token.

on:
issue_comment:
types: [created]
Expand All @@ -9,8 +28,6 @@ on:
types: [opened, assigned]
pull_request_review:
types: [submitted]
pull_request_target:
types: [opened, synchronize]

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
Expand All @@ -22,18 +39,14 @@ jobs:
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.body, '@claude'))
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
) && (
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR' ||
github.event.sender.author_association == 'OWNER' ||
github.event.sender.author_association == 'MEMBER' ||
github.event.sender.author_association == 'COLLABORATOR' ||
github.event.pull_request.author_association == 'OWNER' ||
github.event.pull_request.author_association == 'MEMBER' ||
github.event.pull_request.author_association == 'COLLABORATOR'
github.event.sender.author_association == 'COLLABORATOR'
)
runs-on: ubuntu-latest
permissions:
Expand All @@ -49,10 +62,28 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6
with:
# For pull_request_target, checkout the PR head to review the actual changes
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
fetch-depth: 1

# Provision the toolchain @claude needs to self-verify a change before pushing.
# Without this the runner has no just/uv, so a run burns its whole time budget on
# pip workarounds and can time out AFTER writing the code but BEFORE committing
# (that is what happened on the first #1011 run). Mirrors test.yml's known-good setup.
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: "pip"

- name: Install uv
run: pip install uv

- uses: extractions/setup-just@v4

- name: Create virtual env and install dependencies
run: |
uv venv
uv pip install -e ".[dev]"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid installing PR code under pull_request_target

When this workflow is triggered by pull_request_target for a member/collaborator PR, the checkout step has already selected github.event.pull_request.head.sha while the job has write-scoped permissions, so this command installs the PR's editable package before Claude starts. A PR can change pyproject.toml, the build backend, or dependency metadata so uv pip install -e executes PR-controlled build/install code with the workflow token still available through the checkout credentials, turning the self-verify setup into automatic privileged code execution; install only trusted base-repo tooling before the action, or move untrusted installation into a lower-permission context.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid installing PR code on PR review triggers

Fresh evidence after the pull_request_target removal: GitHub still runs pull_request_review and pull_request_review_comment workflows on the PR merge ref, and actions/checkout keeps the job token available to later steps by default. When a maintainer mentions @claude on a same-repo PR, this editable install can execute PR-controlled pyproject.toml or build-backend code before Claude starts, with the workflow's contents/pull-requests/issues write permissions; install only trusted base-repo tooling or disable persisted credentials before running any PR-controlled code.

Useful? React with 👍 / 👎.


- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
Expand Down
Loading