diff --git a/Dockerfile b/Dockerfile index 493e885..95062d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,56 @@ FROM node:22-slim -# Install runtime dependencies +# System dependencies: +# - curl/ca-certificates: downloads (Claude Code installer, nvm, git-spice) +# - git + ripgrep: Claude Code requirements +# - build-essential, libssl-dev, libreadline-dev, zlib1g-dev: rbenv/ruby-build deps RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ ca-certificates \ git \ ripgrep \ bash \ + build-essential \ + libssl-dev \ + libreadline-dev \ + zlib1g-dev \ + libffi-dev \ + libyaml-dev \ && rm -rf /var/lib/apt/lists/* -# Create a non-root user with a real home directory +# --- rbenv (installed into home volume on first run, via entrypoint) --- +# RBENV_ROOT points into the home volume so the install persists across rebuilds. +ENV RBENV_ROOT=/home/coder/.rbenv +ENV PATH="$RBENV_ROOT/bin:$RBENV_ROOT/shims:$PATH" + +# Initialise rbenv shims for all bash sessions (no-op if not yet installed) +RUN echo '[ -d "$RBENV_ROOT/bin" ] && eval "$(rbenv init - bash)"' >> /etc/bash.bashrc + +# --- nvm (installed into home volume on first run, via entrypoint) --- +# NVM_DIR points into the home volume so the install persists across rebuilds. +ENV NVM_DIR=/home/coder/.nvm + +# Source nvm for all bash sessions (nvm.sh is a no-op if not yet installed) +RUN echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> /etc/bash.bashrc + +# --- git-spice --- +# Releases are named git-spice.Linux-.tar.gz; uname -m gives the right arch directly. +RUN ARCH=$(uname -m) && \ + GS_VERSION=$(curl -fsSL https://api.github.com/repos/abhinav/git-spice/releases/latest \ + | grep '"tag_name"' | sed 's/.*"v\([^"]*\)".*/\1/') && \ + mkdir -p /tmp/gs-install && \ + curl -fsSL "https://github.com/abhinav/git-spice/releases/download/v${GS_VERSION}/git-spice.Linux-${ARCH}.tar.gz" \ + | tar -xz -C /tmp/gs-install && \ + find /tmp/gs-install -maxdepth 1 -type f -executable -exec cp {} /usr/local/bin/gs \; && \ + chmod +x /usr/local/bin/gs && \ + rm -rf /tmp/gs-install + +# --- non-root user --- RUN useradd -m -s /bin/bash -u 1001 coder -# Ensure ~/.local/bin is on PATH (where the native installer puts the binary) -ENV PATH="/home/coder/.local/bin:/home/coder/.claude/bin:${PATH}" +# Claude Code native installer lands in ~/.local/bin or ~/.claude/bin +ENV PATH="/home/coder/.local/bin:/home/coder/.claude/bin:$PATH" -# Copy entrypoint (as root, then lock down) COPY --chmod=755 entrypoint.sh /usr/local/bin/entrypoint.sh USER coder diff --git a/entrypoint.sh b/entrypoint.sh index 24de07e..bc1d370 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,15 +1,42 @@ #!/bin/bash set -e +# Install nvm if not already present in the home volume. +if [ ! -s "$NVM_DIR/nvm.sh" ]; then + echo "nvm not found — running installer..." + curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh | + NVM_DIR="$NVM_DIR" PROFILE=/dev/null bash + echo "nvm installed successfully." +fi +. "$NVM_DIR/nvm.sh" + +# Install rbenv + ruby-build if not already present in the home volume. +if [ ! -d "$RBENV_ROOT/bin" ]; then + echo "rbenv not found — installing..." + git clone --depth=1 https://github.com/rbenv/rbenv.git "$RBENV_ROOT" + git clone --depth=1 https://github.com/rbenv/ruby-build.git "$RBENV_ROOT/plugins/ruby-build" + echo "rbenv installed successfully." +fi +eval "$(rbenv init - bash)" + +# Install nvm if not already present in the home volume. +if [ ! -s "$NVM_DIR/nvm.sh" ]; then + echo "nvm not found — running installer..." + curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh | + NVM_DIR="$NVM_DIR" PROFILE=/dev/null bash + echo "nvm installed successfully." +fi +. "$NVM_DIR/nvm.sh" + # Install Claude Code if not already present in the home volume. # Because the home directory is a volume, this install persists across # container restarts and rebuilds. if ! command -v claude &>/dev/null; then - echo "Claude Code not found — running installer..." - curl -fsSL https://claude.ai/install.sh | bash - echo "Claude Code installed successfully." + echo "Claude Code not found — running installer..." + curl -fsSL https://claude.ai/install.sh | bash + echo "Claude Code installed successfully." else - echo "Claude Code $(claude --version 2>/dev/null || echo '(version unknown)') ready." + echo "Claude Code $(claude --version 2>/dev/null || echo '(version unknown)') ready." fi exec "$@"