All checks were successful
Run Check Script / check (pull_request) Successful in 2m17s
The image shipped empty CSS: build.rs shells out to the tailwindcss v4 CLI and silently falls back to an empty bundle when it's absent — which it was in the rust:slim builder, so /static/tailwind.css served nothing. - Dockerfile: install the pinned Tailwind v4 standalone CLI (curl) in the builder and set TAILWIND_REQUIRED=1. - build.rs: when TAILWIND_REQUIRED is set (container/prod), a missing or failing CLI is now a hard build error instead of empty CSS; dev builds keep the soft fallback for the `serve-web --css-from` workflow. The env is a rerun trigger, so the first required build regenerates rather than reusing a cache-mounted empty bundle. Verified: with the CLI on PATH the embedded bundle is ~26 KB; with TAILWIND_REQUIRED=1 and no CLI the build fails as intended.
85 lines
3.8 KiB
Docker
85 lines
3.8 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|
# Multi-stage container build for harmony-fleet-operator.
|
|
#
|
|
# Build context is the workspace root (the operator's Cargo.toml has
|
|
# `path = "../../harmony"` deps, which only resolve when the whole
|
|
# workspace is in scope). Invoke from the repo root:
|
|
#
|
|
# docker build -f fleet/harmony-fleet-operator/Dockerfile \
|
|
# -t hub.nationtech.io/harmony/harmony-fleet-operator:<tag> .
|
|
#
|
|
# This replaces an earlier single-stage image that copied a host-built
|
|
# binary into archlinux:base — the archlinux choice was a glibc-ABI
|
|
# workaround for that host-build path. Building inside a pinned Rust
|
|
# image lets us use a matched debian-slim runtime instead.
|
|
#
|
|
# The builder and runtime stages must pin the same Debian release —
|
|
# unsuffixed `rust:slim` follows Debian's latest stable (trixie =
|
|
# glibc 2.40), so a binary built there fails to start on
|
|
# `debian:bookworm-slim` (glibc 2.36) with "GLIBC_2.39 not found".
|
|
# Both stages are pinned to bookworm here for a stable, matched ABI.
|
|
|
|
FROM docker.io/rust:1.94-slim-bookworm AS builder
|
|
|
|
# pkg-config + libssl-dev cover transitive native-tls/openssl-sys deps
|
|
# that surface during link; ca-certificates lets cargo fetch over TLS;
|
|
# curl downloads the Tailwind CLI below.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
pkg-config \
|
|
ca-certificates \
|
|
libssl-dev \
|
|
curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Tailwind v4 standalone CLI: build.rs shells out to it to generate the
|
|
# embedded CSS bundle. Pinned to match the dev host; override with
|
|
# --build-arg TAILWIND_VERSION=... TAILWIND_REQUIRED makes build.rs hard
|
|
# fail (not silently ship empty CSS) if the CLI is missing — see build.rs.
|
|
ARG TAILWIND_VERSION=v4.3.0
|
|
RUN curl -fsSL -o /usr/local/bin/tailwindcss \
|
|
"https://github.com/tailwindlabs/tailwindcss/releases/download/${TAILWIND_VERSION}/tailwindcss-linux-x64" \
|
|
&& chmod +x /usr/local/bin/tailwindcss \
|
|
&& tailwindcss --help >/dev/null
|
|
ENV TAILWIND_REQUIRED=1
|
|
|
|
WORKDIR /app
|
|
COPY . .
|
|
|
|
# `web-frontend` bundles the dashboard the operator serves in-process
|
|
# alongside the reconcile loop.
|
|
#
|
|
# BuildKit cache mounts persist the cargo registry + `target/` across
|
|
# builds, so an iterate-build-deploy loop recompiles only changed crates
|
|
# (seconds) instead of the whole workspace (minutes). The mounts are
|
|
# build-time only — the resulting image is identical, and a cold CI
|
|
# runner just rebuilds from scratch. The binary is copied out of the
|
|
# cache-mounted target within the same RUN, since cache mounts aren't
|
|
# part of the produced layer.
|
|
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
|
--mount=type=cache,target=/usr/local/cargo/git \
|
|
--mount=type=cache,target=/app/target \
|
|
cargo build --release --locked -p harmony-fleet-operator --features web-frontend \
|
|
&& cp /app/target/release/harmony-fleet-operator /harmony-fleet-operator
|
|
|
|
FROM docker.io/library/debian:bookworm-slim
|
|
|
|
# ca-certificates: outbound TLS to the K8s API and (optionally) NATS.
|
|
# kube + async-nats use rustls, so no libssl3 needed at runtime.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY --from=builder /harmony-fleet-operator /usr/local/bin/harmony-fleet-operator
|
|
|
|
# Non-root runtime. Pairs with the Pod's `securityContext.
|
|
# runAsNonRoot: true` in the helm chart — k8s admission rejects pods
|
|
# with that flag unless either the image declares a non-root USER or
|
|
# the Pod pins runAsUser. We deliberately don't pin runAsUser
|
|
# (OpenShift's restricted-v2 SCC assigns a namespace-specific UID and
|
|
# rejects fixed UIDs); the image's USER is the portable mechanism.
|
|
# 65532 is the `nonroot` UID convention used by distroless + many
|
|
# security-hardened base images.
|
|
USER 65532:65532
|
|
|
|
ENTRYPOINT ["/usr/local/bin/harmony-fleet-operator"]
|