diff --git a/src/claude-code/devcontainer-feature.json b/src/claude-code/devcontainer-feature.json index a633106..cd56552 100644 --- a/src/claude-code/devcontainer-feature.json +++ b/src/claude-code/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "claude-code", - "version": "1.0.2", + "version": "1.1.0", "name": "Claude Code CLI", "description": "Installs the Claude Code CLI on a selectable channel.", "documentationURL": "https://github.com/hansehart/devcontainer-features/tree/main/src/claude-code", @@ -20,6 +20,11 @@ "type": "boolean", "default": true, "description": "Set CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 to suppress telemetry and other non-essential network calls." + }, + "settingsJson": { + "type": "string", + "default": "", + "description": "Claude settings as a JSON object, merged into settings.json." } }, "postCreateCommand": "/usr/local/share/claude-code/init.sh" diff --git a/src/claude-code/init.sh b/src/claude-code/init.sh index 31bc13a..a6db1d1 100644 --- a/src/claude-code/init.sh +++ b/src/claude-code/init.sh @@ -1,7 +1,17 @@ #!/usr/bin/env bash set -euo pipefail -# postCreate hook: create the state dir once the volume is mounted. if [ -r /etc/profile.d/claude-code.sh ]; then . /etc/profile.d/claude-code.sh; fi -[ -n "${CLAUDE_CONFIG_DIR:-}" ] || exit 0 -mkdir -p "${CLAUDE_CONFIG_DIR}" + +# Create the state dir once its volume is mounted. +if [ -n "${CLAUDE_CONFIG_DIR:-}" ]; then mkdir -p "${CLAUDE_CONFIG_DIR}"; fi + +# Merge the requested settings into settings.json (empty leaves it untouched). +req=/usr/local/share/claude-code/requested-settings.json +if [ -s "$req" ]; then + target="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" + mkdir -p "$(dirname "$target")" + prev='{}' + if [ -f "$target" ]; then prev="$(cat "$target")"; fi + printf '%s' "$prev" | jq --argjson add "$(cat "$req")" '. + $add' > "$target" +fi diff --git a/src/claude-code/install.sh b/src/claude-code/install.sh index c480414..b6512c8 100644 --- a/src/claude-code/install.sh +++ b/src/claude-code/install.sh @@ -1,9 +1,10 @@ #!/usr/bin/env bash set -euo pipefail -# Options (uppercased by the CLI): VERSION, STATEDIR, DISABLENONESSENTIALTRAFFIC. +# Options (uppercased by the CLI): VERSION, STATEDIR, DISABLENONESSENTIALTRAFFIC, SETTINGSJSON. STATE_DIR="$STATEDIR" DISABLE_NONESSENTIAL_TRAFFIC="$DISABLENONESSENTIALTRAFFIC" +SETTINGS_JSON="$SETTINGSJSON" export DEBIAN_FRONTEND=noninteractive @@ -11,7 +12,8 @@ export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y --no-install-recommends \ ca-certificates \ - curl + curl \ + jq rm -rf /var/lib/apt/lists/* # Install: run the upstream installer as the dev user so claude lands in ~/.local/bin. @@ -29,9 +31,10 @@ su - "$_REMOTE_USER" -c "curl -fsSL https://claude.ai/install.sh | bash -s -- '$ } > /etc/profile.d/claude-code.sh chmod 0644 /etc/profile.d/claude-code.sh -# Hook: install the create-state-dir hook to run once at container create. +# Hook: install the run-once hook and save the requested settings for it to merge. install -d /usr/local/share/claude-code install -m 0755 "$(dirname "$0")/init.sh" /usr/local/share/claude-code/init.sh +printf '%s' "$SETTINGS_JSON" > /usr/local/share/claude-code/requested-settings.json # Verify: claude resolves on PATH (as the dev user). su - "$_REMOTE_USER" -c "claude --version" diff --git a/test/claude-code/scenarios.json b/test/claude-code/scenarios.json new file mode 100644 index 0000000..2f351dd --- /dev/null +++ b/test/claude-code/scenarios.json @@ -0,0 +1,10 @@ +{ + "settings_json": { + "image": "ubuntu:24.04", + "features": { + "claude-code": { + "settingsJson": "{\\\"env\\\":{\\\"IS_DEMO\\\":\\\"1\\\"}}" + } + } + } +} diff --git a/test/claude-code/settings_json.sh b/test/claude-code/settings_json.sh new file mode 100644 index 0000000..4660953 --- /dev/null +++ b/test/claude-code/settings_json.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +source dev-container-features-test-lib + +# The merge runs at postCreate, so invoke the hook here to apply the baked settings. +/usr/local/share/claude-code/init.sh + +target="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" +check "settings merged" jq -e '.env.IS_DEMO == "1"' "$target" + +reportResults