Merge branch 'main' of https://github.com/anthropics/claude-code-action into km/network-restrictions-feature

This commit is contained in:
km-anthropic
2025-07-16 08:53:19 -07:00
6 changed files with 175 additions and 9 deletions

140
README.md
View File

@@ -35,6 +35,86 @@ This command will guide you through setting up the GitHub app and required secre
- Or `CLAUDE_CODE_OAUTH_TOKEN` for OAuth token authentication (Pro and Max users can generate this by running `claude setup-token` locally) - Or `CLAUDE_CODE_OAUTH_TOKEN` for OAuth token authentication (Pro and Max users can generate this by running `claude setup-token` locally)
3. Copy the workflow file from [`examples/claude.yml`](./examples/claude.yml) into your repository's `.github/workflows/` 3. Copy the workflow file from [`examples/claude.yml`](./examples/claude.yml) into your repository's `.github/workflows/`
### Using a Custom GitHub App
If you prefer not to install the official Claude app, you can create your own GitHub App to use with this action. This gives you complete control over permissions and access.
**When you may want to use a custom GitHub App:**
- You need more restrictive permissions than the official app
- Organization policies prevent installing third-party apps
- You're using AWS Bedrock or Google Vertex AI
**Steps to create and use a custom GitHub App:**
1. **Create a new GitHub App:**
- Go to https://github.com/settings/apps (for personal apps) or your organization's settings
- Click "New GitHub App"
- Configure the app with these minimum permissions:
- **Repository permissions:**
- Contents: Read & Write
- Issues: Read & Write
- Pull requests: Read & Write
- **Account permissions:** None required
- Set "Where can this GitHub App be installed?" to your preference
- Create the app
2. **Generate and download a private key:**
- After creating the app, scroll down to "Private keys"
- Click "Generate a private key"
- Download the `.pem` file (keep this secure!)
3. **Install the app on your repository:**
- Go to the app's settings page
- Click "Install App"
- Select the repositories where you want to use Claude
4. **Add the app credentials to your repository secrets:**
- Go to your repository's Settings → Secrets and variables → Actions
- Add these secrets:
- `APP_ID`: Your GitHub App's ID (found in the app settings)
- `APP_PRIVATE_KEY`: The contents of the downloaded `.pem` file
5. **Update your workflow to use the custom app:**
```yaml
name: Claude with Custom App
on:
issue_comment:
types: [created]
# ... other triggers
jobs:
claude-response:
runs-on: ubuntu-latest
steps:
# Generate a token from your custom app
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
# Use Claude with your custom app's token
- uses: anthropics/claude-code-action@beta
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.app-token.outputs.token }}
# ... other configuration
```
**Important notes:**
- The custom app must have read/write permissions for Issues, Pull Requests, and Contents
- Your app's token will have the exact permissions you configured, nothing more
For more information on creating GitHub Apps, see the [GitHub documentation](https://docs.github.com/en/apps/creating-github-apps).
## 📚 FAQ ## 📚 FAQ
Having issues or questions? Check out our [Frequently Asked Questions](./FAQ.md) for solutions to common problems and detailed explanations of Claude's capabilities and limitations. Having issues or questions? Check out our [Frequently Asked Questions](./FAQ.md) for solutions to common problems and detailed explanations of Claude's capabilities and limitations.
@@ -109,6 +189,7 @@ jobs:
| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` | | `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` |
| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` | | `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` |
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" | | `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" |
| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" |
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | | `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
| `allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" | | `allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" |
@@ -557,6 +638,65 @@ For GitHub Enterprise users, replace the GitHub.com domains above with your ente
To determine which domains your workflow needs, you can temporarily run without restrictions and monitor the network requests, or check your GitHub Enterprise configuration for the specific services you use. To determine which domains your workflow needs, you can temporarily run without restrictions and monitor the network requests, or check your GitHub Enterprise configuration for the specific services you use.
### Claude Code Settings
You can provide Claude Code settings to customize behavior such as model selection, environment variables, permissions, and hooks. Settings can be provided either as a JSON string or a path to a settings file.
#### Option 1: Settings File
```yaml
- uses: anthropics/claude-code-action@beta
with:
settings: "path/to/settings.json"
# ... other inputs
```
#### Option 2: Inline Settings
```yaml
- uses: anthropics/claude-code-action@beta
with:
settings: |
{
"model": "claude-opus-4-20250514",
"env": {
"DEBUG": "true",
"API_URL": "https://api.example.com"
},
"permissions": {
"allow": ["Bash", "Read"],
"deny": ["WebFetch"]
},
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "echo Running bash command..."
}]
}]
}
}
# ... other inputs
```
The settings support all Claude Code settings options including:
- `model`: Override the default model
- `env`: Environment variables for the session
- `permissions`: Tool usage permissions
- `hooks`: Pre/post tool execution hooks
- And more...
For a complete list of available settings and their descriptions, see the [Claude Code settings documentation](https://docs.anthropic.com/en/docs/claude-code/settings).
**Notes**:
- The `enableAllProjectMcpServers` setting is always set to `true` by this action to ensure MCP servers work correctly.
- If both the `model` input parameter and a `model` in settings are provided, the `model` input parameter takes precedence.
- The `allowed_tools` and `disallowed_tools` input parameters take precedence over `permissions` in settings.
- In a future version, we may deprecate individual input parameters in favor of using the settings file for all configuration.
## Cloud Providers ## Cloud Providers
You can authenticate with Claude using any of these three methods: You can authenticate with Claude using any of these three methods:

View File

@@ -60,6 +60,10 @@ inputs:
description: "Custom environment variables to pass to Claude Code execution (YAML format)" description: "Custom environment variables to pass to Claude Code execution (YAML format)"
required: false required: false
default: "" default: ""
settings:
description: "Claude Code settings as JSON string or path to settings JSON file"
required: false
default: ""
# Auth configuration # Auth configuration
anthropic_api_key: anthropic_api_key:
@@ -181,7 +185,7 @@ runs:
- name: Run Claude Code - name: Run Claude Code
id: claude-code id: claude-code
if: steps.prepare.outputs.contains_trigger == 'true' if: steps.prepare.outputs.contains_trigger == 'true'
uses: anthropics/claude-code-base-action@ca8aaa8335d12ada79d9336739b03e24b4aa5ae3 # v0.0.34 uses: anthropics/claude-code-base-action@503cc7080e62d63d2cc1d80035ed04617d5efb47 # v0.0.35
with: with:
prompt_file: ${{ runner.temp }}/claude-prompts/claude-prompt.txt prompt_file: ${{ runner.temp }}/claude-prompts/claude-prompt.txt
allowed_tools: ${{ env.ALLOWED_TOOLS }} allowed_tools: ${{ env.ALLOWED_TOOLS }}
@@ -196,6 +200,7 @@ runs:
anthropic_api_key: ${{ inputs.anthropic_api_key }} anthropic_api_key: ${{ inputs.anthropic_api_key }}
claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }}
claude_env: ${{ inputs.claude_env }} claude_env: ${{ inputs.claude_env }}
settings: ${{ inputs.settings }}
env: env:
# Model configuration # Model configuration
ANTHROPIC_MODEL: ${{ inputs.model || inputs.anthropic_model }} ANTHROPIC_MODEL: ${{ inputs.model || inputs.anthropic_model }}

