From 0d2971c794049856e777f4e8bb5a81623ec632d3 Mon Sep 17 00:00:00 2001 From: Ashwin Bhat Date: Fri, 17 Apr 2026 15:50:46 -0700 Subject: [PATCH] fix: pass install.sh binary path explicitly to Agent SDK (#1235) Agent SDK 0.2.113 dropped vendor/ripgrep and now ships native binaries via per-platform optionalDependencies. Two breakages: - action.yml chmod'd vendor/ripgrep which no longer exists, failing the Install Dependencies step with find exit 1. - The SDK auto-resolves its bundled binary by trying the -musl platform package before the glibc one. bun install does not respect the package.json libc field and installs both on glibc Linux, so the SDK picks the musl binary and spawn fails with ENOENT. Remove the obsolete ripgrep chmod. Make installClaudeCode() return the install.sh binary path and pass it explicitly as pathToClaudeCodeExecutable so the SDK skips auto-resolution entirely. --- action.yml | 4 ---- base-action/src/index.ts | 13 ++++++++++--- src/entrypoints/run.ts | 15 ++++++++------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/action.yml b/action.yml index b6e909a..1079534 100644 --- a/action.yml +++ b/action.yml @@ -194,10 +194,6 @@ runs: run: | cd ${GITHUB_ACTION_PATH} bun install --production - # bun install --production strips execute bits from vendored binaries (bun issue #1140). - # Restore +x on the ripgrep binaries so the Claude Agent SDK can exec them. - find "${GITHUB_ACTION_PATH}/node_modules/@anthropic-ai/claude-agent-sdk/vendor/ripgrep" \ - -name "rg" -type f -exec chmod +x {} \; - name: Install subprocess isolation dependencies # Install subprocess isolation dependencies when processing content from non-write users. diff --git a/base-action/src/index.ts b/base-action/src/index.ts index 970e79d..160e641 100644 --- a/base-action/src/index.ts +++ b/base-action/src/index.ts @@ -11,6 +11,14 @@ async function run() { try { validateEnvironmentVariables(); + // The composite action's "Install Claude Code" step writes the binary to + // ~/.local/bin/claude. Pass that path explicitly so the Agent SDK doesn't + // fall back to its bundled platform package, which bun may resolve to the + // wrong libc variant on Linux. + const claudeExecutable = + process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE || + `${process.env.HOME}/.local/bin/claude`; + await setupClaudeCodeSettings( process.env.INPUT_SETTINGS, undefined, // homeDir @@ -20,7 +28,7 @@ async function run() { await installPlugins( process.env.INPUT_PLUGIN_MARKETPLACES, process.env.INPUT_PLUGINS, - process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE, + claudeExecutable, ); const promptConfig = await preparePrompt({ @@ -38,8 +46,7 @@ async function run() { appendSystemPrompt: process.env.INPUT_APPEND_SYSTEM_PROMPT, fallbackModel: process.env.INPUT_FALLBACK_MODEL, model: process.env.ANTHROPIC_MODEL, - pathToClaudeCodeExecutable: - process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE, + pathToClaudeCodeExecutable: claudeExecutable, showFullOutput: process.env.INPUT_SHOW_FULL_OUTPUT, }); diff --git a/src/entrypoints/run.ts b/src/entrypoints/run.ts index f7578c0..3cae5f2 100644 --- a/src/entrypoints/run.ts +++ b/src/entrypoints/run.ts @@ -43,8 +43,9 @@ import type { ClaudeRunResult } from "../../base-action/src/run-claude-sdk"; /** * Install Claude Code CLI, handling retry logic and custom executable paths. + * Returns the absolute path to the claude executable. */ -async function installClaudeCode(): Promise { +async function installClaudeCode(): Promise { const customExecutable = process.env.PATH_TO_CLAUDE_CODE_EXECUTABLE; if (customExecutable) { if (/[\x00-\x1f\x7f]/.test(customExecutable)) { @@ -61,7 +62,7 @@ async function installClaudeCode(): Promise { } // Also add to current process PATH process.env.PATH = `${claudeDir}:${process.env.PATH}`; - return; + return customExecutable; } const claudeCodeVersion = "2.1.113"; @@ -93,7 +94,7 @@ async function installClaudeCode(): Promise { await appendFile(githubPath, `${homeBin}\n`); } process.env.PATH = `${homeBin}:${process.env.PATH}`; - return; + return `${homeBin}/claude`; } catch (error) { if (attempt === 3) { throw new Error( @@ -104,6 +105,7 @@ async function installClaudeCode(): Promise { await new Promise((resolve) => setTimeout(resolve, 5000)); } } + throw new Error("unreachable"); } /** @@ -220,7 +222,7 @@ async function run() { prepareCompleted = true; // Phase 2: Install Claude Code CLI - await installClaudeCode(); + const claudeExecutable = await installClaudeCode(); // Phase 3: Run Claude (import base-action directly) // Set env vars needed by the base-action code @@ -259,7 +261,7 @@ async function run() { await installPlugins( process.env.INPUT_PLUGIN_MARKETPLACES, process.env.INPUT_PLUGINS, - process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE, + claudeExecutable, ); const promptFile = @@ -274,8 +276,7 @@ async function run() { claudeArgs: prepareResult.claudeArgs, appendSystemPrompt: process.env.APPEND_SYSTEM_PROMPT, model: process.env.ANTHROPIC_MODEL, - pathToClaudeCodeExecutable: - process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE, + pathToClaudeCodeExecutable: claudeExecutable, showFullOutput: process.env.INPUT_SHOW_FULL_OUTPUT, });