199 lines
10 KiB
Text
199 lines
10 KiB
Text
# htmx-ssr runtime image. Disjoint layer graph from Dockerfile.leptos-hydration: no
|
|
# wasm32 target, no wasm-bindgen, no wasm-opt, no cargo-leptos, no node/css
|
|
# stage. The server renders the full body server-side; interactivity is htmx
|
|
# attributes + /api/htmx/* endpoints, so the image bakes only the binary.
|
|
#
|
|
# Base: lamina/rust (rust-slim + sccache + cargo-chef + just + mold/clang) — NOT
|
|
# lamina/leptos/lamina/rustelo. The rustelo pre-cook layer buys nothing here:
|
|
# the framework source is COPY'd in regardless (path dep), so cargo-chef cooks
|
|
# the full recipe (rustelo + website deps) over the thin rust base.
|
|
#
|
|
# Content-agnostic: NO site/ baked in, and unlike the leptos image there is no
|
|
# WASM pkg/ either. The entire site tree (content/, config/, i18n/, templates/,
|
|
# public/ incl. freshly-built styles/website.css, rbac.ncl) is delivered to the
|
|
# PV at /var/www/site/ by the publish tool. website.css is built during distro
|
|
# assembly (just distro / content.nu run uno), never inside this image.
|
|
#
|
|
# Build arguments — supplied via --build-arg from lian-build/build_directives.ncl.
|
|
# RUST_VERSION lamina/rust tag
|
|
# NICKEL_VERSION lamina/nickel tag (runtime nickel + build.rs NCL eval)
|
|
# LAMINA_REGISTRY lamina catalog registry base
|
|
# BIN_NAME [[bin]] name in crates/server/Cargo.toml
|
|
# BIN_FEATURES full feature expression for the SSR build (e.g. "htmx-ssr")
|
|
# SERVER_PORT port the server listens on (EXPOSE + LEPTOS_SITE_ADDR)
|
|
ARG RUST_VERSION
|
|
ARG NICKEL_VERSION
|
|
ARG LAMINA_REGISTRY
|
|
ARG BIN_NAME
|
|
ARG BIN_FEATURES
|
|
ARG SERVER_PORT
|
|
|
|
# Named alias — BuildKit does not expand ARGs inside --from=; named stage required.
|
|
# No --platform override: nickel is invoked both by build.rs (native, in-build,
|
|
# must match the current target arch) and copied into the runtime image (same
|
|
# arch again) — $BUILDPLATFORM pinned it to the buildkit worker's own arch
|
|
# instead, which breaks when one worker native-builds a non-native target arch
|
|
# (e.g. lian-02 serving both amd64 and arm64 claims).
|
|
FROM ${LAMINA_REGISTRY}/nickel:${NICKEL_VERSION} AS nickel-bin
|
|
|
|
# ─── Stage 0: cargo-chef planner ─────────────────────────────────────────────
|
|
FROM ${LAMINA_REGISTRY}/rust:${RUST_VERSION} AS planner
|
|
WORKDIR /workspace
|
|
ENV CARGO_TARGET_DIR=/workspace/website/target
|
|
COPY Cargo.toml Cargo.lock ./website/
|
|
COPY crates/ ./website/crates/
|
|
COPY rustelo/ /rustelo/
|
|
COPY stratumiops/ /stratumiops/
|
|
WORKDIR /workspace/website
|
|
RUN cargo chef prepare --recipe-path recipe.json
|
|
|
|
# ─── Stage 1: build (deps cook + SSR binary) ─────────────────────────────────
|
|
FROM ${LAMINA_REGISTRY}/rust:${RUST_VERSION} AS builder
|
|
# Pin target dir so the runtime-stage COPY path is stable (lamina/rust defaults
|
|
# to /cargo-install-target).
|
|
ENV CARGO_TARGET_DIR=/workspace/website/target
|
|
|
|
COPY --from=nickel-bin /usr/local/bin/nickel /usr/local/bin/nickel
|
|
|
|
WORKDIR /workspace/website
|
|
COPY --from=planner /workspace/website/recipe.json recipe.json
|
|
COPY rustelo/ /rustelo/
|
|
COPY stratumiops/ /stratumiops/
|
|
|
|
# Cook deps for the exact feature set the binary is built with — no WASM target,
|
|
# so a single native cook covers everything (contrast: the leptos image cooks
|
|
# ssr deps separately because cargo-leptos handles the wasm32 pass).
|
|
# sccache DISABLED TEMPORARILY (2026-07-06): the S3 backend (Garage, libre-wuji)
|
|
# is not yet reachable from the fleet's buildkit runners — cross-network routing
|
|
# unresolved. RUSTC_WRAPPER unset, builds run uncached until that's fixed. When
|
|
# re-enabling, keep SCCACHE_S3_KEY_PREFIX="${TARGETARCH}/" — this is a native
|
|
# (non-cross-compiled) multi-arch build, so cache entries must stay namespaced
|
|
# per architecture.
|
|
ARG BIN_FEATURES
|
|
ARG TARGETARCH
|
|
# Original (sccache-enabled) — restore once Garage is reachable:
|
|
# RUN --mount=type=secret,id=sccache_env,required \
|
|
# --mount=type=cache,target=/workspace/website/target \
|
|
# --mount=type=cache,target=/usr/local/cargo/registry \
|
|
# set -a && . /run/secrets/sccache_env && set +a && \
|
|
# export RUSTC_WRAPPER=sccache && \
|
|
# export SCCACHE_S3_KEY_PREFIX="${TARGETARCH}/" && \
|
|
# RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold" \
|
|
# cargo chef cook --release --no-default-features --features "${BIN_FEATURES}" \
|
|
# --recipe-path recipe.json && \
|
|
# sccache --show-stats
|
|
RUN --mount=type=cache,target=/workspace/website/target,id=cargo-target-${TARGETARCH} \
|
|
--mount=type=cache,target=/usr/local/cargo/registry \
|
|
RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold" \
|
|
cargo chef cook --release --no-default-features --features "${BIN_FEATURES}" \
|
|
--recipe-path recipe.json
|
|
|
|
# Full sources for the actual build.
|
|
COPY Cargo.toml Cargo.lock ./
|
|
COPY crates/ ./crates/
|
|
# ManifestResolver (rustelo_utils) walks up from CARGO_MANIFEST_DIR for this.
|
|
COPY rustelo.manifest.toml ./rustelo.manifest.toml
|
|
# build.rs (build_page_generator) evaluates site/config NCL during compilation.
|
|
COPY site/config/ ./site/config/
|
|
COPY site/rbac.ncl ./site/rbac.ncl
|
|
|
|
ARG BIN_NAME
|
|
# build.rs reads SITE_CONFIG_PATH; nickel resolves framework contracts via
|
|
# NICKEL_IMPORT_PATH (rustelo layout is resources/nickel/, not nickel/).
|
|
ENV SITE_CONFIG_PATH=/workspace/website/site/config/index.ncl
|
|
ENV NICKEL_IMPORT_PATH=/rustelo/code/resources/nickel:/workspace/website/site/config
|
|
# Original (sccache-enabled) — restore once Garage is reachable:
|
|
# RUN --mount=type=secret,id=sccache_env,required \
|
|
# --mount=type=cache,target=/workspace/website/target \
|
|
# --mount=type=cache,target=/usr/local/cargo/registry \
|
|
# set -a && . /run/secrets/sccache_env && set +a && \
|
|
# export RUSTC_WRAPPER=sccache && \
|
|
# export SCCACHE_S3_KEY_PREFIX="${TARGETARCH}/" && \
|
|
# sccache --start-server && \
|
|
# RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold" \
|
|
# cargo build --release -p rustelo-htmx-server --no-default-features --features "${BIN_FEATURES}" && \
|
|
# cp /workspace/website/target/release/${BIN_NAME} /tmp/${BIN_NAME} && \
|
|
# CARGO_TARGET_DIR=/workspace/website/target \
|
|
# cargo build --release -p rustelo_content_graph_ssr --features gen \
|
|
# --manifest-path /rustelo/code/crates/foundation/crates/rustelo_content_graph_ssr/Cargo.toml && \
|
|
# cp /workspace/website/target/release/gen-content-graph /tmp/gen-content-graph && \
|
|
# sccache --show-stats
|
|
RUN --mount=type=cache,target=/workspace/website/target,id=cargo-target-${TARGETARCH} \
|
|
--mount=type=cache,target=/usr/local/cargo/registry \
|
|
RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold" \
|
|
cargo build --release -p rustelo-htmx-server --no-default-features --features "${BIN_FEATURES}" && \
|
|
cp /workspace/website/target/release/${BIN_NAME} /tmp/${BIN_NAME} && \
|
|
CARGO_TARGET_DIR=/workspace/website/target \
|
|
cargo build --release -p rustelo_content_graph_ssr --features gen \
|
|
--manifest-path /rustelo/code/crates/foundation/crates/rustelo_content_graph_ssr/Cargo.toml && \
|
|
cp /workspace/website/target/release/gen-content-graph /tmp/gen-content-graph
|
|
|
|
# ─── Stage 2: runtime ─────────────────────────────────────────────────────────
|
|
FROM debian:bookworm-slim
|
|
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates libssl3 curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
RUN useradd -m -u 1000 www
|
|
|
|
ARG BIN_NAME
|
|
ARG SERVER_PORT
|
|
|
|
# Server binary → /usr/local/bin/ so data PV mounts never shadow it.
|
|
COPY --from=builder /tmp/${BIN_NAME} /usr/local/bin/${BIN_NAME}
|
|
# Content-graph generator: regenerates content_graph.json from site/content + ontoref
|
|
# without a rebuild. Run: gen-content-graph [out.json] (needs ONTOREF_NICKEL_IMPORT_PATH).
|
|
COPY --from=builder /tmp/gen-content-graph /usr/local/bin/gen-content-graph
|
|
# nickel required at runtime: server calls `nickel export` to parse site/config/*.ncl
|
|
# on startup and site/rbac.ncl on RBAC hot-reload.
|
|
COPY --from=nickel-bin /usr/local/bin/nickel /usr/local/bin/nickel
|
|
# Rustelo framework nickel contracts — required by site/config/*.ncl at runtime.
|
|
COPY --from=builder /rustelo/code/resources/nickel/ /usr/local/share/rustelo/nickel/
|
|
|
|
WORKDIR /var/www
|
|
# Content-agnostic image: NO site/ baked in, NO WASM pkg/. The site tree
|
|
# (public/ incl. styles/website.css, content/, config/, i18n/, templates/,
|
|
# rbac.ncl) is delivered to the PV mounted at /var/www/site/ by the publish
|
|
# tool. server.ncl public_dir = "site/public" resolves identically in dev
|
|
# (source tree) and prod (PV-delivered tree). The htmx shell loads its runtime
|
|
# from public/ (htmx.min.js, extensions, reinit handlers) — all PV-served.
|
|
# The PV mount must be populated before first pod start; the K8s securityContext
|
|
# fsGroup=1000 (catalog deployment template) handles ownership at mount.
|
|
|
|
USER www
|
|
EXPOSE ${SERVER_PORT}
|
|
|
|
ENV RUST_LOG=info \
|
|
LEPTOS_ENV=PROD \
|
|
LEPTOS_SITE_ADDR=0.0.0.0:${SERVER_PORT} \
|
|
LEPTOS_SITE_ROOT=site \
|
|
SITE_CONFIG_PATH=/var/www/site/config/index.ncl \
|
|
NICKEL_IMPORT_PATH=/usr/local/share/rustelo/nickel:/var/www/site/config \
|
|
SITE_SERVER_CONTENT_URL=/r \
|
|
SITE_SERVER_ROOT_CONTENT=r \
|
|
SITE_SERVER_CONTENT_ROOT=site/r \
|
|
NATS_ADMIN_CREDENTIALS_FILE=
|
|
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD curl -sf http://localhost:${SERVER_PORT}/health > /dev/null || exit 1
|
|
|
|
# Entrypoint: wait for the PV-delivered site tree before exec'ing the server.
|
|
# Image is content-agnostic; site/config/index.ncl must exist for the binary to
|
|
# boot. While the PV is empty (first install, before publish-tool delivery),
|
|
# this keeps the container alive and logs a clear "waiting" message instead of
|
|
# crash-looping. Signals propagate via exec when the server takes over.
|
|
COPY --chmod=755 <<'EOF' /usr/local/bin/entrypoint.sh
|
|
#!/bin/sh
|
|
set -eu
|
|
CONFIG="${SITE_CONFIG_PATH:-/var/www/site/config/index.ncl}"
|
|
while [ ! -f "$CONFIG" ]; do
|
|
printf '[%s] site config missing at %s — waiting for PV bootstrap (publish tool to deliver site tarball)\n' \
|
|
"$(date -Iseconds)" "$CONFIG" >&2
|
|
sleep 10
|
|
done
|
|
printf '[%s] site config found at %s — starting server\n' "$(date -Iseconds)" "$CONFIG" >&2
|
|
exec /usr/local/bin/rustelo-htmx-server "$@"
|
|
EOF
|
|
|
|
CMD ["/usr/local/bin/entrypoint.sh"]
|