-
Notifications
You must be signed in to change notification settings - Fork 1
fix: make devcontainer portable across hosts #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Reviewer's GuideSwitches Claude Code devcontainer feature from a Node/npm-based installation to a pixi-based claude-shim installation, simplifies host mounts by using a single ~/.claude bind plus a host init script, and ensures the pixi bin paths are on PATH so the claude CLI works across fresh hosts and different environments. Sequence diagram for devcontainer startup with pixi-based Claude Code installationsequenceDiagram
actor DevUser as Dev_user
participant Host as Host_machine
participant InitHost as init_host_sh
participant DevPod as Devpod
participant Docker as Docker_engine
participant Container as Devcontainer
participant Feature as claude_code_feature_install_sh
participant Pixi as pixi
DevUser->>DevPod: devpod up .
DevPod->>Host: Read devcontainer_json
DevPod->>InitHost: initializeCommand init_host_sh
InitHost->>Host: mkdir -p $HOME/.claude
DevPod->>Docker: Build devcontainer image
Docker->>Container: Create container with bind mount ~/.claude
DevPod->>Container: Start container
Container->>Feature: Run feature install script
Feature->>Container: Resolve target_user and target_home
Feature->>Container: Check claude at target_home/.pixi/bin/claude
alt claude_binary_missing
Feature->>Container: Check command -v pixi
alt pixi_missing
Feature-->>DevUser: Print error and exit
else pixi_available
alt running_as_root_and_target_user_not_root
Feature->>Pixi: su - target_user -c "pixi global install claude-shim"
else non_root_or_target_user_root
Feature->>Pixi: pixi global install claude-shim
end
Feature->>Container: Verify target_home/.pixi/bin/claude exists
end
else claude_binary_present
Feature->>Container: Print existing claude version
end
Feature->>Container: create_claude_directories
Feature->>Container: mkdir -p ~/.claude and subdirs
Feature->>Container: Ensure config files exist
Feature->>Container: chown -R target_user:target_user ~/.claude
Feature-->>DevUser: Print success and usage message
DevUser->>Container: claude --version
Container->>Pixi: Execute claude from pixi bin
Pixi-->>DevUser: Display Claude Code version
Flow diagram for claude-code install.sh with pixi-based installationflowchart TD
A[Start main] --> B[Resolve target_user and target_home]
B --> C{claude at target_home/.pixi/bin/claude exists?}
C -- Yes --> D[Print existing Claude Code version]
D --> H[create_claude_directories]
C -- No --> E{command -v pixi succeeds?}
E -- No --> F[Print pixi required error and exit 1]
E -- Yes --> G{running as root and target_user not root?}
G -- Yes --> G1[Run su - target_user -c 'pixi global install --channel blooop claude-shim']
G -- No --> G2[Run pixi global install --channel blooop claude-shim]
G1 --> I[Verify target_home/.pixi/bin/claude exists]
G2 --> I[Verify target_home/.pixi/bin/claude exists]
I -->|Exists| H[create_claude_directories]
I -->|Missing| J[Print installation failed, exit 1]
H --> H1[mkdir -p ~/.claude and subdirectories]
H1 --> H2[Create .credentials.json and .claude.json if missing]
H2 --> H3[Set chmod 600 on credentials, 644 on config]
H3 --> H4{running as root?}
H4 -- Yes --> H5[chown -R target_user:target_user ~/.claude]
H4 -- No --> H6[Skip chown]
H5 --> K[Print feature activated and usage]
H6 --> K[Print feature activated and usage]
K --> L[End main]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - I've found 1 issue, and left some high level feedback:
- The hardcoded PATH entries for
$HOME/.pixi/envs/claude-shim/binassume a specific pixi global env name; consider deriving the actual bin paths frompixi infoor relying on~/.pixi/binonly to avoid breaking if the env layout or name changes. - The
install.shmessaging about configuration being stored in aclaude-configDocker volume appears inconsistent with the new host~/.claudeinitialization and bind-mount approach; update the user-facing output so it accurately reflects the current storage and persistence behavior.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The hardcoded PATH entries for `$HOME/.pixi/envs/claude-shim/bin` assume a specific pixi global env name; consider deriving the actual bin paths from `pixi info` or relying on `~/.pixi/bin` only to avoid breaking if the env layout or name changes.
- The `install.sh` messaging about configuration being stored in a `claude-config` Docker volume appears inconsistent with the new host `~/.claude` initialization and bind-mount approach; update the user-facing output so it accurately reflects the current storage and persistence behavior.
## Individual Comments
### Comment 1
<location> `.devcontainer/claude-code/install.sh:25-26` </location>
<code_context>
- # Install with npm
- npm install -g @anthropic-ai/claude-code
+ # Determine target user for pixi global install
+ local target_user="${_REMOTE_USER:-vscode}"
+ local target_home="${_REMOTE_USER_HOME:-/home/${target_user}}"
+
+ # Install with pixi global from blooop channel
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Home directory resolution in `install_claude_code` is less defensive than in `create_claude_directories` and can point to a non-existent path.
`create_claude_directories` does a full fallback chain for the home directory (`_REMOTE_USER_HOME` → `$HOME` → `/home/${target_user}`) and errors if none exist, but `install_claude_code` just uses `_REMOTE_USER_HOME` or `/home/${target_user}` without checking the path exists. If `_REMOTE_USER_HOME` is wrong or missing, install will succeed for the user but the verification step and `claude_bin` resolution in `main()` can point at a non-existent directory and falsely report the CLI as missing.
Consider extracting the home-resolution logic into a shared helper (or at least applying the same fallback + existence checks here) so installation and verification both rely on a validated `target_home`.
Suggested implementation:
```
# Determine target user for pixi global install
local target_user="${_REMOTE_USER:-vscode}"
# Resolve target home directory with fallbacks and validation
local target_home="${_REMOTE_USER_HOME:-}"
# If _REMOTE_USER_HOME is not set, try to infer from current user or /home/<user>
if [ -z "${target_home}" ]; then
# If we're already running as the target user and HOME is set, prefer that
if [ "$(id -un 2>/dev/null)" = "${target_user}" ] && [ -n "${HOME}" ]; then
target_home="${HOME}"
# Otherwise fall back to the conventional /home/<user> path if it exists
elif [ -d "/home/${target_user}" ]; then
target_home="/home/${target_user}"
fi
fi
# Ensure we ended up with a valid, existing home directory
if [ -z "${target_home}" ] || [ ! -d "${target_home}" ]; then
echo "Error: could not determine a valid home directory for user '${target_user}'." >&2
echo "Checked _REMOTE_USER_HOME ('${_REMOTE_USER_HOME:-}'), \$HOME ('${HOME:-}'), and /home/${target_user}." >&2
exit 1
fi
# Install with pixi global from blooop channel
```
To fully align installation and verification:
1. Extract this home-resolution logic into a shared helper function (e.g. `resolve_target_home()`) near the top of the script.
2. Update `create_claude_directories` to call that helper instead of duplicating the logic, so both functions rely on the same validated `target_home`.
3. Ensure `main()` and any `claude_bin` / verification logic also call the same helper (or use the same `target_home` variable) rather than recomputing the home path differently.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- Add init-host.sh to create ~/.claude directory on host before container starts - Use bind mount for ~/.claude folder instead of individual file mounts - Switch from npm to pixi for claude installation (claude-shim package) - Remove Node.js dependency from claude-code feature - Add pixi bin paths to .profile for proper PATH setup - Add initializeCommand to devcontainer.json to run host init script - Install pixi automatically if not found in base image - Feature now adds PATH setup to user profile (self-contained) This allows the devcontainer to work on fresh hosts without requiring pre-existing ~/.claude files, while still sharing credentials across all devcontainers. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cb8ede9 to
e9fa5bf
Compare
- Remove hardcoded $HOME/.pixi/envs/claude-shim/bin from PATH in both Dockerfile and install.sh - rely on ~/.pixi/bin only - Add resolve_target_home() helper function for consistent, defensive home directory resolution with proper fallbacks - Update messaging to say "bind-mounted from host" instead of "Docker volume" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add conditional PATH entry for ~/.pixi/envs/claude-shim/bin to work around pixi trampoline bug that fails for bash script executables - Remove claude --version calls during feature install to avoid downloading the Claude binary during container build - Claude binary will now be downloaded on first user invocation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
init-host.shscript to create~/.claudedirectory on host before container startsclaude-shimpackage~/.claudefolder instead of individual file mounts that fail on fresh hosts.profilefor proper PATH setup (bypasses pixi trampoline issue with bash scripts)Changes
.devcontainer/claude-code/init-host.sh(new): Creates~/.claudeon host if missing.devcontainer/claude-code/devcontainer-feature.json: Simplified mounts, removed Node.js dependency.devcontainer/claude-code/install.sh:pixi global install claude-shiminstead of npm.profile.devcontainer/Dockerfile: Adds pixi bin paths to PATH.devcontainer/devcontainer.json: AddsinitializeCommandto run host init scriptTest plan
devpod up .on fresh host with no~/.claudedirectoryclaude --versionreturns2.1.2 (Claude Code)🤖 Generated with Claude Code