Compare commits

...
Author SHA1 Message Date
Ashwin Bhat ece24efbb4 debug: add logging to credential clearing 2026-02-15 15:00:51 -08:00
Ashwin Bhat fea6d7077e fix: clear actions/checkout includeIf credential configs
actions/checkout now stores git credentials in external config files
via includeIf instead of directly in .git/config. Our configureGitAuth
was only unsetting extraheader from local config, missing the included
files. This caused git push to use the stale github-actions[bot] token
instead of the Claude app token.

Now we enumerate all config origins and clear extraheader entries from
any included credential files.
2026-02-15 14:54:33 -08:00
Ashwin BhatandGitHub 68cfeead18 Revert "fix: replace deprecated :* with modern * wildcard in git permissions (#929)" (#949)
This reverts commit 1bb0e7464b.
2026-02-15 14:39:25 -08:00
Ashwin BhatandGitHub f5088835af Fix stale claudeCodeVersion in run.ts and update bump automation (#943) 2026-02-13 15:12:56 -08:00
GitHub Actions ea36d6abde chore: bump Claude Code to 2.1.42 and Agent SDK to 0.2.42 2026-02-13 19:55:13 +00:00
c22f7c3f9d revert: undo PR checkout fork support and unique branch naming (#937)
Reverts the following commits:
- f669191 fix: use unique local branch names for PR checkout to avoid conflicts (#931)
- 21e3fe0 Fix PR checkout to support fork PRs (#851)

Simplifies PR branch checkout back to using headRefName directly instead
of the pr-{number} local branch naming scheme introduced in #931 and the
GitHub pull ref fetch approach introduced in #851.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 09:45:01 -08:00
11 changed files with 87 additions and 60 deletions
+1 -1
View File
@@ -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..."
+2 -2
View File
@@ -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=="],
+1 -1
View File
@@ -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": {
+2 -2
View File
@@ -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
View File
@@ -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",
+7 -7
View File
@@ -57,13 +57,13 @@ export function buildAllowedToolsString(
} else {
// When not using commit signing, add specific Bash git commands
baseTools.push(
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git push *)",
"Bash(git status *)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git rm *)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git rm:*)",
);
}
+1 -1
View File
@@ -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++) {
+6 -21
View File
@@ -151,10 +151,7 @@ export async function setupBranch(
// Handle open PR: Checkout the PR branch
console.log("This is an open PR, checking out PR branch...");
const originalBranchName = prData.headRefName;
// Use unique local branch name to avoid conflicts with existing branches
// Format: pr-{number} ensures uniqueness since PR numbers are unique per repo
const localBranchName = `pr-${entityNumber}`;
const branchName = prData.headRefName;
// Determine optimal fetch depth based on PR commit count, with a minimum of 20
const commitCount = prData.commits.totalCount;
@@ -163,28 +160,16 @@ export async function setupBranch(
console.log(
`PR #${entityNumber}: ${commitCount} commits, using fetch depth ${fetchDepth}`,
);
console.log(
`Fetching PR branch '${originalBranchName}' as local branch '${localBranchName}'`,
);
// Validate branch names before use to prevent command injection
validateBranchName(localBranchName);
validateBranchName(branchName);
// 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
// The local branch name uses pr-{number} format to ensure uniqueness
execGit([
"fetch",
"origin",
`--depth=${fetchDepth}`,
`pull/${entityNumber}/head:${localBranchName}`,
]);
execGit(["checkout", localBranchName, "--"]);
execGit(["fetch", "origin", `--depth=${fetchDepth}`, branchName]);
execGit(["checkout", branchName, "--"]);
console.log(
`Successfully checked out PR #${entityNumber} as local branch '${localBranchName}'`,
);
console.log(`Successfully checked out PR branch for PR #${entityNumber}`);
// For open PRs, we need to get the base branch of the PR
const baseBranch = prData.baseRefName;
@@ -192,7 +177,7 @@ export async function setupBranch(
return {
baseBranch,
currentBranch: localBranchName,
currentBranch: branchName,
};
}
}
+46 -4
View File
@@ -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
+7 -7
View File
@@ -135,13 +135,13 @@ export async function prepareTagMode({
// SSH signing still uses git CLI, just with signing enabled
if (!useApiCommitSigning) {
tagModeTools.push(
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git push *)",
"Bash(git status *)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git rm *)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git rm:*)",
);
} else {
// When using API commit signing, use MCP file ops tools
+13 -13
View File
@@ -894,9 +894,9 @@ describe("buildAllowedToolsString", () => {
expect(result).toContain("Write");
// Default is no commit signing, so should have specific Bash git commands
expect(result).toContain("Bash(git add *)");
expect(result).toContain("Bash(git commit *)");
expect(result).toContain("Bash(git push *)");
expect(result).toContain("Bash(git add:*)");
expect(result).toContain("Bash(git commit:*)");
expect(result).toContain("Bash(git push:*)");
expect(result).toContain("mcp__github_comment__update_claude_comment");
// Should not have commit signing tools
@@ -916,8 +916,8 @@ describe("buildAllowedToolsString", () => {
expect(result).toContain("Write");
// Should have specific Bash git commands for non-signing mode
expect(result).toContain("Bash(git add *)");
expect(result).toContain("Bash(git commit *)");
expect(result).toContain("Bash(git add:*)");
expect(result).toContain("Bash(git commit:*)");
expect(result).toContain("mcp__github_comment__update_claude_comment");
// Should not have commit signing tools
@@ -1009,13 +1009,13 @@ describe("buildAllowedToolsString", () => {
expect(result).toContain("Write");
// Specific Bash git commands should be included
expect(result).toContain("Bash(git add *)");
expect(result).toContain("Bash(git commit *)");
expect(result).toContain("Bash(git push *)");
expect(result).toContain("Bash(git status *)");
expect(result).toContain("Bash(git diff *)");
expect(result).toContain("Bash(git log *)");
expect(result).toContain("Bash(git rm *)");
expect(result).toContain("Bash(git add:*)");
expect(result).toContain("Bash(git commit:*)");
expect(result).toContain("Bash(git push:*)");
expect(result).toContain("Bash(git status:*)");
expect(result).toContain("Bash(git diff:*)");
expect(result).toContain("Bash(git log:*)");
expect(result).toContain("Bash(git rm:*)");
// Comment tool from minimal server should be included
expect(result).toContain("mcp__github_comment__update_claude_comment");
@@ -1031,7 +1031,7 @@ describe("buildAllowedToolsString", () => {
// Base tools should be present
expect(result).toContain("Edit");
expect(result).toContain("Bash(git add *)");
expect(result).toContain("Bash(git add:*)");
// Custom tools should be included
expect(result).toContain("CustomTool1");