Skip to content

Commit b537806

Browse files
committed
feat: add allow_bot_actor parameter for automated workflows
- Add allow_bot_actor parameter to enable GitHub bots to trigger Claude Code Action - Implement robust bot write permission validation - Use repo.permissions for comprehensive access checks - Handle both collaborator and installation permissions - Add comprehensive test coverage for bot scenarios - Update documentation with security considerations This enables automated workflows like documentation updates, CI-triggered code reviews, and scheduled maintenance while maintaining security through explicit opt-in and proper permission validation.
1 parent e07ea01 commit b537806

File tree

10 files changed

+869
-93
lines changed

10 files changed

+869
-93
lines changed

FAQ.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ This FAQ addresses common questions and gotchas when using the Claude Code GitHu
66

77
### Why doesn't tagging @claude from my automated workflow work?
88

9-
The `github-actions` user cannot trigger subsequent GitHub Actions workflows. This is a GitHub security feature to prevent infinite loops. To make this work, you need to use a Personal Access Token (PAT) instead, which will act as a regular user, or use a separate app token of your own. When posting a comment on an issue or PR from your workflow, use your PAT instead of the `GITHUB_TOKEN` generated in your workflow.
9+
By default, bots cannot trigger Claude for security reasons. With `allow_bot_actor: true`, you can enable bot triggers, but there are important distinctions:
10+
11+
1. **GitHub Apps** (recommended): Create a GitHub App, use app tokens, and set `allow_bot_actor: true`. The app needs write permissions.
12+
2. **Personal Access Tokens**: Use a PAT instead of `GITHUB_TOKEN` in your workflows with `allow_bot_actor: true`.
13+
3. **github-actions[bot]**: Can trigger Claude with `allow_bot_actor: true`, BUT due to GitHub's security, responses won't trigger subsequent workflows.
14+
15+
**Important**: With `allow_bot_actor: true`, `github-actions[bot]` CAN trigger Claude initially. However, Claude's responses (when using `GITHUB_TOKEN`) cannot trigger subsequent workflows due to GitHub's anti-loop security feature.
1016

1117
### Why does Claude say I don't have permission to trigger it?
1218

README.md

Lines changed: 31 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,6 @@ jobs:
145145
# Or use OAuth token instead:
146146
# claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
147147
github_token: ${{ secrets.GITHUB_TOKEN }}
148-
# Optional: set execution mode (default: tag)
149-
# mode: "tag"
150148
# Optional: add custom trigger phrase (default: @claude)
151149
# trigger_phrase: "/claude"
152150
# Optional: add assignee trigger for issues
@@ -167,79 +165,41 @@ jobs:
167165

168166
## Inputs
169167

170-
| Input | Description | Required | Default |
171-
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | -------- | --------- |
172-
| `mode` | Execution mode: 'tag' (default - triggered by mentions/assignments), 'agent' (for automation with no trigger checking) | No | `tag` |
173-
| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - |
174-
| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - |
175-
| `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - |
176-
| `override_prompt` | Complete replacement of Claude's prompt with custom template (supports variable substitution) | No | - |
177-
| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - |
178-
| `max_turns` | Maximum number of conversation turns Claude can take (limits back-and-forth exchanges) | No | - |
179-
| `timeout_minutes` | Timeout in minutes for execution | No | `30` |
180-
| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` |
181-
| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - |
182-
| `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | - |
183-
| `fallback_model` | Enable automatic fallback to specified model when primary model is unavailable | No | - |
184-
| `anthropic_model` | **DEPRECATED**: Use `model` instead. Kept for backward compatibility. | No | - |
185-
| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` |
186-
| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` |
187-
| `allowed_tools` | Additional tools for Claude to use (the base GitHub tools will always be included) | No | "" |
188-
| `disallowed_tools` | Tools that Claude should never use | No | "" |
189-
| `custom_instructions` | Additional custom instructions to include in the prompt for Claude | No | "" |
190-
| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" |
191-
| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - |
192-
| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - |
193-
| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` |
194-
| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` |
195-
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" |
196-
| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" |
197-
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
198-
| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" |
199-
| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` |
168+
| Input | Description | Required | Default |
169+
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -------- | --------- |
170+
| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - |
171+
| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - |
172+
| `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - |
173+
| `override_prompt` | Complete replacement of Claude's prompt with custom template (supports variable substitution) | No | - |
174+
| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - |
175+
| `max_turns` | Maximum number of conversation turns Claude can take (limits back-and-forth exchanges) | No | - |
176+
| `timeout_minutes` | Timeout in minutes for execution | No | `30` |
177+
| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` |
178+
| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - |
179+
| `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | - |
180+
| `fallback_model` | Enable automatic fallback to specified model when primary model is unavailable | No | - |
181+
| `anthropic_model` | **DEPRECATED**: Use `model` instead. Kept for backward compatibility. | No | - |
182+
| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` |
183+
| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` |
184+
| `allowed_tools` | Additional tools for Claude to use (the base GitHub tools will always be included) | No | "" |
185+
| `disallowed_tools` | Tools that Claude should never use | No | "" |
186+
| `custom_instructions` | Additional custom instructions to include in the prompt for Claude | No | "" |
187+
| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" |
188+
| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - |
189+
| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - |
190+
| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` |
191+
| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` |
192+
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" |
193+
| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" |
194+
| `allow_bot_actor` | Allow GitHub bots and automation accounts to trigger Claude (security: defaults to false, requires explicit opt-in) | No | `false` |
195+
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
196+
| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" |
197+
| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` |
200198

