mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-09-21 16:40:34 +08:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ece24efbb4 | ||
|
|
fea6d7077e | ||
|
|
68cfeead18 | ||
|
|
f5088835af | ||
|
|
ea36d6abde | ||
|
|
c22f7c3f9d | ||
|
|
f669191d7d | ||
|
|
8c383c5de3 | ||
|
|
1bb0e7464b |
@@ -124,7 +124,7 @@ runs:
|
||||
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||
run: |
|
||||
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
||||
CLAUDE_CODE_VERSION="2.1.39"
|
||||
CLAUDE_CODE_VERSION="2.1.42"
|
||||
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
||||
for attempt in 1 2 3; do
|
||||
echo "Installation attempt $attempt..."
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"name": "@anthropic-ai/claude-code-base-action",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.39",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.42",
|
||||
"shell-quote": "^1.8.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.39", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-wR1TBH62X6E1YwRnWa+A2Eau7AfpTWtfpnwQXO3yRY31FtmzOjPkQb93hbF3AkT0WL7YF9mxBBwJKUa3ZEc5+A=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.42", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-/CugP7AjP57Dqtl2sbsDtxdbpQoPKIhjyF5WrTViGu4NHQdM+UikrRs4MhZ2jeotiC5R7iK9ZUN9SiBgcZ8oLw=="],
|
||||
|
||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.39",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.42",
|
||||
"shell-quote": "^1.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.39",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.42",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.39", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-wR1TBH62X6E1YwRnWa+A2Eau7AfpTWtfpnwQXO3yRY31FtmzOjPkQb93hbF3AkT0WL7YF9mxBBwJKUa3ZEc5+A=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.42", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-/CugP7AjP57Dqtl2sbsDtxdbpQoPKIhjyF5WrTViGu4NHQdM+UikrRs4MhZ2jeotiC5R7iK9ZUN9SiBgcZ8oLw=="],
|
||||
|
||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.39",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.42",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
|
||||
@@ -51,7 +51,7 @@ async function installClaudeCode(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const claudeCodeVersion = "2.1.31";
|
||||
const claudeCodeVersion = "2.1.42";
|
||||
console.log(`Installing Claude Code v${claudeCodeVersion}...`);
|
||||
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
|
||||
@@ -166,13 +166,7 @@ export async function setupBranch(
|
||||
|
||||
// Execute git commands to checkout PR branch (dynamic depth based on PR size)
|
||||
// Using execFileSync instead of shell template literals for security
|
||||
// Use GitHub's PR ref which works for both same-repo and fork PRs
|
||||
execGit([
|
||||
"fetch",
|
||||
"origin",
|
||||
`--depth=${fetchDepth}`,
|
||||
`pull/${entityNumber}/head:${branchName}`,
|
||||
]);
|
||||
execGit(["fetch", "origin", `--depth=${fetchDepth}`, branchName]);
|
||||
execGit(["checkout", branchName, "--"]);
|
||||
|
||||
console.log(`Successfully checked out PR branch for PR #${entityNumber}`);
|
||||
|
||||
@@ -42,13 +42,55 @@ export async function configureGitAuth(
|
||||
await $`git config user.email "${botId}+${botName}@${noreplyDomain}"`;
|
||||
console.log(`✓ Set git user as ${botName}`);
|
||||
|
||||
// Remove the authorization header that actions/checkout sets
|
||||
// Remove the authorization header that actions/checkout sets.
|
||||
// Newer versions of actions/checkout store credentials in external config files
|
||||
// referenced via includeIf, so --unset-all on local config may not find them.
|
||||
// We clear both: local config entries and any includeIf credential files.
|
||||
console.log("Removing existing git authentication headers...");
|
||||
try {
|
||||
await $`git config --unset-all http.${GITHUB_SERVER_URL}/.extraheader`;
|
||||
console.log("✓ Removed existing authentication headers");
|
||||
} catch (e) {
|
||||
console.log("No existing authentication headers to remove");
|
||||
console.log("✓ Removed existing authentication headers from local config");
|
||||
} catch {
|
||||
console.log("No existing authentication headers in local config to remove");
|
||||
}
|
||||
|
||||
// Clear extraheader from includeIf credential config files set by actions/checkout
|
||||
try {
|
||||
const configOutput = await $`git config --list --show-origin`.text();
|
||||
const extraheaderPattern = `http.${GITHUB_SERVER_URL}/.extraheader=`;
|
||||
console.log(
|
||||
`Scanning git config origins for extraheader pattern: ${extraheaderPattern}`,
|
||||
);
|
||||
let foundExternalEntries = false;
|
||||
for (const line of configOutput.split("\n")) {
|
||||
if (!line.includes(extraheaderPattern)) continue;
|
||||
// Format: "file:<path>\t<key>=<value>"
|
||||
const match = line.match(/^file:(.+?)\t/);
|
||||
if (!match) {
|
||||
console.log(
|
||||
`Found extraheader but could not parse origin: ${line.substring(0, 80)}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const configFile = match[1]!;
|
||||
// Skip the local .git/config — we already handled that above
|
||||
if (configFile.endsWith("/.git/config")) {
|
||||
console.log("Skipping .git/config (already handled)");
|
||||
continue;
|
||||
}
|
||||
foundExternalEntries = true;
|
||||
try {
|
||||
await $`git config --file ${configFile} --unset-all http.${GITHUB_SERVER_URL}/.extraheader`;
|
||||
console.log(`✓ Removed authentication headers from ${configFile}`);
|
||||
} catch {
|
||||
console.log(`Could not unset extraheader from ${configFile}`);
|
||||
}
|
||||
}
|
||||
if (!foundExternalEntries) {
|
||||
console.log("No external credential config files found");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Could not enumerate git config origins: ${error}`);
|
||||
}
|
||||
|
||||
// Update the remote URL to include the token for authentication
|
||||
|
||||
@@ -177,25 +177,27 @@ export async function prepareMcpConfig(
|
||||
if (!actuallyHasPermission) {
|
||||
core.warning(
|
||||
"The github_ci MCP server requires 'actions: read' permission. " +
|
||||
"Please ensure your GitHub token has this permission. " +
|
||||
"Skipping CI server installation. " +
|
||||
"To enable CI status checks, add 'actions: read' to your workflow permissions. " +
|
||||
"See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token",
|
||||
);
|
||||
} else {
|
||||
baseMcpConfig.mcpServers.github_ci = {
|
||||
command: "bun",
|
||||
args: [
|
||||
"run",
|
||||
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-actions-server.ts`,
|
||||
],
|
||||
env: {
|
||||
// Use workflow github token, not app token
|
||||
GITHUB_TOKEN: process.env.DEFAULT_WORKFLOW_TOKEN,
|
||||
REPO_OWNER: owner,
|
||||
REPO_NAME: repo,
|
||||
PR_NUMBER: context.entityNumber?.toString() || "",
|
||||
RUNNER_TEMP: process.env.RUNNER_TEMP || "/tmp",
|
||||
},
|
||||
};
|
||||
}
|
||||
baseMcpConfig.mcpServers.github_ci = {
|
||||
command: "bun",
|
||||
args: [
|
||||
"run",
|
||||
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-actions-server.ts`,
|
||||
],
|
||||
env: {
|
||||
// Use workflow github token, not app token
|
||||
GITHUB_TOKEN: process.env.DEFAULT_WORKFLOW_TOKEN,
|
||||
REPO_OWNER: owner,
|
||||
REPO_NAME: repo,
|
||||
PR_NUMBER: context.entityNumber?.toString() || "",
|
||||
RUNNER_TEMP: process.env.RUNNER_TEMP || "/tmp",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (hasGitHubMcpTools) {
|
||||
|
||||
@@ -9,6 +9,7 @@ describe("prepareMcpConfig", () => {
|
||||
let consoleWarningSpy: any;
|
||||
let setFailedSpy: any;
|
||||
let processExitSpy: any;
|
||||
let fetchSpy: any;
|
||||
|
||||
// Create a mock context for tests
|
||||
const mockContext: ParsedGitHubContext = {
|
||||
@@ -66,6 +67,10 @@ describe("prepareMcpConfig", () => {
|
||||
processExitSpy = spyOn(process, "exit").mockImplementation(() => {
|
||||
throw new Error("Process exit");
|
||||
});
|
||||
// Mock fetch so checkActionsReadPermission succeeds (returns 200 for actions API)
|
||||
fetchSpy = spyOn(global, "fetch").mockResolvedValue(
|
||||
new Response(JSON.stringify({ workflow_runs: [] }), { status: 200 }),
|
||||
);
|
||||
|
||||
// Set up required environment variables
|
||||
if (!process.env.GITHUB_ACTION_PATH) {
|
||||
@@ -78,6 +83,7 @@ describe("prepareMcpConfig", () => {
|
||||
consoleWarningSpy.mockRestore();
|
||||
setFailedSpy.mockRestore();
|
||||
processExitSpy.mockRestore();
|
||||
fetchSpy.mockRestore();
|
||||
});
|
||||
|
||||
test("should return comment server when commit signing is disabled", async () => {
|
||||
@@ -263,6 +269,33 @@ describe("prepareMcpConfig", () => {
|
||||
expect(parsed.mcpServers.github_ci).not.toBeDefined();
|
||||
});
|
||||
|
||||
test("should not include github_ci server when actions:read permission is missing", async () => {
|
||||
process.env.DEFAULT_WORKFLOW_TOKEN = "workflow-token";
|
||||
// Simulate 403 from actions API
|
||||
fetchSpy.mockResolvedValue(
|
||||
new Response(
|
||||
JSON.stringify({ message: "Resource not accessible by integration" }),
|
||||
{ status: 403 },
|
||||
),
|
||||
);
|
||||
|
||||
const result = await prepareMcpConfig({
|
||||
githubToken: "test-token",
|
||||
owner: "test-owner",
|
||||
repo: "test-repo",
|
||||
branch: "test-branch",
|
||||
baseBranch: "main",
|
||||
allowedTools: [],
|
||||
mode: "tag",
|
||||
context: mockPRContext,
|
||||
});
|
||||
|
||||
const parsed = JSON.parse(result);
|
||||
expect(parsed.mcpServers.github_ci).not.toBeDefined();
|
||||
|
||||
delete process.env.DEFAULT_WORKFLOW_TOKEN;
|
||||
});
|
||||
|
||||
test("should not include github_ci server when DEFAULT_WORKFLOW_TOKEN is missing", async () => {
|
||||
delete process.env.DEFAULT_WORKFLOW_TOKEN;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user