View File

@@ -91,7 +91,8 @@ async function run() {
githubToken, githubToken,
owner: context.repository.owner, owner: context.repository.owner,
repo: context.repository.repo, repo: context.repository.repo,
branch: branchInfo.currentBranch, branch: branchInfo.claudeBranch || branchInfo.currentBranch,
baseBranch: branchInfo.baseBranch,
additionalMcpConfig, additionalMcpConfig,
claudeCommentId: commentId.toString(), claudeCommentId: commentId.toString(),
allowedTools: context.inputs.allowedTools, allowedTools: context.inputs.allowedTools,

View File

@@ -78,11 +78,7 @@ async function getOrCreateBranchRef(
throw new Error(`Failed to get branch reference: ${refResponse.status}`); throw new Error(`Failed to get branch reference: ${refResponse.status}`);
} }
// Branch doesn't exist, need to create it const baseBranch = process.env.BASE_BRANCH!;
console.log(`Branch ${branch} does not exist, creating it...`);
// Get base branch from environment or determine it
const baseBranch = process.env.BASE_BRANCH || "main";
// Get the SHA of the base branch // Get the SHA of the base branch
const baseRefUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${baseBranch}`; const baseRefUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${baseBranch}`;
@@ -139,7 +135,7 @@ async function getOrCreateBranchRef(
baseSha = baseRefData.object.sha; baseSha = baseRefData.object.sha;
} }
// Create the new branch // Create the new branch using the same pattern as octokit
const createRefUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs`; const createRefUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs`;
const createRefResponse = await fetch(createRefUrl, { const createRefResponse = await fetch(createRefUrl, {
method: "POST", method: "POST",

View File

@@ -8,6 +8,7 @@ type PrepareConfigParams = {
owner: string; owner: string;
repo: string; repo: string;
branch: string; branch: string;
baseBranch: string;
additionalMcpConfig?: string; additionalMcpConfig?: string;
claudeCommentId?: string; claudeCommentId?: string;
allowedTools: string[]; allowedTools: string[];
@@ -54,6 +55,7 @@ export async function prepareMcpConfig(
owner, owner,
repo, repo,
branch, branch,
baseBranch,
additionalMcpConfig, additionalMcpConfig,
claudeCommentId, claudeCommentId,
allowedTools, allowedTools,
@@ -100,7 +102,7 @@ export async function prepareMcpConfig(
REPO_OWNER: owner, REPO_OWNER: owner,
REPO_NAME: repo, REPO_NAME: repo,
BRANCH_NAME: branch, BRANCH_NAME: branch,
BASE_BRANCH: process.env.BASE_BRANCH || "", BASE_BRANCH: baseBranch,
REPO_DIR: process.env.GITHUB_WORKSPACE || process.cwd(), REPO_DIR: process.env.GITHUB_WORKSPACE || process.cwd(),
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || "", GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || "",
IS_PR: process.env.IS_PR || "false", IS_PR: process.env.IS_PR || "false",

View File

@@ -88,6 +88,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: mockContext, context: mockContext,
}); });
@@ -118,6 +119,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: contextWithSigning, context: contextWithSigning,
}); });
@@ -143,6 +145,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [ allowedTools: [
"mcp__github__create_issue", "mcp__github__create_issue",
"mcp__github_file_ops__commit_files", "mcp__github_file_ops__commit_files",
@@ -174,6 +177,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [ allowedTools: [
"mcp__github_file_ops__commit_files", "mcp__github_file_ops__commit_files",
"mcp__github_file_ops__update_claude_comment", "mcp__github_file_ops__update_claude_comment",
@@ -193,6 +197,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: ["Edit", "Read", "Write"], allowedTools: ["Edit", "Read", "Write"],
context: mockContext, context: mockContext,
}); });
@@ -210,6 +215,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: "", additionalMcpConfig: "",
allowedTools: [], allowedTools: [],
context: mockContext, context: mockContext,
@@ -228,6 +234,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: " \n\t ", additionalMcpConfig: " \n\t ",
allowedTools: [], allowedTools: [],
context: mockContext, context: mockContext,
@@ -258,6 +265,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: additionalConfig, additionalMcpConfig: additionalConfig,
allowedTools: [ allowedTools: [
"mcp__github__create_issue", "mcp__github__create_issue",
@@ -296,6 +304,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: additionalConfig, additionalMcpConfig: additionalConfig,
allowedTools: [ allowedTools: [
"mcp__github__create_issue", "mcp__github__create_issue",
@@ -337,6 +346,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: additionalConfig, additionalMcpConfig: additionalConfig,
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
@@ -357,6 +367,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: invalidJson, additionalMcpConfig: invalidJson,
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
@@ -378,6 +389,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: nonObjectJson, additionalMcpConfig: nonObjectJson,
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
@@ -402,6 +414,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: nullJson, additionalMcpConfig: nullJson,
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
@@ -426,6 +439,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: arrayJson, additionalMcpConfig: arrayJson,
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
@@ -473,6 +487,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
additionalMcpConfig: additionalConfig, additionalMcpConfig: additionalConfig,
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
@@ -496,6 +511,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
}); });
@@ -517,6 +533,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
}); });
@@ -545,6 +562,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: contextWithPermissions, context: contextWithPermissions,
}); });
@@ -564,6 +582,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: mockContextWithSigning, context: mockContextWithSigning,
}); });
@@ -582,6 +601,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: mockPRContextWithSigning, context: mockPRContextWithSigning,
}); });
@@ -613,6 +633,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: contextWithPermissions, context: contextWithPermissions,
}); });
@@ -641,6 +662,7 @@ describe("prepareMcpConfig", () => {
owner: "test-owner", owner: "test-owner",
repo: "test-repo", repo: "test-repo",
branch: "test-branch", branch: "test-branch",
baseBranch: "main",
allowedTools: [], allowedTools: [],
context: contextWithPermissions, context: contextWithPermissions,
}); });