201199
\*Required when using direct Anthropic API (default and when not using Bedrock or Vertex)
202200

203201
> **Note**: This action is currently in beta. Features and APIs may change as we continue to improve the integration.
204202
205-
## Execution Modes
206-
207-
The action supports two execution modes, each optimized for different use cases:
208-
209-
### Tag Mode (Default)
210-
211-
The traditional implementation mode that responds to @claude mentions, issue assignments, or labels.
212-
213-
- **Triggers**: `@claude` mentions, issue assignment, label application
214-
- **Features**: Creates tracking comments with progress checkboxes, full implementation capabilities
215-
- **Use case**: General-purpose code implementation and Q&A
216-
217-
```yaml
218-
- uses: anthropics/claude-code-action@beta
219-
with:
220-
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
221-
# mode: tag is the default
222-
```
223-
224-
### Agent Mode
225-
226-
For automation and scheduled tasks without trigger checking.
227-
228-
- **Triggers**: Always runs (no trigger checking)
229-
- **Features**: Perfect for scheduled tasks, works with `override_prompt`
230-
- **Use case**: Maintenance tasks, automated reporting, scheduled checks
231-
232-
```yaml
233-
- uses: anthropics/claude-code-action@beta
234-
with:
235-
mode: agent
236-
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
237-
override_prompt: |
238-
Check for outdated dependencies and create an issue if any are found.
239-
```
240-
241-
See [`examples/claude-modes.yml`](./examples/claude-modes.yml) for complete examples of each mode.
242-
243203
### Using Custom MCP Configuration
244204

245205
The `mcp_config` input allows you to add custom MCP (Model Context Protocol) servers to extend Claude's capabilities. These servers merge with the built-in GitHub MCP servers.
@@ -871,7 +831,7 @@ Both AWS Bedrock and GCP Vertex AI require OIDC authentication.
871831
### Access Control
872832

873833
- **Repository Access**: The action can only be triggered by users with write access to the repository
874-
- **No Bot Triggers**: GitHub Apps and bots cannot trigger this action
834+
- **Bot Actor Control**: GitHub Apps and bots are blocked by default for security. Use `allow_bot_actor: true` to enable automated workflows (requires explicit opt-in)
875835
- **Token Permissions**: The GitHub app receives only a short-lived token scoped specifically to the repository it's operating in
876836
- **No Cross-Repository Access**: Each action invocation is limited to the repository where it was triggered
877837
- **Limited Scope**: The token cannot access other repositories or perform actions beyond the configured permissions

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Thank you for trying out the beta of our GitHub Action! This document outlines o
1010
- **Support for workflow_dispatch and repository_dispatch events** - Dispatch Claude on events triggered via API from other workflows or from other services
1111
- **Ability to disable commit signing** - Option to turn off GPG signing for environments where it's not required. This will enable Claude to use normal `git` bash commands for committing. This will likely become the default behavior once added.
1212
- **Better code review behavior** - Support inline comments on specific lines, provide higher quality reviews with more actionable feedback
13-
- **Support triggering @claude from bot users** - Allow automation and bot accounts to invoke Claude
13+
- ~**Support triggering @claude from bot users** - Allow automation and bot accounts to invoke Claude~
1414
- **Customizable base prompts** - Full control over Claude's initial context with template variables like `$PR_COMMENTS`, `$PR_FILES`, etc. Users can replace our default prompt entirely while still accessing key contextual data
1515

