Rework Dockerfile.backend to the mandated Go multistage lint-stage pattern #17
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Dockerfile.backenddoes not follow the Go Dockerfile pattern thatREPO_POLICIES.mdmandates, and it drags the entire git history into the build context to do it. Verified onmainatfbfe1df.Current shape: a single
builderstage based ongolang:1.25-alpinethatapk addsgit make gcc musl-dev,go installs golangci-lint from source, copies.git, then runsmake checkandmake build.Divergences from policy
No separate lint stage. Policy: "Dockerfiles must use a separate lint stage for fail-fast feedback. Go repos use a multistage build where linting runs in an independent stage based on the
golangci/golangci-lintimage (pinned by hash). This stage runsmake fmt-checkandmake lintbefore the full build begins." There is noAS lintstage at all.No BuildKit stage dependency. Policy requires
COPY --from=lint /src/go.sum /dev/nullin the build stage to force BuildKit to complete linting before compiling. Absent.golangci-lint is compiled from source inside the build.
RUN CGO_ENABLED=0 go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@9f61b0f...builds the linter on every cache miss. The policy pattern uses the prebuiltgolangci/golangci-lintimage directly, which "includes both Go and the linter, so there is no need to install the linter separately." This is the single largest contributor to build time.COPY .git /repo/.git. The whole history is copied into the image solely sobackend/Makefile'sVERSION := $(shell git describe --always --dirty)resolves. Policy's pattern usesARG VERSION=devand-ldflags "-X main.Version=${VERSION}"instead. The.gitcopy also blocks adding.gitto.dockerignore(see #15).Static linking via CGO.
backend/Makefilebuilds with-linkmode external -extldflags -static, which is whygccandmusl-devare installed. The policy pattern isCGO_ENABLED=0 go build -trimpath -ldflags="-s -w -X main.Version=${VERSION}", which needs no C toolchain and produces a static binary anyway.Definition of done
Dockerfile.backendhas alintstageFROM golangci/golangci-lint@sha256:...(pinned by digest, with a# golangci/golangci-lint:v2.12.2, YYYY-MM-DDcomment above it) that runsmake fmt-checkandmake lint.COPY --from=lint /src/go.sum /dev/null(or the equivalent for the chosen paths) so BuildKit cannot run the stages in parallel and skip a lint failure. Verify this actually works: introduce a deliberate lint error, confirmdocker build -f Dockerfile.backend .fails, revert.make testand builds withCGO_ENABLED=0 go build -trimpathand-ldflags="-s -w -X main.Version=${VERSION}", driven byARG VERSION=dev.gccandmusl-devare no longer installed;backend/Makefile's-linkmode external -extldflags -staticis removed. Confirm the resulting binary is still static and runs in thealpineruntime stage.COPY .git /repo/.gitis gone. Version comes fromARG VERSION, andbackend/Makefiletoleratesgit describebeing unavailable (no build failure when.gitis absent).FROMis pinned by@sha256:digest with a version-and-date comment above it, per the hash-pinning policy.docker build -f Dockerfile.backend .succeeds and completes in under 5 minutes.make checkandcd backend && make checkboth pass.TODO.mdupdated in the same commit.(closes #N).Implementation requirements
REPO_POLICIES.mdclosely; deviate only where this repo genuinely differs, and note any deviation in the PR description.ca-certificates), keepsEXPOSE 8080, and keeps the existing entrypoint behaviour.maketargets andscript/entrypoints only for verification.