Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM mcr.microsoft.com/devcontainers/base:jammy

ARG PIXI_VERSION
ARG PIXI_VERSION=v0.62.2

RUN curl -L -o /usr/local/bin/pixi -fsSL --compressed "https://github.com/prefix-dev/pixi/releases/download/${PIXI_VERSION}/pixi-$(uname -m)-unknown-linux-musl" \
&& chmod +x /usr/local/bin/pixi \
Expand All @@ -11,3 +11,6 @@ USER vscode
WORKDIR /home/vscode

RUN echo 'eval "$(pixi completion -s bash)"' >> /home/vscode/.bashrc

# Create .ssh directory with proper permissions for SSH config mounts
RUN mkdir -p /home/vscode/.ssh && chmod 700 /home/vscode/.ssh
41 changes: 8 additions & 33 deletions .devcontainer/claude-code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ These files **must be writable** to enable:

## Usage

### Basic Setup
### Setup

Add this feature to your `devcontainer.json`:

Expand All @@ -63,19 +63,7 @@ Add this feature to your `devcontainer.json`:
}
```

### Recommended Setup (with Node.js)

For better compatibility, include the Node.js feature:

```json
{
"features": {
"ghcr.io/devcontainers/features/node:1": {},
"./claude-code": {}
},
"runArgs": ["--network=host"]
}
```
**Note**: Node.js is automatically installed via the `installsAfter` dependency mechanism - you don't need to explicitly add it to your features.

### Why `--network=host` is Required

Expand Down Expand Up @@ -140,14 +128,14 @@ touch ~/.claude/settings.json

### Container

- **Node.js 18+** and **npm** must be available (will be auto-installed if possible)
- Supported base images: Debian, Ubuntu, Alpine, Fedora, RHEL, CentOS
- **Node.js 18+** and **npm** are automatically installed via the `installsAfter` dependency mechanism
- No manual configuration required

## Assumptions

1. **User**: The feature assumes the container user is `vscode` (standard for Dev Containers)
- Files are mounted to `/home/vscode/.claude/`
- If your container uses a different user, you may need to adjust the mount paths
1. **Container User**: This feature assumes the container user is `vscode` (standard for Dev Containers)
- Configuration files are mounted to `/home/vscode/.claude/`
- If your container uses a different user (e.g., `root`, `codespace`), you'll need to customize the mounts in your `devcontainer.json`

2. **HOME Environment Variable**: Must be set on the host machine (standard on Unix systems)

Expand Down Expand Up @@ -385,19 +373,6 @@ Then use both:

## Troubleshooting

### "Node.js not found" error

**Solution**: Add the Node.js feature explicitly:

```json
{
"features": {
"ghcr.io/devcontainers/features/node:1": {},
"./claude-code": {}
}
}
```

### OAuth callback hangs at "Paste code here"

**Problem**: Browser clicks "Authorize" but container never receives the callback.
Expand All @@ -422,7 +397,7 @@ See "Why `--network=host` is Required" section above for details.

### VS Code extensions don't install with `--network=host`

**Known Issue**: [Using runArg network=host prevents extensions from installing](https://github.com/microsoft/vscode-remote-release/issues/9212)
**Known Issue**: [Using runArgs network=host prevents extensions from installing](https://github.com/microsoft/vscode-remote-release/issues/9212)

**Workarounds:**
1. **Rebuild without runArgs first**, let extensions install, then add runArgs (extensions persist)
Expand Down
3 changes: 3 additions & 0 deletions .devcontainer/claude-code/devcontainer-feature.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"containerEnv": {
"CLAUDE_CONFIG_DIR": "/home/vscode/.claude"
},
"dependsOn": [
"ghcr.io/devcontainers/features/node"
],
"installsAfter": [
"ghcr.io/devcontainers/features/node"
],
Expand Down
158 changes: 39 additions & 119 deletions .devcontainer/claude-code/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,95 +5,27 @@ set -eu
# Based on: https://github.com/anthropics/devcontainer-features/pull/25
# Combines CLI installation with configuration directory setup

# Function to detect the package manager and OS type
detect_package_manager() {
for pm in apt-get apk dnf yum; do
if command -v $pm >/dev/null; then
case $pm in
apt-get) echo "apt" ;;
*) echo "$pm" ;;
esac
return 0
fi
done
echo "unknown"
return 1
}
# Function to install Claude Code CLI
install_claude_code() {
echo "Installing Claude Code CLI globally..."

# Function to install packages using the appropriate package manager
install_packages() {
local pkg_manager="$1"
shift
local packages="$@"

case "$pkg_manager" in
apt)
apt-get update
apt-get install -y $packages
;;
apk)
apk add --no-cache $packages
;;
dnf|yum)
$pkg_manager install -y $packages
;;
*)
echo "WARNING: Unsupported package manager. Cannot install packages: $packages"
return 1
;;
esac

return 0
}
# Verify Node.js and npm are available
if ! command -v node >/dev/null || ! command -v npm >/dev/null; then
cat <<EOF

# Function to install Node.js
install_nodejs() {
local pkg_manager="$1"

echo "Installing Node.js using $pkg_manager..."

case "$pkg_manager" in
apt)
# Debian/Ubuntu - install more recent Node.js LTS
install_packages apt "ca-certificates curl gnupg"
mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
apt-get update
apt-get install -y nodejs
;;
apk)
# Alpine
install_packages apk "nodejs npm"
;;
dnf)
# Fedora/RHEL
install_packages dnf "nodejs npm"
;;
yum)
# CentOS/RHEL
curl -sL https://rpm.nodesource.com/setup_18.x | bash -
yum install -y nodejs
;;
*)
echo "ERROR: Unsupported package manager for Node.js installation"
return 1
;;
esac
ERROR: Node.js and npm are required but not found!

# Verify installation
if command -v node >/dev/null && command -v npm >/dev/null; then
echo "Successfully installed Node.js and npm"
return 0
else
echo "Failed to install Node.js and npm"
return 1
fi
}
This should not happen as the Node.js feature is automatically installed
via the 'installsAfter' mechanism in devcontainer-feature.json.

# Function to install Claude Code CLI
install_claude_code() {
echo "Installing Claude Code CLI globally..."
Please check:
1. The devcontainer feature specification is correct
2. The Node.js feature (ghcr.io/devcontainers/features/node) is available
3. Your devcontainer build logs for errors

EOF
exit 1
fi

# Install with npm
npm install -g @anthropic-ai/claude-code
Expand All @@ -116,9 +48,28 @@ create_claude_directories() {
echo "Creating Claude configuration directories..."

# Determine the target user's home directory
# $_REMOTE_USER is set by devcontainer, fallback to 'vscode' or current user
local target_home="${_REMOTE_USER_HOME:-/home/${_REMOTE_USER:-vscode}}"
# $_REMOTE_USER is set by devcontainer, fallback to 'vscode'
local target_user="${_REMOTE_USER:-vscode}"
local target_home="${_REMOTE_USER_HOME:-/home/${target_user}}"

# Be defensive: if the resolved home does not exist, fall back to $HOME,
# then to /home/${target_user}. If neither is available, fail clearly.
if [ ! -d "$target_home" ]; then
if [ -n "${HOME:-}" ] && [ -d "$HOME" ]; then
echo "Warning: target_home '$target_home' does not exist, falling back to \$HOME: $HOME" >&2
target_home="$HOME"
elif [ -d "/home/${target_user}" ]; then
echo "Warning: target_home '$target_home' does not exist, falling back to /home/${target_user}" >&2
target_home="/home/${target_user}"
else
echo "Error: No suitable home directory found for '${target_user}'. Tried:" >&2
echo " - _REMOTE_USER_HOME='${_REMOTE_USER_HOME:-}'" >&2
echo " - \$HOME='${HOME:-}'" >&2
echo " - /home/${target_user}" >&2
echo "Please set _REMOTE_USER_HOME to a valid, writable directory." >&2
exit 1
fi
fi

echo "Target home directory: $target_home"
echo "Target user: $target_user"
Expand Down Expand Up @@ -152,44 +103,13 @@ create_claude_directories() {
echo "Claude directories created successfully"
}

# Print error message about requiring Node.js feature
print_nodejs_requirement() {
cat <<EOF

ERROR: Node.js and npm are required but could not be installed!
Please add the Node.js feature to your devcontainer.json:

"features": {
"ghcr.io/devcontainers/features/node:1": {},
"./claude-code": {}
}

EOF
exit 1
}

# Main script starts here
main() {
echo "========================================="
echo "Activating feature 'claude-code' (local)"
echo "========================================="

# Detect package manager
PKG_MANAGER=$(detect_package_manager)
echo "Detected package manager: $PKG_MANAGER"

# Check if Node.js and npm are available
if ! command -v node >/dev/null || ! command -v npm >/dev/null; then
echo "Node.js or npm not found, attempting to install automatically..."
install_nodejs "$PKG_MANAGER" || print_nodejs_requirement
else
echo "Node.js and npm are already installed"
node --version
npm --version
fi

# Install Claude Code CLI
# Check if already installed to make this idempotent
# Install Claude Code CLI (or verify it's already installed)
if command -v claude >/dev/null; then
echo "Claude Code CLI is already installed"
claude --version
Expand Down
12 changes: 6 additions & 6 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
"name": "python_template",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"args": {
"PIXI_VERSION": "v0.62.2"
}
"context": ".."
},
"customizations": {
"vscode": {
Expand All @@ -21,8 +18,9 @@
}
},
"features": {
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/devcontainers/features/node":{},
"./claude-code": {}

// "ghcr.io/devcontainers/features/docker-in-docker:2": {}
// "ghcr.io/devcontainers/features/common-utils:2": {},
// "ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {},
Expand All @@ -38,7 +36,9 @@
"XDG_DATA_HOME": "/home/vscode/.local/share"
},
"mounts": [
"source=${localWorkspaceFolderBasename}-pixi,target=${containerWorkspaceFolder}/.pixi,type=volume"
"source=${localWorkspaceFolderBasename}-pixi,target=${containerWorkspaceFolder}/.pixi,type=volume",
"source=${localEnv:HOME}/.ssh/known_hosts,target=/home/vscode/.ssh/known_hosts,type=bind,ro",
"source=${localEnv:HOME}/.ssh/config,target=/home/vscode/.ssh/config,type=bind,ro"
],
"postCreateCommand": "sudo chown vscode .pixi && pixi install"
}
17 changes: 7 additions & 10 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
{
"recommendations": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.pylint",
"njpwerner.autodocstring",
"charliermarsh.ruff",
"mhutchie.git-graph",
"tamasfe.even-better-toml",
"ms-azuretools.vscode-docker",
"ryanluker.vscode-coverage-gutters",
"jjjermiah.pixi-vscode"
"tamasfe.even-better-toml",
"ms-python.python",
"ms-python.vscode-pylance",
"jjjermiah.pixi-vscode",
"charliermarsh.ruff",
"tamasfe.even-better-toml",
"mhutchie.git-graph"
]
}
Loading