Files
stack/infra/images/notebooks.Dockerfile
kert cf292ca7c5 chore(deps): Node 22 → 26 for the docs and notebooks builds
Node 26 is the current LTS. It ships without corepack (dropped from
Node 25 on), so both build stages died at `corepack enable` ("not
found"). Each now installs a pinned corepack from npm first
(COREPACK_VERSION=0.36.0, renovate-annotated so it gets bumped too).

Verified: both images build on node 26; the notebooks image passes
nb_fe_smoke --image (marimo home and editor, no console errors); the
docs site renders /, /api/ and search with no page errors.
2026-10-29 19:30:33 -04:00

163 lines
6.9 KiB
Docker

# syntax=docker/dockerfile:1
#
# Notebooks runtime — fhirworx-themed marimo on CUDA + Python 3.13.
#
# Stage 1 (fe): node + pnpm. Overlay fhirworx theme onto a pinned
# marimo source tree and compile frontend + lsp into
# marimo/_static/ and marimo/_lsp/.
# Stage 2 (wheel): uv builds a marimo wheel with the fhirworx bundle
# baked in.
# Stage 3 (final): CUDA runtime + Python stack; installs the wheel into
# the workspace venv.
#
# Iterate by editing infra/marimo/theme/**. BuildKit cache mounts keep
# pnpm and turbo warm between rebuilds, so CSS/icon edits land in under
# a minute after first bootstrap.
# Tracks marimo's latest release: dev/scripts/marimo_latest.py (nightly
# systemd timer) bumps this pin, gates and rolls the image. Keep the
# renovate comment directly above the ARG — its regex needs them adjacent.
# renovate: datasource=github-tags depName=marimo-team/marimo extractVersion=^v?(?<version>.+)$
ARG MARIMO_VERSION=0.25.2
# ---- stage 1: frontend ------------------------------------------------------
FROM node:26-bookworm-slim AS fe
ARG MARIMO_VERSION
WORKDIR /src
# Node 25+ no longer bundles corepack, so install a pinned one from npm.
# renovate: datasource=npm depName=corepack
ARG COREPACK_VERSION=0.36.0
RUN apt-get update \
&& apt-get install -y --no-install-recommends git ca-certificates python3 jq \
&& rm -rf /var/lib/apt/lists/* \
&& npm install -g corepack@${COREPACK_VERSION} \
&& corepack enable
# No `corepack prepare pnpm@latest`: the corepack shim resolves the exact
# pnpm version from marimo's package.json `packageManager` field at first
# use, so the package manager can't float between builds (the lockfile fix
# in apply-overlay.sh covers the dependency graph; this covers the tool).
# Shallow clone marimo at the pinned tag.
RUN git clone --depth 1 --branch ${MARIMO_VERSION} --filter=blob:none \
https://github.com/marimo-team/marimo /src
COPY infra/marimo/theme /overlay
RUN /overlay/scripts/apply-overlay.sh /src /overlay
RUN --mount=type=cache,target=/root/.cache/pnpm,sharing=locked \
pnpm install --no-frozen-lockfile
RUN --mount=type=cache,target=/root/.cache/turbo,sharing=locked \
--mount=type=cache,target=/src/frontend/node_modules/.cache,sharing=locked \
NODE_ENV=production pnpm turbo build --filter @marimo-team/frontend --output-logs=full
RUN --mount=type=cache,target=/root/.cache/turbo,sharing=locked \
NODE_ENV=production pnpm turbo build --filter @marimo-team/lsp --output-logs=full
RUN rm -rf marimo/_static marimo/_lsp \
&& mkdir -p marimo/_static marimo/_lsp \
&& cp -R frontend/dist/. marimo/_static/ \
&& rm -f marimo/_static/files/wasm-intro.py \
&& cp docs/_static/CLAUDE.md marimo/_static/CLAUDE.md 2>/dev/null || true \
&& cp packages/lsp/dist/index.cjs marimo/_lsp/ \
&& { [ -d packages/lsp/dist/copilot/dist ] \
&& cp -R packages/lsp/dist/copilot/dist/. marimo/_lsp/copilot/ \
|| true; }
# ---- stage 2: wheel ---------------------------------------------------------
FROM ghcr.io/astral-sh/uv:0.13.2-python3.13-trixie-slim AS wheel
WORKDIR /src
COPY --from=fe /src/ /src/
RUN uv build --wheel
# ---- stage 2b: notebook requirements ----------------------------------------
#
# Notebooks import src/ (bind-mounted, on PYTHONPATH), so the image needs the
# stack's base dependencies plus pyproject's `notebooks` extra, expanded by
# infra/images/notebooks/notebook_requirements.py. Generating the list in
# its own stage means the heavy install layer below rebuilds only when the
# list changes, not on every pyproject edit. (A script file, not a RUN
# heredoc: hadolint can't parse heredocs.)
FROM ghcr.io/astral-sh/uv:0.13.2-python3.13-trixie-slim AS nbreqs
COPY pyproject.toml infra/images/notebooks/notebook_requirements.py /
RUN python3 /notebook_requirements.py /pyproject.toml > /notebooks-requirements.txt \
&& cat /notebooks-requirements.txt
# ---- stage 3: runtime -------------------------------------------------------
FROM nvidia/cuda:12.6.0-runtime-ubuntu24.04
ARG USERNAME=kert
ARG USER_UID=1000
ARG USER_GID=1000
ARG PYTHON_VERSION=3.13
# Local package registry (Gitea) — set via --build-arg to pull from mirror
ARG PYPI_INDEX_URL=""
# MARIMO_SKIP_UPDATE_CHECK: no PyPI version check, so no "Update available"
# toast in the editor — marimo_latest.py keeps the version current instead.
ENV DEBIAN_FRONTEND=noninteractive \
HOME=/home/kert \
PATH="/home/kert/.local/bin:${PATH}" \
NVIDIA_VISIBLE_DEVICES=all \
NVIDIA_DRIVER_CAPABILITIES=compute,utility \
MARIMO_SKIP_UPDATE_CHECK=1
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \
curl ca-certificates git build-essential \
&& rm -rf /var/lib/apt/lists/*
RUN groupmod -n ${USERNAME} ubuntu \
&& usermod -l ${USERNAME} -d /home/${USERNAME} -m -s /bin/bash ubuntu \
&& chown -R ${USER_UID}:${USER_GID} /home/${USERNAME}
COPY --from=ghcr.io/astral-sh/uv:0.13.2 /uv /usr/local/bin/uv
COPY --from=ghcr.io/astral-sh/uv:0.13.2 /uvx /usr/local/bin/uvx
WORKDIR /home/${USERNAME}
# Bring in the fhirworx marimo wheel from the wheel stage.
COPY --from=wheel /src/dist/ /tmp/marimo-wheel/
COPY --from=nbreqs /notebooks-requirements.txt /tmp/notebooks-requirements.txt
# No release cooldown here (unlike pyproject.toml's exclude-newer): this
# workspace pulls cudf-polars-cu12 from pypi.nvidia.com, which publishes no
# upload dates, so any exclude-newer filters out every release and the build
# fails. uv has no per-package opt-out for date-less indexes.
# Install python + init workspace venv. Use the local wheel for marimo so
# the fhirworx frontend ships in the image; no upstream PyPI fetch for
# marimo itself. Packages the stack's code needs (duckdb, narwhals, sqlglot,
# pyarrow, httpx, ...) come from the requirements file, not this list — a
# name listed in both would have its spec replaced by whichever comes last.
RUN uv python install ${PYTHON_VERSION} \
&& uv init workspace --python ${PYTHON_VERSION} \
&& cd workspace \
&& MARIMO_WHL=$(ls /tmp/marimo-wheel/marimo-*.whl | head -1) \
&& uv add "${MARIMO_WHL}[recommended]" polars cudf-polars-cu12 pandas numpy \
"pyiceberg[s3,pyarrow]>=0.7.0" "trino>=0.328.0" \
vega_datasets pyzotero obstore s3fs \
"sqlalchemy>=2.0.0" "psycopg[binary]>=3.2.0" \
-r /tmp/notebooks-requirements.txt \
--extra-index-url https://pypi.nvidia.com
# Create marimo config directory with minimal defaults.
# Full config (marimo.toml, snippets, loch.css) is mounted at runtime
# from infra/marimo/ via compose.yml volumes.
RUN mkdir -p /home/${USERNAME}/.config/marimo
EXPOSE 2718
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -sf http://localhost:2718/health || exit 1
CMD ["uv", "run", "--project", "/home/kert/workspace", \
"marimo", "edit", \
"--host", "0.0.0.0", "--port", "2718", "--headless", "--no-token", \
"/home/kert/notebooks"]