# Hugo static-site build image. The build runs the individual non-lint # checks -- script/test, a clean `hugo --minify` production build, and # script/fmt-check, the read-only prettier check -- so the image build # fails on any template, content, config or formatting error. # # It deliberately does NOT run `make check`, and only the lint is # missing from what it does run. `make check` calls script/lint, and # script/lint is a `docker build` of Dockerfile.lint, so `RUN make # check` here would attempt a docker build inside a build step, in a # bare alpine with no docker client and no daemon socket. Putting # `make check` (or a `make lint`) back reintroduces exactly that # recursion. The lint is not skipped: script/cibuild runs script/lint # first, in its own container, before this build starts. # # Build this only via script/cibuild or script/docker: both pass the # CHECK_EPOCH build argument that this file requires, and a bare # `docker build .` fails by design. See the guard below for why. # alpine 3.21, 2026-02-28 FROM alpine@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709 WORKDIR /src # Install build dependencies first so the layer caches until the # scripts change (script/bootstrap installs git, make, go, hugo, # node/npm). Hugo is not an apk package here: script/bootstrap builds # the exact pinned version with `go install`, hash-verified against # sum.golang.org, so the published artifact does not depend on whatever # hugo this base image's repos happen to serve. COPY script/ script/ RUN script/bootstrap COPY . . # CHECK_EPOCH is a per-invocation nonce supplied by script/cibuild and # script/docker. Without it an unchanged tree serves this layer from # cache and the build reports a green it never ran. ARG is stage-scoped, # so it must be redeclared in every stage that runs checks - this image # has one stage, so one declaration. Declared with no default: a default # would be a constant, and a constant is a stable cache key. The guard # makes a bare `docker build .` fail loudly instead of silently reusing # the empty (and therefore stable) cache key. Expand the value into the # command so the cache miss does not depend on BuildKit's handling of an # unreferenced ARG. Both the guard and the check RUN reference the value, # so both are value-keyed: there are two independent invalidation points # here, not one. Keep both. # # Everything above this point still caches, so the script/bootstrap # layer - which compiles Hugo from source - is not rebuilt. ARG CHECK_EPOCH RUN [ -n "$CHECK_EPOCH" ] || exit 1 # The individual non-lint checks - build fails if either fails. Invoked # as script/ entrypoints rather than `make check` for the reason in the # header comment above. RUN echo "check epoch: ${CHECK_EPOCH}" && script/test RUN script/fmt-check