next #136
38
Dockerfile
38
Dockerfile
@@ -1,13 +1,9 @@
|
||||
# Build stage
|
||||
# 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
|
||||
|
||||
# golangci-lint v2.12.2, 2026-08-07
|
||||
RUN go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@c0d3ddc9cf3faa61a4e378e879ece580256d76e5
|
||||
# goimports v0.42.0
|
||||
RUN go install golang.org/x/tools/cmd/goimports@009367f5c17a8d4c45a961a3a509277190a9a6f0
|
||||
# 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.
|
||||
# golangci/golangci-lint:v2.12.2 (Debian-based), 2026-08-10
|
||||
FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS lint
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
@@ -15,8 +11,26 @@ RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
# Run all checks - build fails if any check fails
|
||||
RUN make check
|
||||
RUN make fmt-check
|
||||
RUN golangci-lint run --config .golangci.yml ./...
|
||||
|
||||
# Build stage
|
||||
# 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
|
||||
|
||||
# Force BuildKit to run the lint stage before proceeding
|
||||
COPY --from=lint /src/go.sum /dev/null
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
# Run the tests - build fails if any test fails
|
||||
RUN make test
|
||||
|
||||
# Build the binary
|
||||
RUN make build
|
||||
|
||||
27
Dockerfile.lint
Normal file
27
Dockerfile.lint
Normal file
@@ -0,0 +1,27 @@
|
||||
# Lint-only image: used by script/lint. golangci-lint is never run on
|
||||
# the host — the repo is COPYed into the build context and the linter
|
||||
# runs as a build step, so a successful build IS a clean lint. This
|
||||
# also works where the docker daemon is remote and bind mounts are
|
||||
# impossible.
|
||||
#
|
||||
# `golangci-lint config verify` is deliberately NOT run here: it
|
||||
# fetches its JSON schema over a live, unpinned HTTPS call, which would
|
||||
# make linting network-dependent and defeat hash-pinning.
|
||||
#
|
||||
# golangci/golangci-lint:v2.12.2 (Debian-based), 2026-08-10
|
||||
FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS deps
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
# Dependencies first, so this stage stays cached across lint runs.
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Everything below is invalidated on every run by the
|
||||
# --no-cache-filter=lint that script/lint passes: caching is explicitly
|
||||
# waived for linting, and a cached build lints nothing.
|
||||
FROM deps AS lint
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN golangci-lint run --config .golangci.yml ./...
|
||||
15
README.md
15
README.md
@@ -380,14 +380,21 @@ standard: normalized scripts in `script/` are the entrypoints for the
|
||||
development workflow, and the Makefile targets are thin shims that call
|
||||
them. We provide:
|
||||
|
||||
- `script/bootstrap` — install all dependencies (go, pinned
|
||||
golangci-lint and goimports, `go mod download`)
|
||||
- `script/bootstrap` — install all dependencies (go, pinned goimports,
|
||||
`go mod download`). It does not install golangci-lint: see
|
||||
`script/lint` below.
|
||||
- `script/setup` — make a fresh clone ready for development: bootstrap
|
||||
plus the git pre-commit hook
|
||||
- `script/projectname` — print the project name (used for the Docker
|
||||
image tag)
|
||||
- `script/test` — run the test suite (race detector, coverage)
|
||||
- `script/lint` — run golangci-lint
|
||||
- `script/lint` — run golangci-lint, always inside Docker: it builds
|
||||
`Dockerfile.lint`, which COPYs the repo into the digest-pinned
|
||||
`golangci-lint` image and lints as a build step, so a successful
|
||||
build is a clean lint. The linter is never installed or run on the
|
||||
host, and Docker is the only prerequisite. Caching is waived for
|
||||
linting: the lint stage is forced to execute on every run with
|
||||
`--no-cache-filter`, because a cached build lints nothing.
|
||||
- `script/fmt` — format all code (gofmt -s, goimports)
|
||||
- `script/fmt-check` — check formatting (read-only)
|
||||
- `script/check` — run test, lint, and fmt-check
|
||||
@@ -403,7 +410,7 @@ them. We provide:
|
||||
```sh
|
||||
make build # Build binary to bin/dnswatcher
|
||||
make test # Run tests with race detector
|
||||
make lint # Run golangci-lint
|
||||
make lint # Run golangci-lint in Docker (requires docker)
|
||||
make fmt # Format code
|
||||
make check # Run all checks (test, lint, fmt-check)
|
||||
make clean # Remove build artifacts
|
||||
|
||||
10
TODO.md
10
TODO.md
@@ -25,6 +25,16 @@ confirm make check still passes.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-08-10: all linting moved into Docker: new root `Dockerfile.lint`
|
||||
on the digest-pinned `golangci/golangci-lint:v2.12.2` image,
|
||||
`script/lint` reduced to a thin wrapper that builds it with
|
||||
`--no-cache-filter=lint` so the linter actually executes every run,
|
||||
golangci-lint install dropped from `script/bootstrap` (goimports
|
||||
stays, `script/fmt` needs it on the host), and the root `Dockerfile`
|
||||
given its own lint stage so its build no longer recurses through
|
||||
`make check` into `script/lint`. `golangci-lint config verify` is
|
||||
deliberately omitted: it fetches its schema over an unpinned live
|
||||
HTTPS call
|
||||
- 2026-08-07: golangci-lint bumped to v2.12.2 (commit-pinned installs
|
||||
in `Dockerfile` and `script/bootstrap`); `.golangci.yml` set to the
|
||||
org-standard v2-schema config used across the org's repos
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
# this repo. Idempotent: every install is guarded by a check so already
|
||||
# installed tools are skipped. Base tooling comes from nix, apt, brew,
|
||||
# or apk (detected in that order); assumes nothing is present.
|
||||
# golangci-lint and goimports are installed via `go install` at the same
|
||||
# pinned commits the Dockerfile uses (never "latest").
|
||||
# goimports is installed via `go install` at a pinned commit (never
|
||||
# "latest") because script/fmt and script/fmt-check run it on the host.
|
||||
# The linter is NOT installed here: golangci-lint runs via docker only
|
||||
# (script/lint), pinned by image digest, so its only prerequisite is a
|
||||
# working docker.
|
||||
set -eu
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
|
||||
# Pinned versions, 2026-08-07 (same pins as the Dockerfile)
|
||||
# golangci-lint v2.12.2
|
||||
GOLANGCI_LINT_REF="github.com/golangci/golangci-lint/v2/cmd/golangci-lint@c0d3ddc9cf3faa61a4e378e879ece580256d76e5"
|
||||
# Pinned version, 2026-08-07 (same pin as the Dockerfile)
|
||||
# goimports v0.42.0
|
||||
GOIMPORTS_REF="golang.org/x/tools/cmd/goimports@009367f5c17a8d4c45a961a3a509277190a9a6f0"
|
||||
|
||||
@@ -69,11 +70,18 @@ main() {
|
||||
if missing make; then pkg_install gnumake make make make; fi
|
||||
if missing go; then pkg_install go golang go go; fi
|
||||
|
||||
# Lint/format tools, pinned via go install (installs into
|
||||
# Format tools, pinned via go install (installs into
|
||||
# "$(go env GOPATH)/bin"; ensure that is on your PATH).
|
||||
if missing golangci-lint; then go install "$GOLANGCI_LINT_REF"; fi
|
||||
if missing goimports; then go install "$GOIMPORTS_REF"; fi
|
||||
|
||||
# Linting runs via docker only (script/lint). Warn, don't fail:
|
||||
# everything except `make lint` works without it.
|
||||
if missing docker; then
|
||||
echo "bootstrap: WARNING: docker not found; make lint and" >&2
|
||||
echo "bootstrap: make docker require it. Install docker to" >&2
|
||||
echo "bootstrap: run the linter." >&2
|
||||
fi
|
||||
|
||||
go mod download
|
||||
|
||||
echo "bootstrap complete"
|
||||
|
||||
20
script/lint
20
script/lint
@@ -1,12 +1,28 @@
|
||||
#!/bin/sh
|
||||
# script/lint: run the linter.
|
||||
# script/lint: run the linter. golangci-lint is never installed or run
|
||||
# on the host: it runs via docker only, one way, everywhere. This
|
||||
# builds Dockerfile.lint, which COPYs the repo into the digest-pinned
|
||||
# golangci-lint image and lints as a build step, so a successful build
|
||||
# means a clean lint.
|
||||
#
|
||||
# --no-cache-filter=lint forces the lint stage (source copy + linter
|
||||
# run) to execute on every invocation. Without it an unchanged tree
|
||||
# returns success in well under a second having linted nothing. The
|
||||
# deps stage (base image + go mod download) stays cached, and no global
|
||||
# cache invalidation is performed. --progress=plain keeps the linter's
|
||||
# own output visible.
|
||||
set -eu
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
golangci-lint run --config .golangci.yml ./...
|
||||
docker build \
|
||||
--progress=plain \
|
||||
--no-cache-filter=lint \
|
||||
--target lint \
|
||||
-f Dockerfile.lint \
|
||||
.
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user