refactor(agent-images): use explicit Debian base
tracker-policy-pr / check-pr (pull_request) Successful in 13s
test / integration-docker (pull_request) Successful in 36s
test / unit (pull_request) Successful in 43s
lint / lint (push) Successful in 50s
test / integration-firecracker (pull_request) Failing after 7m18s
test / coverage (pull_request) Has been skipped
test / publish-infra (pull_request) Has been skipped

This commit is contained in:
2026-07-21 17:40:18 +00:00
parent 7e5ffd9674
commit 51b82f80d1
5 changed files with 55 additions and 29 deletions
+12 -8
View File
@@ -8,15 +8,15 @@
# Layer ordering is deliberate: the npm install lives in its own layer so # 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. # 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 # Debian stable is the supported userspace. The provider's Node.js runtime is
# providing apt-get for any future additions. # installed explicitly below rather than inherited from a language image.
FROM node:22-trixie-slim FROM debian:trixie-slim
# Install runtime system deps. claude-code shells out to git for several # Install runtime system deps. claude-code shells out to git for several
# features (status checks, commits, PR creation) — without git in the # features (status checks, commits, PR creation) — without git in the
# image, those features fail in surprising ways once the user does any # image, those features fail in surprising ways once the user does any
# real work. ca-certificates is already in the slim base; listed for # real work. ca-certificates is installed explicitly for TLS clients. curl is
# clarity in case the base ever drops it. curl is here so any # here so any
# HTTPS_PROXY-aware tool (curl itself, plus anything that shells out # HTTPS_PROXY-aware tool (curl itself, plus anything that shells out
# to it) works against egress's bumped TLS without the agent needing # to it) works against egress's bumped TLS without the agent needing
# local DNS. # local DNS.
@@ -30,8 +30,13 @@ RUN apt-get update \
ripgrep \ ripgrep \
iproute2 \ iproute2 \
dnsutils \ dnsutils \
nodejs \
npm \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN groupadd --gid 1000 node \
&& useradd --uid 1000 --gid node --shell /bin/bash --create-home node
# App-specific deps. Python isn't required by claude-code itself # App-specific deps. Python isn't required by claude-code itself
# (claude-code is a Node CLI), but is convenient for the agent to # (claude-code is a Node CLI), but is convenient for the agent to
# shell out to for ad-hoc scripts. Kept on its own layer so it can # shell out to for ad-hoc scripts. Kept on its own layer so it can
@@ -52,9 +57,8 @@ RUN npm install -g --no-fund --no-audit @anthropic-ai/claude-code@2.1.172 \
# suppress bot-bottle's git-gate insteadOf rules. # suppress bot-bottle's git-gate insteadOf rules.
RUN install -d -o node -g node -m 755 /home/node/.config /home/node/.config/git RUN install -d -o node -g node -m 755 /home/node/.config /home/node/.config/git
# Run as a non-root user. The node image already provides a `node` user # Run as the explicitly-created non-root user. Claude Code writes its session
# (uid 1000) with a home directory, which is where claude-code will write # state under this user's home directory.
# its session state.
USER node USER node
WORKDIR /home/node WORKDIR /home/node
+10 -7
View File
@@ -1,9 +1,9 @@
# bot-bottle Codex provider image. # bot-bottle Codex provider image.
# #
# Mirrors the default Claude image shape: Node LTS, git/network tooling, # Mirrors the default Claude image shape: Debian stable, git/network tooling,
# non-root node user, and the provider CLI installed for that user. # a non-root runtime user, and the provider CLI installed for that user.
FROM node:22-trixie-slim FROM debian:trixie-slim
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
@@ -16,8 +16,11 @@ RUN apt-get update \
ripgrep \ ripgrep \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# App-specific deps. Python isn't required by codex itself RUN groupadd --gid 1000 node \
# (codex is a Node CLI), but is convenient for the agent to shell && useradd --uid 1000 --gid node --shell /bin/bash --create-home node
# App-specific deps. Python isn't required by Codex itself, but is convenient
# for the agent to shell
# out to for ad-hoc scripts. Kept on its own layer so it can be # out to for ad-hoc scripts. Kept on its own layer so it can be
# moved to a downstream image if the base ever needs to shrink. # moved to a downstream image if the base ever needs to shrink.
RUN apt-get update \ RUN apt-get update \
@@ -32,8 +35,8 @@ WORKDIR /home/node
ENV PATH="/home/node/.local/bin:${PATH}" ENV PATH="/home/node/.local/bin:${PATH}"
# Remote-control support requires the standalone Codex install layout # Remote-control support requires the standalone Codex install layout
# under ~/.codex/packages/standalone/current. The npm package can run # under ~/.codex/packages/standalone/current. Remote-control commands expect
# the TUI, but remote-control commands expect this installer-owned path. # this installer-owned path.
RUN mkdir -p /home/node/.codex \ RUN mkdir -p /home/node/.codex \
&& curl -fsSL https://chatgpt.com/codex/install.sh | sh && curl -fsSL https://chatgpt.com/codex/install.sh | sh
+7 -2
View File
@@ -1,8 +1,8 @@
# bot-bottle Pi provider image. # bot-bottle Pi provider image.
# #
# Node LTS, git/network tooling, and the Pi coding-agent CLI installed globally. # Debian stable, git/network tooling, and the Pi coding-agent CLI.
FROM node:22-trixie-slim FROM debian:trixie-slim
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
@@ -13,9 +13,14 @@ RUN apt-get update \
openssh-client \ openssh-client \
podman \ podman \
ripgrep \ ripgrep \
nodejs \
npm \
&& ln -s /usr/bin/fdfind /usr/local/bin/fd \ && ln -s /usr/bin/fdfind /usr/local/bin/fd \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN groupadd --gid 1000 node \
&& useradd --uid 1000 --gid node --shell /bin/bash --create-home node
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends python3 python3-pip python3-venv \ && apt-get install -y --no-install-recommends python3 python3-pip python3-venv \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@@ -13,16 +13,18 @@ userspace and an OCI container tool without requiring per-project setup.
## Problem ## Problem
The Claude, Codex, and Pi images inherit the generic `node:22-slim` tag. That The Claude, Codex, and Pi images inherited the generic `node:22-slim` tag. That
tag does not state which Debian release the project supports and currently tag did not make Debian the explicit supported base and left the images on the
leaves the images on the older Bookworm release. None of the built-in images older Bookworm release. None of the built-in images installed Podman, so tasks
installs Podman, so tasks that need to inspect or build OCI images must first that need to inspect or build OCI images first had to modify the bottle or
modify the bottle or cannot run at all. could not run at all.
## Goals / success criteria ## Goals / success criteria
- Every Dockerfile under `bot_bottle/contrib/*/Dockerfile` explicitly inherits - Every Dockerfile under `bot_bottle/contrib/*/Dockerfile` explicitly inherits
`node:22-trixie-slim`, based on Debian 13 (the current stable release). `debian:trixie-slim`, Debian 13 (the current stable release).
- Every image explicitly creates the non-root `node` runtime user with UID and
GID 1000 instead of relying on a language image to provide it.
- Every built-in agent image installs Podman from Debian stable. - Every built-in agent image installs Podman from Debian stable.
- Every built-in agent image retains an SSH client for Git-over-SSH workflows. - Every built-in agent image retains an SSH client for Git-over-SSH workflows.
- The non-root agent user owns a traversable XDG Git configuration directory, - The non-root agent user owns a traversable XDG Git configuration directory,
@@ -39,9 +41,10 @@ modify the bottle or cannot run at all.
## Design ## Design
Use the explicit `node:22-trixie-slim` base rather than the floating `slim` Use the explicit `debian:trixie-slim` base. Install Node.js and npm from Debian
variant. Install the `podman` package with each image's existing `apt-get` stable where a provider needs them, and create the common `node` runtime user
explicitly. Install the `podman` package with each image's existing `apt-get`
dependency layer, so package metadata and caches are still removed in the same dependency layer, so package metadata and caches are still removed in the same
layer. Treat Debian stable as the Podman stability and update channel; this layer. Treat Debian stable as the Node.js and Podman stability and update
keeps the images stdlib/distribution-first and avoids adding a third-party channel; this keeps the images distribution-first and avoids adding a
package repository. third-party package repository.
+12 -1
View File
@@ -18,7 +18,18 @@ class TestBuiltinAgentImages(unittest.TestCase):
with self.subTest(provider=dockerfile.parent.name): with self.subTest(provider=dockerfile.parent.name):
self.assertRegex( self.assertRegex(
dockerfile.read_text(), dockerfile.read_text(),
r"(?m)^FROM node:22-trixie-slim\s*$", r"(?m)^FROM debian:trixie-slim\s*$",
)
def test_all_create_node_runtime_user(self):
for dockerfile in _AGENT_DOCKERFILES:
with self.subTest(provider=dockerfile.parent.name):
dockerfile_text = dockerfile.read_text()
self.assertIn("groupadd --gid 1000 node", dockerfile_text)
self.assertIn(
"useradd --uid 1000 --gid node --shell /bin/bash "
"--create-home node",
dockerfile_text,
) )
def test_all_install_podman(self): def test_all_install_podman(self):