fix: temp-fix for not using coder_env to set path due to limitations (#699)
### Summary Temporary workaround for non-deterministic PATH handling when using `coder_env` across multiple modules ([coder/coder#21885](https://github.com/coder/coder/issues/21885)). ### Problem When multiple modules define `coder_env` with the same `name` (e.g., `PATH`), the final value is non-deterministic due to Go map iteration order. This caused PATH overwrites instead of appending, breaking Claude Code discovery in workspaces using multiple modules. ### Solution Replace `coder_env` PATH manipulation with script-based PATH handling: - **Install script**: Exports PATH and adds claude binary directory to shell profiles (`.profile`, `.bashrc`, `.zshrc`, fish) for interactive shell access - **Start script**: Exports PATH at script execution time - **Symlink**: Creates symlink in `CODER_SCRIPT_BIN_DIR` as additional fallback - **Validation**: Prevents invalid configuration where `claude_binary_path` is customized but `install_claude_code=true` (official installer doesn't support custom paths) ### Changes - Removed `coder_env` resource for PATH - Added PATH export to `install.sh` and `start.sh` - Added shell profile modifications for cross-shell compatibility (bash, zsh, fish) - Added variable validation for `claude_binary_path` ### Note This is a temporary fix until [coder/coder#21885](https://github.com/coder/coder/issues/21885) is resolved with a proper `merge_strategy` attribute for `coder_env`. ## Type of Change - [ ] New module - [ ] New template - [X] Bug fix - [ ] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information <!-- Delete this section if not applicable --> **Path:** `registry/coder/modules/claude-code` **New version:** `v4.7.5` **Breaking change:** [ ] Yes [X] No ## Testing & Validation - [X] Tests pass (`bun test`) - [X] Code formatted (`bun fmt`) - [X] Changes tested locally ## Related Issues ([coder/coder#21885](https://github.com/coder/coder/issues/21885))
This commit is contained in:
parent
6ac4d70405
commit
7e3e842aaa
@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
|
|||||||
```tf
|
```tf
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
claude_api_key = "xxxx-xxxxx-xxxx"
|
claude_api_key = "xxxx-xxxxx-xxxx"
|
||||||
@ -47,7 +47,7 @@ By default, when `enable_boundary = true`, the module uses `coder boundary` subc
|
|||||||
```tf
|
```tf
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
enable_boundary = true
|
enable_boundary = true
|
||||||
@ -68,7 +68,7 @@ For tasks integration with AI Bridge, add `enable_aibridge = true` to the [Usage
|
|||||||
```tf
|
```tf
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
enable_aibridge = true
|
enable_aibridge = true
|
||||||
@ -97,7 +97,7 @@ data "coder_task" "me" {}
|
|||||||
|
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
ai_prompt = data.coder_task.me.prompt
|
ai_prompt = data.coder_task.me.prompt
|
||||||
@ -120,7 +120,7 @@ This example shows additional configuration options for version pinning, custom
|
|||||||
```tf
|
```tf
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
|
|||||||
```tf
|
```tf
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
install_claude_code = true
|
install_claude_code = true
|
||||||
@ -198,7 +198,7 @@ variable "claude_code_oauth_token" {
|
|||||||
|
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
claude_code_oauth_token = var.claude_code_oauth_token
|
claude_code_oauth_token = var.claude_code_oauth_token
|
||||||
@ -271,7 +271,7 @@ resource "coder_env" "bedrock_api_key" {
|
|||||||
|
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||||
@ -328,7 +328,7 @@ resource "coder_env" "google_application_credentials" {
|
|||||||
|
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "4.7.4"
|
version = "4.7.5"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
model = "claude-sonnet-4@20250514"
|
model = "claude-sonnet-4@20250514"
|
||||||
|
|||||||
@ -208,6 +208,11 @@ variable "claude_binary_path" {
|
|||||||
type = string
|
type = string
|
||||||
description = "Directory where the Claude Code binary is located. Use this if Claude is pre-installed or installed outside the module to a non-default location."
|
description = "Directory where the Claude Code binary is located. Use this if Claude is pre-installed or installed outside the module to a non-default location."
|
||||||
default = "$HOME/.local/bin"
|
default = "$HOME/.local/bin"
|
||||||
|
|
||||||
|
validation {
|
||||||
|
condition = var.claude_binary_path == "$HOME/.local/bin" || !var.install_claude_code
|
||||||
|
error_message = "Custom claude_binary_path can only be used when install_claude_code is false. The official installer always installs to $HOME/.local/bin and does not support custom paths."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "install_via_npm" {
|
variable "install_via_npm" {
|
||||||
@ -290,18 +295,6 @@ resource "coder_env" "disable_autoupdater" {
|
|||||||
value = "1"
|
value = "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "coder_env" "claude_binary_path" {
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "PATH"
|
|
||||||
value = "${var.claude_binary_path}:$PATH"
|
|
||||||
|
|
||||||
lifecycle {
|
|
||||||
precondition {
|
|
||||||
condition = var.claude_binary_path == "$HOME/.local/bin" || !var.install_claude_code
|
|
||||||
error_message = "Custom claude_binary_path can only be used when install_claude_code is false. The official installer and npm both install to fixed locations."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "anthropic_model" {
|
resource "coder_env" "anthropic_model" {
|
||||||
count = var.model != "" ? 1 : 0
|
count = var.model != "" ? 1 : 0
|
||||||
@ -400,6 +393,7 @@ module "agentapi" {
|
|||||||
ARG_COMPILE_FROM_SOURCE='${var.compile_boundary_from_source}' \
|
ARG_COMPILE_FROM_SOURCE='${var.compile_boundary_from_source}' \
|
||||||
ARG_USE_BOUNDARY_DIRECTLY='${var.use_boundary_directly}' \
|
ARG_USE_BOUNDARY_DIRECTLY='${var.use_boundary_directly}' \
|
||||||
ARG_CODER_HOST='${local.coder_host}' \
|
ARG_CODER_HOST='${local.coder_host}' \
|
||||||
|
ARG_CLAUDE_BINARY_PATH='${var.claude_binary_path}' \
|
||||||
/tmp/start.sh
|
/tmp/start.sh
|
||||||
EOT
|
EOT
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ ARG_CLAUDE_CODE_VERSION=${ARG_CLAUDE_CODE_VERSION:-}
|
|||||||
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
|
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
|
||||||
ARG_INSTALL_CLAUDE_CODE=${ARG_INSTALL_CLAUDE_CODE:-}
|
ARG_INSTALL_CLAUDE_CODE=${ARG_INSTALL_CLAUDE_CODE:-}
|
||||||
ARG_CLAUDE_BINARY_PATH=${ARG_CLAUDE_BINARY_PATH:-"$HOME/.local/bin"}
|
ARG_CLAUDE_BINARY_PATH=${ARG_CLAUDE_BINARY_PATH:-"$HOME/.local/bin"}
|
||||||
|
ARG_CLAUDE_BINARY_PATH=$(eval echo "$ARG_CLAUDE_BINARY_PATH")
|
||||||
ARG_INSTALL_VIA_NPM=${ARG_INSTALL_VIA_NPM:-false}
|
ARG_INSTALL_VIA_NPM=${ARG_INSTALL_VIA_NPM:-false}
|
||||||
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
|
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
|
||||||
ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-}
|
ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-}
|
||||||
@ -21,6 +22,8 @@ ARG_ALLOWED_TOOLS=${ARG_ALLOWED_TOOLS:-}
|
|||||||
ARG_DISALLOWED_TOOLS=${ARG_DISALLOWED_TOOLS:-}
|
ARG_DISALLOWED_TOOLS=${ARG_DISALLOWED_TOOLS:-}
|
||||||
ARG_ENABLE_AIBRIDGE=${ARG_ENABLE_AIBRIDGE:-false}
|
ARG_ENABLE_AIBRIDGE=${ARG_ENABLE_AIBRIDGE:-false}
|
||||||
|
|
||||||
|
export PATH="$ARG_CLAUDE_BINARY_PATH:$PATH"
|
||||||
|
|
||||||
echo "--------------------------------"
|
echo "--------------------------------"
|
||||||
|
|
||||||
printf "ARG_CLAUDE_CODE_VERSION: %s\n" "$ARG_CLAUDE_CODE_VERSION"
|
printf "ARG_CLAUDE_CODE_VERSION: %s\n" "$ARG_CLAUDE_CODE_VERSION"
|
||||||
@ -51,13 +54,28 @@ function add_mcp_servers() {
|
|||||||
done < <(echo "$mcp_json" | jq -r '.mcpServers | to_entries[] | .key, (.value | @json)')
|
done < <(echo "$mcp_json" | jq -r '.mcpServers | to_entries[] | .key, (.value | @json)')
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensure_claude_in_path() {
|
function add_path_to_shell_profiles() {
|
||||||
if [ -z "${CODER_SCRIPT_BIN_DIR:-}" ]; then
|
local path_dir="$1"
|
||||||
echo "CODER_SCRIPT_BIN_DIR not set, skipping PATH setup"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -e "$CODER_SCRIPT_BIN_DIR/claude" ]; then
|
for profile in "$HOME/.profile" "$HOME/.bash_profile" "$HOME/.bashrc" "$HOME/.zprofile" "$HOME/.zshrc"; do
|
||||||
|
if [ -f "$profile" ]; then
|
||||||
|
if ! grep -q "$path_dir" "$profile" 2> /dev/null; then
|
||||||
|
echo "export PATH=\"\$PATH:$path_dir\"" >> "$profile"
|
||||||
|
echo "Added $path_dir to $profile"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
local fish_config="$HOME/.config/fish/config.fish"
|
||||||
|
if [ -f "$fish_config" ]; then
|
||||||
|
if ! grep -q "$path_dir" "$fish_config" 2> /dev/null; then
|
||||||
|
echo "fish_add_path $path_dir" >> "$fish_config"
|
||||||
|
echo "Added $path_dir to $fish_config"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensure_claude_in_path() {
|
||||||
local CLAUDE_BIN=""
|
local CLAUDE_BIN=""
|
||||||
if command -v claude > /dev/null 2>&1; then
|
if command -v claude > /dev/null 2>&1; then
|
||||||
CLAUDE_BIN=$(command -v claude)
|
CLAUDE_BIN=$(command -v claude)
|
||||||
@ -67,23 +85,20 @@ function ensure_claude_in_path() {
|
|||||||
CLAUDE_BIN="$HOME/.local/bin/claude"
|
CLAUDE_BIN="$HOME/.local/bin/claude"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$CLAUDE_BIN" ] && [ -x "$CLAUDE_BIN" ]; then
|
if [ -z "$CLAUDE_BIN" ] || [ ! -x "$CLAUDE_BIN" ]; then
|
||||||
ln -s "$CLAUDE_BIN" "$CODER_SCRIPT_BIN_DIR/claude"
|
echo "Warning: Could not find claude binary"
|
||||||
echo "Created symlink: $CODER_SCRIPT_BIN_DIR/claude -> $CLAUDE_BIN"
|
return
|
||||||
else
|
|
||||||
echo "Warning: Could not find claude binary to symlink"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Claude already available in CODER_SCRIPT_BIN_DIR"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local marker="# Added by claude-code module"
|
local CLAUDE_DIR
|
||||||
for profile in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.profile"; do
|
CLAUDE_DIR=$(dirname "$CLAUDE_BIN")
|
||||||
if [ -f "$profile" ] && ! grep -q "$marker" "$profile" 2> /dev/null; then
|
|
||||||
printf "\n%s\nexport PATH=\"%s:\$PATH\"\n" "$marker" "$CODER_SCRIPT_BIN_DIR" >> "$profile"
|
if [ -n "${CODER_SCRIPT_BIN_DIR:-}" ] && [ ! -e "$CODER_SCRIPT_BIN_DIR/claude" ]; then
|
||||||
echo "Added $CODER_SCRIPT_BIN_DIR to PATH in $profile"
|
ln -s "$CLAUDE_BIN" "$CODER_SCRIPT_BIN_DIR/claude"
|
||||||
|
echo "Created symlink: $CODER_SCRIPT_BIN_DIR/claude -> $CLAUDE_BIN"
|
||||||
fi
|
fi
|
||||||
done
|
|
||||||
|
add_path_to_shell_profiles "$CLAUDE_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_claude_code_cli() {
|
function install_claude_code_cli() {
|
||||||
|
|||||||
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
ARG_CLAUDE_BINARY_PATH=${ARG_CLAUDE_BINARY_PATH:-"$HOME/.local/bin"}
|
||||||
|
ARG_CLAUDE_BINARY_PATH=$(eval echo "$ARG_CLAUDE_BINARY_PATH")
|
||||||
|
|
||||||
|
export PATH="$ARG_CLAUDE_BINARY_PATH:$PATH"
|
||||||
|
|
||||||
command_exists() {
|
command_exists() {
|
||||||
command -v "$1" > /dev/null 2>&1
|
command -v "$1" > /dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user