3f03d65e73
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
80 lines
3.5 KiB
Docker
80 lines
3.5 KiB
Docker
# claude-bottle container image.
|
|
#
|
|
# Goal: a small, cache-friendly base that ships claude-code (the
|
|
# `@anthropic-ai/claude-code` npm package, CLI name `claude`) ready to run
|
|
# interactively. The container is ephemeral; per PRD 0001 v1 the host
|
|
# filesystem is not mounted in.
|
|
#
|
|
# Layer ordering is deliberate: the npm install lives in its own layer so
|
|
# changes to the rest of the repo (or to the CMD) don't bust it.
|
|
|
|
# Current Node LTS; slim variant keeps the image small while still
|
|
# providing apt-get for any future additions.
|
|
FROM node:22-slim
|
|
|
|
# Install runtime system deps. claude-code shells out to git for several
|
|
# features (status checks, commits, PR creation) — without git in the
|
|
# image, those features fail in surprising ways once the user does any
|
|
# real work. ca-certificates is already in the slim base; listed for
|
|
# clarity in case the base ever drops it. socat is the privileged
|
|
# forwarder for the in-container ssh-agent (see lib/ssh.sh): the agent
|
|
# runs as root and rejects non-root connections, so socat sits between
|
|
# node and the agent socket.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends git ca-certificates openssh-client socat \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install claude-code globally. Pinned to the version verified in the v1
|
|
# build (`claude --version` returns 2.1.126). Bump deliberately when
|
|
# rolling forward; an unpinned install would mean rebuilds silently pick
|
|
# up new behavior.
|
|
RUN npm install -g --no-fund --no-audit @anthropic-ai/claude-code@2.1.126 \
|
|
&& npm cache clean --force
|
|
|
|
# Run as a non-root user. The node image already provides a `node` user
|
|
# (uid 1000) with a home directory, which is where claude-code will write
|
|
# its session state.
|
|
USER node
|
|
WORKDIR /home/node
|
|
|
|
# Pre-create the skills directory so PRD 0002's host->container skill
|
|
# copier (scripts/lib/skills.sh) drops files into a path owned by the
|
|
# `node` user. `skills_copy_into` also `mkdir -p`s defensively, but
|
|
# baking it into the image avoids a permission-confusion footgun if a
|
|
# future change to the launcher copies in as a different user.
|
|
RUN mkdir -p /home/node/.claude/skills
|
|
|
|
# Pre-populate ~/.claude.json so claude skips the first-run onboarding
|
|
# screens on every fresh container launch: the theme picker
|
|
# (`hasCompletedOnboarding`), the "trust this folder" dialog
|
|
# (`projects.<cwd>.hasTrustDialogAccepted`), the implicit theme
|
|
# default, and the bypass-permissions-mode warning that fires the
|
|
# first time `--dangerously-skip-permissions` is used in a profile
|
|
# (`bypassPermissionsModeAccepted`). Without these, an ephemeral
|
|
# container shows all four on every start. The fifth screen —
|
|
# "Detected a custom API key in your environment" — only fires on the
|
|
# ANTHROPIC_API_KEY fallback path; under the primary OAuth-token flow
|
|
# (CLAUDE_CODE_OAUTH_TOKEN) it does not appear. When it does fire it
|
|
# is handled at launch time by scripts/lib/auth.sh, which computes
|
|
# the key suffix inside the container so the value never crosses
|
|
# host argv.
|
|
#
|
|
# Heredoc delimiter is unquoted so $HOME expands; no other `$` appears
|
|
# in the body, so this is safe under dash (Docker's default RUN shell).
|
|
RUN cat > "$HOME/.claude.json" <<JSON
|
|
{
|
|
"hasCompletedOnboarding": true,
|
|
"theme": "dark",
|
|
"bypassPermissionsModeAccepted": true,
|
|
"projects": {
|
|
"$HOME": { "hasTrustDialogAccepted": true }
|
|
}
|
|
}
|
|
JSON
|
|
|
|
# Default to an interactive claude session. In the v1 launcher,
|
|
# `scripts/start.sh` runs the container detached and uses `docker exec`
|
|
# to attach a TTY, but this CMD makes `docker run -it claude-bottle` also
|
|
# do something useful for ad-hoc debugging.
|
|
CMD ["claude"]
|