build: re-vendor canonical files from prompts dd4027b (closes #257)
check / check (push) Successful in 4m51s

The canonical files are fetched whole from sneak/prompts at dd4027b.
Kept after the canonical content: the livednstest deny entry in
.golangci.yml, /bin in .dockerignore, this repo's own .gitignore lines
and a [*.go] tab section in .editorconfig. The workflow keeps its
concurrency block and persist-credentials: false. Lint and test are
phases of the Dockerfile (golangci-lint v2.14.0; tests on the Debian
Go 1.25.7 image as an ordinary user, with the same flags); the build
stage depends on both and stamps the version the canonical way.
Dockerfile.lint is gone; the prettier stages of Dockerfile.fmt moved
into the Dockerfile. Every scripted docker build passes --no-cache;
script/cibuild bootstraps, runs script/check, then builds the image.
script/fmt-check absorbs fmt-check-go and fmt-check-markdown.

Model: opus-5-5
This commit is contained in:
2026-10-06 01:35:47 +00:00
parent d1060315b7
commit e2f8ec9dc9
23 changed files with 699 additions and 434 deletions
+83 -49
View File
@@ -1,68 +1,102 @@
# Lint stage - fast feedback on lint issues, before the build starts.
# The linter is invoked directly rather than through `make lint`: that
# target shells out to `docker build -f Dockerfile.lint`, and there is
# no docker daemon inside a docker build. For the same reason this stage
# runs only the Go half of `make fmt-check`; script/cibuild runs the
# markdown half after this build.
# script/cibuild and script/docker name this stage in --no-cache-filter.
# golangci/golangci-lint:v2.12.2 (Debian-based), 2026-08-10
FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS lint
# Lint phase, built alone by script/lint. The linter is invoked directly
# rather than through `make lint`, which is itself a docker build and
# would recurse into a daemon that does not exist in a build step.
# golangci/golangci-lint:v2.14.0 (Debian-based), 2026-10-06
FROM golangci/golangci-lint@sha256:ad862ba6b3798cbe0fd9fd7408d498fd74fbd2623a92406b2fd3898faf0bf98f AS lint
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN script/fmt-check-go
RUN golangci-lint run --config .golangci.yml ./...
# Build stage
# script/cibuild and script/docker name this stage in --no-cache-filter.
# golang 1.25-alpine, 2026-02-28
FROM golang@sha256:f6751d823c26342f9506c03797d2527668d095b0a15f1862cddb4d927a7a4ced AS builder
RUN apk add --no-cache git make gcc musl-dev binutils-gold
# A build context sent as a tar archive keeps its files' owners, and git
# refuses to read a checkout owned by another user. Trust this one
# whoever owns it.
RUN git config --system --add safe.directory /src
# Force BuildKit to run the lint stage before proceeding
COPY --from=lint /src/go.sum /dev/null
# 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.
# The tests query live DNS (TESTING.md), so this step needs the network.
# -count=1 keeps Go's test result cache out of both runs, as TESTING.md
# requires. -timeout 90s is a backstop above the 60-second cap on the
# suite. The rerun with -v only shows details: the build fails however
# it ends, because the first run already failed.
# golang 1.25.7-trixie, 2026-10-06
FROM golang@sha256:2b174ffcf56c7ad0c47d30d2630693265639ddf2a5141149c2da34db921791b4 AS test
# The file permission tests skip themselves as root, so the tests run as
# an ordinary user, whose home directory holds Go's build cache.
RUN useradd --create-home tester
USER tester
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go test -count=1 -race -timeout 90s -cover ./... || \
{ echo "--- Rerunning with -v for details ---"; \
go test -count=1 -race -timeout 90s -v ./...; exit 1; }
# Markdown formatting with prettier, at the version package.json and
# yarn.lock pin, so it is never installed on the host. script/fmt-check
# builds the fmt-check stage and script/fmt the fmt-out stage; the image
# does not depend on any of these stages, so a plain `docker build .`
# skips them.
# node:22-bookworm-slim, 2026-09-05
FROM node@sha256:83f487e0a63425e5b4d146fb5e5be574bcbe1b7b843d3ebafdd95eaf7767a7e5 AS nodedeps
# prettier lives outside /src, so the COPY of the repo cannot overwrite
# it and node_modules is not in the tree prettier walks.
WORKDIR /tools
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --non-interactive --no-progress
ENV PATH="/tools/node_modules/.bin:${PATH}"
WORKDIR /src
# --config rather than discovery: a missing .prettierrc is then an error
# instead of prettier's defaults, under which every wrap passes.
# --no-editorconfig so .prettierrc alone sets the style.
FROM nodedeps AS fmt-check
COPY . .
RUN prettier --config .prettierrc --no-editorconfig --check "**/*.md"
# Only the markdown is copied out, with its paths, so the export cannot
# put anything else back over the working tree.
FROM nodedeps AS fmt
COPY . .
RUN prettier --config .prettierrc --no-editorconfig --write "**/*.md" && \
mkdir -p /out && \
find . -name '*.md' -type f -exec cp --parents '{}' /out/ ';'
FROM scratch AS fmt-out
COPY --from=fmt /out/ /
# Build stage. Nothing is wanted from the lint and test phases; the
# copies are what make BuildKit build them first, so this stage cannot
# run unless lint and test passed.
# golang 1.25-alpine, 2026-02-28
FROM golang@sha256:f6751d823c26342f9506c03797d2527668d095b0a15f1862cddb4d927a7a4ced AS builder
COPY --from=lint /src/go.sum /dev/null
COPY --from=test /src/go.sum /dev/null
RUN apk add --no-cache git
# 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 . .
# Run the tests - build fails if any test fails
RUN make test
# Version stamped into the binary: the VERSION build arg when one is
# given and not empty (script/docker passes one), otherwise what
# `git describe` says of the .git in the build context, so a plain
# `docker build .` of a clone stamps its tag or short commit. The build
# arg reaches make through the environment.
# The VERSION build arg when one is given (script/docker and
# script/cibuild pass one), otherwise `git describe --tags --always` on
# the .git in the build context. With .git present, a version that is
# still empty, dev or unknown fails the build: git is missing or could
# not read the checkout.
ARG VERSION
# A context that carries .git, as a directory or as a file, must yield a
# real version: one that is empty, `dev` or `unknown` cannot be traced
# back to a commit.
RUN version="$(make version)"; \
RUN VERSION="${VERSION:-$(git describe --tags --always)}"; \
if [ -e .git ]; then \
case "$version" in \
"" | dev | unknown) \
echo "version is \"$version\" although the build context carries .git" >&2; \
exit 1 ;; \
case "$VERSION" in ""|dev|unknown) \
echo "version is '$VERSION' although .git is present" >&2; \
exit 1 ;; \
esac; \
fi
fi; \
CGO_ENABLED=0 go build -trimpath \
-ldflags="-s -w -X main.Version=${VERSION}" \
-o /src/bin/dnswatcher ./cmd/dnswatcher/
RUN make build
# Runtime stage
# Runtime stage, and the last one: a plain `docker build .` builds this
# stage's chain and nothing else.
# alpine 3.21, 2026-02-28
FROM alpine@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709