# syntax=docker/dockerfile:1.7 FROM ubuntu:24.04 ARG SANDBOX_UID=1001 ARG SANDBOX_GID=1001 ARG TARGETARCH ENV DEBIAN_FRONTEND=noninteractive ENV NODE_VERSION=25.9.0 ENV GO_VERSION=1.26.0 RUN --mount=type=cache,id=lab-ubuntu-apt-cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=lab-ubuntu-apt-lists,target=/var/lib/apt,sharing=locked \ apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ bash \ ca-certificates \ curl \ file \ git \ jq \ libatomic1 \ python3 \ python3-pip \ python3-venv \ ripgrep \ rsync \ sudo \ tini \ tmux \ unzip \ wget \ xz-utils \ && rm -rf /var/lib/apt/lists/* # Install official Node.js binaries RUN set -eux; \ case "${TARGETARCH:-amd64}" in \ amd64) NODE_ARCH="x64" ;; \ arm64) NODE_ARCH="arm64" ;; \ *) echo "Unsupported TARGETARCH for Node: ${TARGETARCH:-unset}" >&2; exit 1 ;; \ esac; \ curl -fsSLO "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz"; \ tar -xJf "node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz" -C /usr/local --strip-components=1; \ rm -f "node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz"; \ node --version; \ npm --version # Install official Go binaries RUN set -eux; \ case "${TARGETARCH:-amd64}" in \ amd64) GO_ARCH="amd64" ;; \ arm64) GO_ARCH="arm64" ;; \ *) echo "Unsupported TARGETARCH for Go: ${TARGETARCH:-unset}" >&2; exit 1 ;; \ esac; \ curl -fsSLO "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz"; \ rm -rf /usr/local/go; \ tar -xzf "go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -C /usr/local; \ rm -f "go${GO_VERSION}.linux-${GO_ARCH}.tar.gz"; \ /usr/local/go/bin/go version # Create runtime user matching host uid/gid RUN set -eux; \ if ! getent group sandbox >/dev/null; then groupadd -g "${SANDBOX_GID}" sandbox; fi; \ if ! id -u sandbox >/dev/null 2>&1; then useradd -m -u "${SANDBOX_UID}" -g "${SANDBOX_GID}" -s /bin/bash sandbox; fi; \ echo 'sandbox ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/sandbox; \ chmod 0440 /etc/sudoers.d/sandbox # Make /workspace the effective HOME for all user-installed tools ENV HOME=/workspace ENV GOPATH=/workspace/go ENV NPM_CONFIG_PREFIX=/workspace/.local ENV npm_config_prefix=/workspace/.local ENV PIP_DISABLE_PIP_VERSION_CHECK=1 ENV PATH=/workspace/.local/bin:/workspace/.cargo/bin:/workspace/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin # Global shell defaults for interactive sessions RUN printf '%s\n' \ 'export HOME=/workspace' \ 'export GOPATH=/workspace/go' \ 'export NPM_CONFIG_PREFIX=/workspace/.local' \ 'export npm_config_prefix=/workspace/.local' \ 'export PATH=/workspace/.local/bin:/workspace/.cargo/bin:/workspace/go/bin:/usr/local/go/bin:$PATH' \ 'export EDITOR=vi' \ > /etc/profile.d/workspace-home.sh \ && chmod 0644 /etc/profile.d/workspace-home.sh COPY --chmod=755 <<'EOF' /usr/local/bin/sandbox-entrypoint.sh #!/usr/bin/env bash set -euo pipefail export HOME="${HOME:-/workspace}" export GOPATH="${GOPATH:-$HOME/go}" export NPM_CONFIG_PREFIX="${NPM_CONFIG_PREFIX:-$HOME/.local}" export npm_config_prefix="${npm_config_prefix:-$HOME/.local}" export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$HOME/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" mkdir -p \ "$HOME" \ "$HOME/.local/bin" \ "$HOME/.local/lib/node_modules" \ "$HOME/.cache" \ "$HOME/.config" \ "$HOME/.npm" \ "$HOME/.cargo/bin" \ "$HOME/go/bin" \ "$HOME/go/pkg" # Keep npm cache/user config inside the workspace-mounted home. if [ ! -f "$HOME/.npmrc" ]; then cat > "$HOME/.npmrc" </dev/null || cd "$HOME" exec "$@" EOF USER sandbox WORKDIR /workspace ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/sandbox-entrypoint.sh"] CMD ["sleep", "infinity"]