check / check (push) Canceled after 0s
Re-vendors REPO_POLICIES.md, script/cibuild, script/docker, script/lint and script/test from sneak/prompts next at 0653579, on sneak's ruling in sneak/prompts#130. In the lint, test and builder stages of the Dockerfile, the module download, and in builder the apk install, move above ARG CACHEBUST, so Docker keeps those layers until the base image, the install line, go.mod or go.sum changes. golangci-lint run also mounts golangci-lint's cache. Every step that lints, tests or compiles still comes after ARG CACHEBUST, and go test keeps -count=1 and -race. README and TODO.md say the same. Model: opus-5-5
102 lines
4.4 KiB
Docker
102 lines
4.4 KiB
Docker
# In every stage ARG CACHEBUST comes right after the steps that install
|
|
# system packages and download Go's modules, or right after FROM in a
|
|
# stage with neither, and every step that lints, tests or compiles
|
|
# comes after it. script/ passes a new value on every build, which
|
|
# reruns every RUN step after it. Docker keeps the layers above it
|
|
# until the base image, their own lines or go.mod and go.sum change.
|
|
# The steps that compile mount Go's build cache, and golangci-lint run
|
|
# also mounts golangci-lint's; both survive from build to build. The
|
|
# test phase runs as nobody and so mounts Go's build cache at
|
|
# /tmp/.cache/go-build; its own comment says why.
|
|
|
|
# Lint phase, built alone by script/lint. The tools are invoked directly
|
|
# rather than through `make lint`, which runs docker itself and so cannot
|
|
# run inside a build step.
|
|
# golangci/golangci-lint:v2.14.0, 2026-10-07
|
|
FROM golangci/golangci-lint@sha256:ad862ba6b3798cbe0fd9fd7408d498fd74fbd2623a92406b2fd3898faf0bf98f AS lint
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
ARG CACHEBUST
|
|
COPY . .
|
|
|
|
# The gofmt half of `make fmt-check`. gofmt's output is assigned to a
|
|
# variable first so that its own exit status, as when it cannot parse a
|
|
# file, still fails the step.
|
|
RUN files="$(gofmt -s -l .)" && \
|
|
if [ -n "$files" ]; then \
|
|
echo "gofmt: files not formatted:" >&2; echo "$files" >&2; exit 1; \
|
|
fi
|
|
|
|
# Validates .golangci.yml against the schema the pinned binary embeds.
|
|
RUN golangci-lint config verify --config .golangci.yml
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
--mount=type=cache,target=/root/.cache/golangci-lint \
|
|
golangci-lint run --config .golangci.yml ./...
|
|
|
|
# Test phase, built alone by script/test. -race needs cgo and so a C
|
|
# compiler, which the Debian Go image ships and the alpine one does not.
|
|
# -count=1 because the mounted cache also holds test results, which go
|
|
# test would otherwise replay.
|
|
#
|
|
# The tests run as nobody: several of them make a file unreadable and
|
|
# expect reading it to fail, and root reads it anyway. nobody has no home
|
|
# directory, so HOME is /tmp, and Go keeps its build cache in
|
|
# /tmp/.cache/go-build. That is where the cache is mounted, owned by
|
|
# nobody (65534) so the tests can write to it.
|
|
# golang:1.25-trixie, 2026-10-04
|
|
FROM golang@sha256:2c4c60ef415fbfa5e90300722293bef36c5e63fae17570ce18f580af933dbd73 AS test
|
|
USER nobody
|
|
ENV HOME=/tmp
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
ARG CACHEBUST
|
|
COPY . .
|
|
RUN --mount=type=cache,target=/tmp/.cache/go-build,uid=65534,gid=65534 \
|
|
go test -count=1 -timeout 90s -race -cover ./... || \
|
|
{ echo "--- Rerunning with -v for details ---"; \
|
|
go test -count=1 -timeout 90s -race -v ./...; exit 1; }
|
|
|
|
# Build stage. Nothing is wanted from either phase above; the copies are
|
|
# what make BuildKit build them first, so nothing after them can run
|
|
# unless lint and test passed.
|
|
# golang:1.25-alpine, 2026-07-23
|
|
FROM golang@sha256:56961d79ea8129efddcc0b8643fd8a5416b4e6228cfd477e3fd61deb2672c587 AS builder
|
|
RUN apk add --no-cache git make
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
ARG CACHEBUST
|
|
COPY --from=lint /src/go.sum /dev/null
|
|
COPY --from=test /src/go.sum /dev/null
|
|
# A tar-stream context keeps the sender's file owners, which git refuses.
|
|
RUN git config --system --add safe.directory /src
|
|
COPY . .
|
|
|
|
# The version stamped into the binary: the VERSION build argument when
|
|
# one is given, otherwise `git describe --tags --always` of the .git in
|
|
# the build context. A context that carries .git and still yields no
|
|
# version fails the build; with neither, as from a source tarball, it is
|
|
# "dev". `make build` rather than `go build`, so the image and a host
|
|
# build share one compile recipe, cgo disabled included.
|
|
ARG VERSION
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
version="${VERSION:-$(git describe --tags --always || echo dev)}"; \
|
|
if [ -e .git ] && { [ -z "$version" ] || [ "$version" = dev ] || \
|
|
[ "$version" = unknown ]; }; then \
|
|
echo "no version could be derived although the build context carries .git" >&2; \
|
|
exit 1; \
|
|
fi; \
|
|
make build VERSION="$version"
|
|
|
|
# Runtime stage, and the last one: a plain `docker build .` builds this
|
|
# stage's chain and nothing else.
|
|
# alpine:3.22, 2026-07-23
|
|
FROM alpine@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce
|
|
ARG CACHEBUST
|
|
|
|
COPY --from=builder /src/sfdupes /usr/local/bin/sfdupes
|
|
|
|
ENTRYPOINT ["sfdupes"]
|