Repository navigation
Expand file tree
/
Copy pathDockerfile.base
More file actions
307 lines (286 loc) · 13.7 KB
/
Copy pathDockerfile.base
File metadata and controls
307 lines (286 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# syntax=docker/dockerfile:1.7
###############################################################################
# Dockerfile.base — foundational layer of the NeoLabHQ sandbox image chain.
#
# Chain: base -> agents -> final (-> universal).
# Dockerfile.agents, Dockerfile and Dockerfile.universalbuild on this
#
#
# Highlights:
# - Built on mcr.microsoft.com/devcontainers/base:trixie (Debian 13). No
# @sha256: pin — Microsoft rebuilds the codename tag for CVE patches and
# reproducibility is recovered downstream via our own :base@sha256: pins.
# - Apt top-up list derived from .devcontainer/Dockerfile lines 7-58,
# adjusted for Debian trixie (libgcc-s1 instead of libgcc1; libicu[NN]
# / liblttng-ust[N] globs match whichever SONAME trixie's archive ships).
# - `gh` CLI installed from cli.github.com's GPG-signed apt repo.
# - `mise` (https://mise.jdx.dev) is installed system-wide under
# /usr/local/share/mise, with shims at /usr/local/share/mise/shims
# prepended to PATH via both ENV and /etc/profile.d so non-interactive
# shells (Docker RUN, docker exec, CI) resolve `node`/`python`/`go`
# through mise without needing `mise activate`.
# - `nix` is installed single-user (--no-daemon) under /nix as the vscode
# user; profile is sourced from /etc/profile.d/nix.sh so non-login
# shells pick it up.
# - `devbox` (Jetify) is installed system-wide and reuses the nix install
# above. No image-level devbox.json is shipped; downstream projects drop
# their own devbox.json at the repo root.
# - Homebrew (Linuxbrew) is installed for the vscode user; PATH extended
# via /etc/profile.d/linuxbrew.sh.
# - mise global defaults pin Node/Python/Go to hedged selectors
# (lts/latest/latest) so security/patch rolls flow in at build
# time — NO specific version numbers are baked into this Dockerfile
# per /workspaces/sandbox/.claude/rules/research-version-claims.md.
# - pip3 helpers (dvc, yq) installed against system Python with
# --break-system-packages because Debian trixie enforces PEP 668's
# "externally-managed-environment" the same way Ubuntu 24.04 does.
# The "real" Python that devs use comes from mise; polluting the
# system Python with two CLI helpers is acceptable.
#
# Per /workspaces/sandbox/.claude/rules/dockerfile-curl-pipe-pipefail.md the
# SHELL directive below switches every subsequent RUN to bash with pipefail
# so a partial `curl ... | bash` aborts the layer instead of silently
# producing an empty install.
###############################################################################
FROM mcr.microsoft.com/devcontainers/base:trixie
###############################################################################
# Switch to bash with pipefail BEFORE any `curl ... | bash` line below.
# Required by /workspaces/sandbox/.claude/rules/dockerfile-curl-pipe-pipefail.md
# (hadolint DL4006). The default `/bin/sh` (dash on Debian) does NOT support
# `set -o pipefail`, so a failing curl piped into bash would silently produce
# a successful-but-empty layer.
###############################################################################
USER root
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
###############################################################################
# Apt top-up list.
#
# base:trixie ships only git, zsh, oh-my-zsh, and a thin shell-utility layer
# on top of buildpack-deps. We add the same package set the legacy
# .devcontainer/Dockerfile installed (lines 7-58), adjusted for Debian trixie:
# - libgcc-s1 instead of libgcc1 (trixie package naming).
# - libicu[0-9][0-9] / liblttng-ust[0-9] are globs that match whichever
# SONAME ships in the trixie archive (verify at build time with
# `apt-cache search '^libicu[0-9][0-9]$'` in a transient base:trixie
# container).
# - python3 / python3-pip are kept as a bootstrap for the later
# `pip install --break-system-packages dvc yq` step; the runtime
# Python that developers use comes from mise.
###############################################################################
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
apt-utils \
bash-completion \
openssh-client \
gnupg2 \
dirmngr \
iproute2 \
procps \
lsof \
htop \
net-tools \
psmisc \
curl \
tree \
wget \
rsync \
ca-certificates \
unzip \
bzip2 \
xz-utils \
zip \
nano \
vim-tiny \
less \
jq \
lsb-release \
apt-transport-https \
dialog \
libc6 \
libgcc-s1 \
libkrb5-3 \
libgssapi-krb5-2 \
'libicu[0-9][0-9]' \
'liblttng-ust[0-9]' \
libstdc++6 \
zlib1g \
locales \
sudo \
ncdu \
man-db \
strace \
manpages \
manpages-dev \
init-system-helpers \
build-essential \
file \
retry \
python3 \
python3-pip \
&& apt-get -y upgrade --no-install-recommends \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
###############################################################################
# GitHub CLI (`gh`).
#
# Verbatim from .devcontainer/Dockerfile lines 64-73, except `sudo` is dropped
# because we are already root. The `https://cli.github.com/packages stable
# main` apt suite serves both Debian and Ubuntu, and the `arch=$(dpkg
# --print-architecture)` substitution makes the repo definition multi-arch.
###############################################################################
RUN install -d -m 0755 /etc/apt/keyrings \
&& out=$(mktemp) \
&& curl -fsSL -o "$out" https://cli.github.com/packages/githubcli-archive-keyring.gpg \
&& cat "$out" > /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& install -d -m 0755 /etc/apt/sources.list.d \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends gh \
&& rm -rf /var/lib/apt/lists/*
###############################################################################
# mise — meta version manager for Node, Python, Go.
#
# Installed system-wide so all users share one runtime tree and one source
# of truth for default versions. Shims directory is prepended to PATH so
# `node`, `python`, and `go` resolve through mise first.
#
# Reference: https://mise.jdx.dev/mise-cookbook/docker.html
###############################################################################
ENV MISE_INSTALL_PATH=/usr/local/bin/mise
ENV MISE_DATA_DIR=/usr/local/share/mise
ENV MISE_CONFIG_DIR=/etc/mise
ENV MISE_CACHE_DIR=/var/cache/mise
# Disable trust prompts, they useless in containerized environments
ENV MISE_TRUSTED_CONFIG_PATHS=/
RUN curl -fsSL https://mise.run | sh \
&& mkdir -p "$MISE_DATA_DIR" "$MISE_CONFIG_DIR" "$MISE_CACHE_DIR" \
&& chown -R vscode:vscode "$MISE_DATA_DIR" "$MISE_CONFIG_DIR" "$MISE_CACHE_DIR"
# Shims on PATH for non-interactive shells. /etc/profile.d/* is sourced by
# login shells; the ENV directive below covers non-login non-interactive
# shells (Docker RUN, `docker exec`, CI invocations).
RUN printf '%s\n' \
'export MISE_DATA_DIR=/usr/local/share/mise' \
'export PATH=/usr/local/share/mise/shims:$PATH' \
> /etc/profile.d/mise.sh \
&& chmod 0644 /etc/profile.d/mise.sh
ENV PATH=/usr/local/share/mise/shims:${PATH}
###############################################################################
# nix — reproducible package manager (single-user install).
#
# Containers have no systemd to host the nix-daemon, so the upstream
# single-user installer (--no-daemon) is the supported path. nix wants to
# own /nix itself, so we pre-create /nix with vscode ownership and then run
# the installer as the vscode user.
#
# Sandbox + seccomp are disabled BEFORE the installer runs because the
# upstream installer self-bootstraps via `nix-env -i nix-<ver>`, which
# spins up a sandboxed build that calls
# `seccomp(SECCOMP_SET_MODE_FILTER, ...)`. Under docker buildkit the default
# seccomp profile rejects the exact BPF program nix tries to load with
# EINVAL ("unable to load seccomp BPF program: Invalid argument"), aborting
# the install — most visibly on linux/arm64. Disabling the sandbox is the
# conservative fix inside a container build (we're already in an isolated
# environment and the build itself is reproducible from this Dockerfile);
# `filter-syscalls = false` is belt-and-suspenders so nix doesn't try to
# install a seccomp filter at all during later devbox/nix invocations.
#
# Both `/etc/nix/nix.conf` (system-wide, written as root) AND
# `~vscode/.config/nix/nix.conf` (per-user, read by the single-user
# install at runtime) carry the settings — the single-user installer does
# NOT always consult /etc/nix, and downstream layers run nix as vscode,
# so the per-user file is what keeps later commands from re-tripping the
# same error.
#
# References:
# - https://nix.dev/manual/nix/stable/installation/single-user
# - https://nix.dev/manual/nix/stable/command-ref/conf-file (sandbox,
# filter-syscalls)
# - https://github.com/NixOS/nix/issues/4178 (seccomp BPF EINVAL under
# containerized installs)
###############################################################################
RUN mkdir -m 0755 /nix && chown vscode:vscode /nix \
&& install -d -m 0755 /etc/nix \
&& printf '%s\n' \
'sandbox = false' \
'filter-syscalls = false' \
> /etc/nix/nix.conf \
&& chmod 0644 /etc/nix/nix.conf
USER vscode
RUN install -d -m 0755 /home/vscode/.config/nix \
&& printf '%s\n' \
'sandbox = false' \
'filter-syscalls = false' \
> /home/vscode/.config/nix/nix.conf \
&& chmod 0644 /home/vscode/.config/nix/nix.conf \
&& curl --proto '=https' --tlsv1.2 -sSf -L https://nixos.org/nix/install -o /tmp/nix-install.sh \
&& sh /tmp/nix-install.sh --no-daemon \
&& rm /tmp/nix-install.sh
USER root
# Source nix profile from /etc/profile.d so non-login shells pick it up.
RUN printf '%s\n' \
'if [ -e /home/vscode/.nix-profile/etc/profile.d/nix.sh ]; then' \
' . /home/vscode/.nix-profile/etc/profile.d/nix.sh' \
'fi' \
> /etc/profile.d/nix.sh \
&& chmod 0644 /etc/profile.d/nix.sh
ENV PATH=/home/vscode/.nix-profile/bin:${PATH}
###############################################################################
# devbox (Jetify) — per-project nix wrapper.
#
# The `-f` flag skips the interactive prompt that otherwise hangs in Docker
# (upstream issues jetify-com/devbox#1594 and #2369). When run as root, the
# installer lands the binary at /usr/local/bin/devbox. devbox detects and
# reuses the nix install above; it does NOT bundle its own nix.
###############################################################################
RUN curl -fsSL https://get.jetify.com/devbox | bash -s -- -f
###############################################################################
# Homebrew (Linuxbrew) — installed as the vscode user.
#
# Preserves the project pattern from .devcontainer/Dockerfile line 99. The
# installer writes brew into /home/linuxbrew/.linuxbrew; the profile.d
# fragment below extends PATH for non-login shells and the ENV directive
# covers non-interactive shells.
###############################################################################
USER vscode
RUN NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
USER root
RUN printf '%s\n' \
'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' \
> /etc/profile.d/linuxbrew.sh \
&& chmod 0644 /etc/profile.d/linuxbrew.sh
ENV PATH=/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:${PATH}
###############################################################################
# Set mise global defaults for the three required languages as the vscode user.
#
# Hedged selectors (resolved at build time so security/patch rolls flow in
# automatically). Exact resolved versions land in the CI build summary via
# the Step 7 verification commands — they are NOT pinned in this Dockerfile,
# per /workspaces/sandbox/.claude/rules/research-version-claims.md:
# - node@lts => current Node LTS
# - python@latest => current Python stable
# - go@latest => current Go stable
###############################################################################
USER vscode
RUN mise use --global node@lts python@latest go@latest \
&& mise install \
&& mise reshim
###############################################################################
# pip helpers used by existing project scripts (dvc, yq).
#
# Installed against the apt-provided system Python with --break-system-packages
# because Debian trixie enforces PEP 668's "externally-managed-environment"
# the same way Ubuntu 24.04 does. The runtime Python that developers use
# comes from mise; the system Python is only a host for these two CLI
# helpers, so the PEP 668 escape hatch is acceptable here.
###############################################################################
USER root
RUN pip3 install --no-cache-dir --break-system-packages dvc yq
###############################################################################
# Final user. base:trixie defaults to `vscode` (UID/GID 1000) but make the
# contract explicit so Dockerfile.agents and the final Dockerfile inherit a
# known starting user.
###############################################################################
USER vscode