mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 23:14:13 +08:00
Compare commits
23 Commits
v1.0.10
...
add-plugin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23e406ca24 | ||
|
|
f5f27d4716 | ||
|
|
fd20c95358 | ||
|
|
d808160c26 | ||
|
|
3eacedbeb7 | ||
|
|
f52f12eba5 | ||
|
|
4a85933f25 | ||
|
|
ba6edd55ef | ||
|
|
06461dddff | ||
|
|
c2a94eead0 | ||
|
|
1c0c3eaced | ||
|
|
23d2d6c6b4 | ||
|
|
e8bad57227 | ||
|
|
0a6d62601b | ||
|
|
777ffcbfc9 | ||
|
|
dc58efed33 | ||
|
|
e5437bfbc5 | ||
|
|
b2dd1006a0 | ||
|
|
ac1a3207f3 | ||
|
|
521d069da7 | ||
|
|
7e4b782d5f | ||
|
|
4fb0ef3be0 | ||
|
|
14ac8aa20e |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
|
||||
2
.github/workflows/claude-review.yml
vendored
2
.github/workflows/claude-review.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
2
.github/workflows/claude.yml
vendored
2
.github/workflows/claude.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
2
.github/workflows/issue-triage.yml
vendored
2
.github/workflows/issue-triage.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
next_version: ${{ steps.next_version.outputs.next_version }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
environment: production
|
||||
steps:
|
||||
- name: Checkout base-action repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: anthropics/claude-code-base-action
|
||||
token: ${{ secrets.CLAUDE_CODE_BASE_ACTION_PAT }}
|
||||
|
||||
@@ -41,6 +41,10 @@ inputs:
|
||||
description: "Claude Code settings as JSON string or path to settings JSON file"
|
||||
required: false
|
||||
default: ""
|
||||
plugins:
|
||||
description: "Comma-separated list of Claude Code plugins to install (e.g., 'plugin-name1,plugin-name2')"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
# Auth configuration
|
||||
anthropic_api_key:
|
||||
@@ -177,7 +181,7 @@ runs:
|
||||
# Install Claude Code if no custom executable is provided
|
||||
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
||||
echo "Installing Claude Code..."
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.2
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.24
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
else
|
||||
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||
@@ -208,6 +212,7 @@ runs:
|
||||
CLAUDE_CODE_ACTION: "1"
|
||||
INPUT_PROMPT_FILE: ${{ runner.temp }}/claude-prompts/claude-prompt.txt
|
||||
INPUT_SETTINGS: ${{ inputs.settings }}
|
||||
INPUT_PLUGINS: ${{ inputs.plugins }}
|
||||
INPUT_CLAUDE_ARGS: ${{ steps.prepare.outputs.claude_args }}
|
||||
INPUT_EXPERIMENTAL_SLASH_COMMANDS_DIR: ${{ github.action_path }}/slash-commands
|
||||
INPUT_ACTION_INPUTS_PRESENT: ${{ steps.prepare.outputs.action_inputs_present }}
|
||||
|
||||
@@ -336,7 +336,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ inputs:
|
||||
description: "Claude Code settings as JSON string or path to settings JSON file"
|
||||
required: false
|
||||
default: ""
|
||||
plugins:
|
||||
description: "Comma-separated list of Claude Code plugins to install (e.g., 'plugin-name1,plugin-name2')"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
# Action settings
|
||||
claude_args:
|
||||
@@ -99,7 +103,7 @@ runs:
|
||||
run: |
|
||||
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
||||
echo "Installing Claude Code..."
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.2
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.24
|
||||
else
|
||||
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||
# Add the directory containing the custom executable to PATH
|
||||
@@ -123,6 +127,7 @@ runs:
|
||||
INPUT_PROMPT: ${{ inputs.prompt }}
|
||||
INPUT_PROMPT_FILE: ${{ inputs.prompt_file }}
|
||||
INPUT_SETTINGS: ${{ inputs.settings }}
|
||||
INPUT_PLUGINS: ${{ inputs.plugins }}
|
||||
INPUT_CLAUDE_ARGS: ${{ inputs.claude_args }}
|
||||
INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||
INPUT_PATH_TO_BUN_EXECUTABLE: ${{ inputs.path_to_bun_executable }}
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
"--rm",
|
||||
"-e",
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
||||
"ghcr.io/github/github-mcp-server:sha-7aced2b"
|
||||
"ghcr.io/github/github-mcp-server:sha-23fa0dd"
|
||||
],
|
||||
"env": {
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
@@ -5,6 +5,7 @@ import { preparePrompt } from "./prepare-prompt";
|
||||
import { runClaude } from "./run-claude";
|
||||
import { setupClaudeCodeSettings } from "./setup-claude-code-settings";
|
||||
import { validateEnvironmentVariables } from "./validate-env";
|
||||
import { installPlugins } from "./install-plugins";
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
@@ -15,6 +16,12 @@ async function run() {
|
||||
undefined, // homeDir
|
||||
);
|
||||
|
||||
// Install plugins if specified
|
||||
await installPlugins(
|
||||
process.env.INPUT_PLUGINS,
|
||||
process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE || "claude",
|
||||
);
|
||||
|
||||
const promptConfig = await preparePrompt({
|
||||
prompt: process.env.INPUT_PROMPT || "",
|
||||
promptFile: process.env.INPUT_PROMPT_FILE || "",
|
||||
|
||||
80
base-action/src/install-plugins.ts
Normal file
80
base-action/src/install-plugins.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { spawn } from "child_process";
|
||||
|
||||
// Declare console as global for TypeScript
|
||||
declare const console: {
|
||||
log: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a comma-separated list of plugin names and returns an array of trimmed plugin names
|
||||
*/
|
||||
export function parsePlugins(pluginsInput: string | undefined): string[] {
|
||||
if (!pluginsInput || pluginsInput.trim() === "") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return pluginsInput
|
||||
.split(",")
|
||||
.map((plugin) => plugin.trim())
|
||||
.filter((plugin) => plugin.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a single Claude Code plugin
|
||||
*/
|
||||
export async function installPlugin(
|
||||
pluginName: string,
|
||||
claudeExecutable: string = "claude",
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const process = spawn(claudeExecutable, ["plugin", "install", pluginName], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
process.on("close", (code: number | null) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(
|
||||
new Error(
|
||||
`Failed to install plugin '${pluginName}' (exit code: ${code})`,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
process.on("error", (err: Error) => {
|
||||
reject(
|
||||
new Error(`Failed to install plugin '${pluginName}': ${err.message}`),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs Claude Code plugins from a comma-separated list
|
||||
*/
|
||||
export async function installPlugins(
|
||||
pluginsInput: string | undefined,
|
||||
claudeExecutable: string = "claude",
|
||||
): Promise<void> {
|
||||
const plugins = parsePlugins(pluginsInput);
|
||||
|
||||
if (plugins.length === 0) {
|
||||
console.log("No plugins to install");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Installing ${plugins.length} plugin(s)...`);
|
||||
|
||||
for (const plugin of plugins) {
|
||||
console.log(`Installing plugin: ${plugin}`);
|
||||
await installPlugin(plugin, claudeExecutable);
|
||||
console.log(`✓ Successfully installed: ${plugin}`);
|
||||
}
|
||||
|
||||
console.log("All plugins installed successfully");
|
||||
}
|
||||
84
base-action/test/install-plugins.test.ts
Normal file
84
base-action/test/install-plugins.test.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { parsePlugins } from "../src/install-plugins";
|
||||
|
||||
describe("parsePlugins", () => {
|
||||
test("should return empty array for undefined input", () => {
|
||||
expect(parsePlugins(undefined)).toEqual([]);
|
||||
});
|
||||
|
||||
test("should return empty array for empty string", () => {
|
||||
expect(parsePlugins("")).toEqual([]);
|
||||
});
|
||||
|
||||
test("should return empty array for whitespace-only string", () => {
|
||||
expect(parsePlugins(" \n\t ")).toEqual([]);
|
||||
});
|
||||
|
||||
test("should parse single plugin", () => {
|
||||
expect(parsePlugins("feature-dev")).toEqual(["feature-dev"]);
|
||||
});
|
||||
|
||||
test("should parse multiple plugins", () => {
|
||||
expect(parsePlugins("feature-dev,test-coverage-reviewer")).toEqual([
|
||||
"feature-dev",
|
||||
"test-coverage-reviewer",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should trim whitespace around plugin names", () => {
|
||||
expect(parsePlugins(" feature-dev , test-coverage-reviewer ")).toEqual([
|
||||
"feature-dev",
|
||||
"test-coverage-reviewer",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should handle spaces between commas", () => {
|
||||
expect(
|
||||
parsePlugins(
|
||||
"feature-dev, test-coverage-reviewer, code-quality-reviewer",
|
||||
),
|
||||
).toEqual([
|
||||
"feature-dev",
|
||||
"test-coverage-reviewer",
|
||||
"code-quality-reviewer",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should filter out empty values from consecutive commas", () => {
|
||||
expect(parsePlugins("feature-dev,,test-coverage-reviewer")).toEqual([
|
||||
"feature-dev",
|
||||
"test-coverage-reviewer",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should handle trailing comma", () => {
|
||||
expect(parsePlugins("feature-dev,test-coverage-reviewer,")).toEqual([
|
||||
"feature-dev",
|
||||
"test-coverage-reviewer",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should handle leading comma", () => {
|
||||
expect(parsePlugins(",feature-dev,test-coverage-reviewer")).toEqual([
|
||||
"feature-dev",
|
||||
"test-coverage-reviewer",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should handle plugins with special characters", () => {
|
||||
expect(parsePlugins("@scope/plugin-name,plugin-name-2")).toEqual([
|
||||
"@scope/plugin-name",
|
||||
"plugin-name-2",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should handle complex whitespace patterns", () => {
|
||||
expect(
|
||||
parsePlugins(
|
||||
"\n feature-dev \n,\t test-coverage-reviewer\t, code-quality \n",
|
||||
),
|
||||
).toEqual(["feature-dev", "test-coverage-reviewer", "code-quality"]);
|
||||
});
|
||||
});
|
||||
@@ -127,7 +127,7 @@ For performance, Claude uses shallow clones:
|
||||
If you need full history, you can configure this in your workflow before calling Claude in the `actions/checkout` step.
|
||||
|
||||
```
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
depth: 0 # will fetch full repo history
|
||||
```
|
||||
|
||||
|
||||
@@ -13,13 +13,28 @@
|
||||
- **No Cross-Repository Access**: Each action invocation is limited to the repository where it was triggered
|
||||
- **Limited Scope**: The token cannot access other repositories or perform actions beyond the configured permissions
|
||||
|
||||
## ⚠️ Prompt Injection Risks
|
||||
|
||||
**Beware of potential hidden markdown when tagging Claude on untrusted content.** External contributors may include hidden instructions through HTML comments, invisible characters, hidden attributes, or other techniques. The action sanitizes content by stripping HTML comments, invisible characters, markdown image alt text, hidden HTML attributes, and HTML entities, but new bypass techniques may emerge. We recommend reviewing the raw content of all input coming from external contributors before allowing Claude to process it.
|
||||
|
||||
## GitHub App Permissions
|
||||
|
||||
The [Claude Code GitHub app](https://github.com/apps/claude) requires these permissions:
|
||||
The [Claude Code GitHub app](https://github.com/apps/claude) requests the following permissions:
|
||||
|
||||
- **Pull Requests**: Read and write to create PRs and push changes
|
||||
- **Issues**: Read and write to respond to issues
|
||||
- **Contents**: Read and write to modify repository files
|
||||
### Currently Used Permissions
|
||||
|
||||
- **Contents** (Read & Write): For reading repository files and creating branches
|
||||
- **Pull Requests** (Read & Write): For reading PR data and creating/updating pull requests
|
||||
- **Issues** (Read & Write): For reading issue data and updating issue comments
|
||||
|
||||
### Permissions for Future Features
|
||||
|
||||
The following permissions are requested but not yet actively used. These will enable planned features in future releases:
|
||||
|
||||
- **Discussions** (Read & Write): For interaction with GitHub Discussions
|
||||
- **Actions** (Read): For accessing workflow run data and logs
|
||||
- **Checks** (Read): For reading check run results
|
||||
- **Workflows** (Read & Write): For triggering and managing GitHub Actions workflows
|
||||
|
||||
## Commit Signing
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -153,7 +153,7 @@ jobs:
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -211,7 +211,7 @@ jobs:
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -268,7 +268,7 @@ jobs:
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -344,7 +344,7 @@ jobs:
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -456,7 +456,7 @@ jobs:
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
@@ -513,7 +513,7 @@ jobs:
|
||||
security-events: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.workflow_run.head_branch }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
actions: read # Required for Claude to read CI results on PRs
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 2 # Need at least 2 commits to analyze the latest
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ export async function prepareMcpConfig(
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
||||
"-e",
|
||||
"GITHUB_HOST",
|
||||
"ghcr.io/github/github-mcp-server:sha-efef8ae", // https://github.com/github/github-mcp-server/releases/tag/v0.9.0
|
||||
"ghcr.io/github/github-mcp-server:sha-23fa0dd", // https://github.com/github/github-mcp-server/releases/tag/v0.17.1
|
||||
],
|
||||
env: {
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: githubToken,
|
||||
|
||||
Reference in New Issue
Block a user