From 99c959d0edd9c5af600b325a76a8e5e441fcb938 Mon Sep 17 00:00:00 2001 From: sneak Date: Sun, 4 Oct 2026 02:22:59 +0000 Subject: [PATCH] Keep the module cache out of the build stage's chown (closes #43) The build stage handed /src and the whole Go module cache to the unprivileged user with chown -R, in a layer that re-ran on every source change. On this host that step took minutes and lately stalled script/cibuild outright. The module cache now sits at /go/pkg/mod and stays root's: bootstrap fills it as root and builder only reads it. The sources are copied with --chown. A small chown, cached with bootstrap, hands builder the /src directory itself, which git and make build need, and the telemetry files root's go commands left in its home. Tests and the build still run as builder. Model: opus-5-5 --- Dockerfile | 29 +++++++++++++++++++++-------- TODO.md | 4 ++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2885086..52b89a4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,14 +51,22 @@ RUN echo "gate lint, epoch ${CHECK_EPOCH}" && \ FROM golang@sha256:56961d79ea8129efddcc0b8643fd8a5416b4e6228cfd477e3fd61deb2672c587 AS builder # We never build or run as root. Create an unprivileged user and point -# HOME and the Go caches at its home so go build and go test can write -# their caches when we drop to it below. $GOPATH/bin is deliberately not -# on PATH: script/bootstrap no longer `go install`s anything (the linter -# runs from a pinned image, never from a host install), so nothing lands +# HOME and the build cache at its home so go build and go test can write +# it when we drop to it below. $GOPATH/bin is deliberately not on PATH: +# script/bootstrap no longer `go install`s anything (the linter runs +# from a pinned image, never from a host install), so nothing lands # there and adding it would only widen what this image resolves. +# +# The module cache is kept outside that home, at the base image's +# default /go/pkg/mod, and belongs to root: script/bootstrap fills it as +# root, and builder only reads it, which is all Go does with a cache +# that already holds every module. Do not move it into the home and +# hand it over with `chown -R`: that walks every file in it and can +# stall the build for half an hour on a loaded host. RUN adduser -D -u 1000 builder ENV HOME=/home/builder ENV GOPATH=/home/builder/go +ENV GOMODCACHE=/go/pkg/mod ENV GOCACHE=/home/builder/.cache/go-build WORKDIR /src @@ -83,11 +91,16 @@ COPY script/ script/ COPY go.mod go.sum ./ RUN script/bootstrap -COPY . . +# /src itself must be builder's: make build writes the binary into it, +# and git refuses a repository whose top directory belongs to another +# user. The go commands bootstrap ran as root also left Go's telemetry +# files in builder's home. Both are a few small files, and this layer +# stays cached with bootstrap. +RUN chown builder:builder /src && chown -R builder:builder /home/builder -# Hand the sources and caches to the unprivileged user, then drop root -# before running any checks or builds. -RUN chown -R builder:builder /src /home/builder +# The sources are handed to builder as they are copied, so no layer has +# to walk them. Then drop root before running any checks or builds. +COPY --chown=builder:builder . . USER builder # Fail the build unless the branch is green. Runs as non-root so the diff --git a/TODO.md b/TODO.md index b07d9a1..5da2082 100644 --- a/TODO.md +++ b/TODO.md @@ -29,6 +29,10 @@ # Completed Steps +- the `Dockerfile` build stage leaves the Go module cache root's and copies + the sources with `--chown`, so no `chown -R` walks them (2026-10-04, + https://git.eeqj.de/sneak/sfdupes/issues/43) + - warn about and skip symlink, socket, FIFO, device and `.zfs` operands, keeping the records beneath them (2026-10-03, https://git.eeqj.de/sneak/sfdupes/issues/9)