1616
---

action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ inputs:
6060
description: "Complete replacement of Claude's prompt with custom template (supports variable substitution)"
6161
required: false
6262
default: ""
63+
allow_bot_actor:
64+
description: "Allow bot actors to trigger the action. Default is false for security reasons."
65+
required: false
66+
default: "false"
6367
mcp_config:
6468
description: "Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers"
6569
additional_permissions:
@@ -154,6 +158,7 @@ runs:
154158
CUSTOM_INSTRUCTIONS: ${{ inputs.custom_instructions }}
155159
DIRECT_PROMPT: ${{ inputs.direct_prompt }}
156160
OVERRIDE_PROMPT: ${{ inputs.override_prompt }}
161+
ALLOW_BOT_ACTOR: ${{ inputs.allow_bot_actor }}
157162
MCP_CONFIG: ${{ inputs.mcp_config }}
158163
OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }}
159164
GITHUB_RUN_ID: ${{ github.run_id }}

src/entrypoints/prepare.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ async function run() {
5151
return;
5252
}
5353

54-
// Step 5: Check if actor is human
54+
// Step 5: Check if actor is human (unless bot actors are allowed)
5555
await checkHumanActor(octokit.rest, context);
5656

5757
// Step 6: Create initial tracking comment (mode-aware)

src/github/context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export type ParsedGitHubContext = {
4343
useStickyComment: boolean;
4444
additionalPermissions: Map<string, string>;
4545
useCommitSigning: boolean;
46+
allowBotActor: boolean;
4647
};
4748
};
4849

@@ -81,6 +82,7 @@ export function parseGitHubContext(): ParsedGitHubContext {
8182
process.env.ADDITIONAL_PERMISSIONS ?? "",
8283
),
8384
useCommitSigning: process.env.USE_COMMIT_SIGNING === "true",
85+
allowBotActor: process.env.ALLOW_BOT_ACTOR === "true",
8486
},
8587
};
8688

src/github/validation/actor.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,47 @@
55
* Prevents automated tools or bots from triggering Claude
66
*/
77

8+
import * as core from "@actions/core";
89
import type { Octokit } from "@octokit/rest";
910
import type { ParsedGitHubContext } from "../context";
1011

12+
/**
13+
* Get the GitHub actor type (User, Bot, Organization, etc.)
14+
*/
15+
async function getActorType(
16+
octokit: Octokit,
17+
actor: string,
18+
): Promise<string | null> {
19+
try {
20+
const { data } = await octokit.users.getByUsername({ username: actor });
21+
return data.type;
22+
} catch (error) {
23+
core.warning(`Failed to get user data for ${actor}: ${error}`);
24+
return null;
25+
}
26+
}
27+
1128
export async function checkHumanActor(
1229
octokit: Octokit,
1330
githubContext: ParsedGitHubContext,
1431
) {
15-
// Fetch user information from GitHub API
16-
const { data: userData } = await octokit.users.getByUsername({
17-
username: githubContext.actor,
18-
});
32+
const actorType = await getActorType(octokit, githubContext.actor);
1933

20-
const actorType = userData.type;
34+
if (!actorType) {
35+
throw new Error(
36+
`Could not determine actor type for: ${githubContext.actor}`,
37+
);
38+
}
2139

2240
console.log(`Actor type: ${actorType}`);
2341

42+
if (githubContext.inputs.allowBotActor && actorType === "Bot") {
43+
console.log(
44+
`Bot actor allowed, skipping human actor check for: ${githubContext.actor}`,
45+
);
46+
return;
47+
}
48+
2449
if (actorType !== "User") {
2550
throw new Error(
2651
`Workflow initiated by non-human actor: ${githubContext.actor} (type: ${actorType}).`,

0 commit comments

Comments
 (0)