check / check (push) Waiting to run
Every vendored file, REPO_POLICIES.md and every model script is the copy at sneak/prompts e3f79c4, with this repository's own entries kept after the canonical content. Lint and test are phases of the Dockerfile that write no image. Every build in script/ passes a new CACHEBUST value, which every stage declares first, so every RUN step runs again; the steps that compile keep Go's build cache, and both go test runs pass -count=1. make test runs the suite under the race detector as nobody. Dockerfile.lint, script/verify-lint-image-pin and make test-race are gone. Prettier runs on the host. .claude/settings.json is deleted. Deviation: the set comes from e3f79c4 on next rather than dd4027b. Judgement call: the test phase mounts the cache at nobody's /tmp/.cache/go-build, owned by nobody. Model: opus-5-5
94 lines
3.9 KiB
Docker
94 lines
3.9 KiB
Docker
# Every stage declares ARG CACHEBUST first: script/ passes a new value
|
|
# on every build, which reruns every RUN step after it. The steps that
|
|
# compile mount Go's build cache, which survives from build to build.
|
|
|
|
# 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
|
|
ARG CACHEBUST
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
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 \
|
|
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
|
|
ARG CACHEBUST
|
|
USER nobody
|
|
ENV HOME=/tmp
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
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 this stage cannot run unless
|
|
# lint and test passed.
|
|
# golang:1.25-alpine, 2026-07-23
|
|
FROM golang@sha256:56961d79ea8129efddcc0b8643fd8a5416b4e6228cfd477e3fd61deb2672c587 AS builder
|
|
ARG CACHEBUST
|
|
COPY --from=lint /src/go.sum /dev/null
|
|
COPY --from=test /src/go.sum /dev/null
|
|
RUN apk add --no-cache git make
|
|
# A tar-stream context keeps the sender's file owners, which git refuses.
|
|
RUN git config --system --add safe.directory /src
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
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"]
|