# Lint phase. The linter is invoked directly rather than through `make # lint` or `script/lint`, which are themselves a docker build and would # recurse into a daemon that does not exist in a build step. # golangci/golangci-lint:v2.14.0, 2026-10-05 FROM golangci/golangci-lint:v2.14.0@sha256:ad862ba6b3798cbe0fd9fd7408d498fd74fbd2623a92406b2fd3898faf0bf98f AS lint WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . # `golangci-lint run` silently ignores an unknown top-level key in # .golangci.yml, such as a misspelt `linters:`; `config verify` fails on it. RUN golangci-lint config verify --config .golangci.yml RUN golangci-lint run --config .golangci.yml ./... # Test phase. -race needs cgo and so a C compiler, which the Debian Go # image ships and the alpine one does not. # golang:1.26.1 (Debian trixie), 2026-10-05 FROM golang:1.26.1@sha256:cd78d88e00afadbedd272f977d375a6247455f3a4b1178f8ae8bbcb201743a8a AS test WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN go test -timeout 90s -race -cover ./... || \ { echo "--- Rerunning with -v for details ---"; \ go test -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.26.1-alpine, 2026-03-17 FROM golang:1.26.1-alpine@sha256:2389ebfa5b7f43eeafbd6be0c3700cc46690ef842ad962f6c5bd6be49ed82039 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 . . # The VERSION build arg when one is given, otherwise # `git describe --tags --always` on the .git in the build context. The # commit and its date always come from that .git. With .git present, a # version that is still empty, dev or unknown, or a commit or date that # is unknown, fails the build: git is missing or could not read the # checkout, as when .git is a file pointing outside the context. A # context without .git, such as a source export, stamps "dev" and an # "unknown" commit and date. ARG VERSION RUN VERSION="${VERSION:-$(git describe --tags --always || echo dev)}"; \ commit="$(git rev-parse HEAD || echo unknown)"; \ commit_date="$(git show -s --format=%cs HEAD || echo unknown)"; \ if [ -e .git ]; then \ case "$VERSION" in ""|dev|unknown) \ echo "version is '$VERSION' although .git is present" >&2; \ exit 1 ;; \ esac; \ if [ "$commit" = unknown ] || [ "$commit_date" = unknown ]; then \ echo "commit is '$commit' and its date '$commit_date'" \ "although .git is present" >&2; \ exit 1; \ fi; \ fi; \ globals=sneak.berlin/go/vaultik/internal/globals; \ CGO_ENABLED=0 go build -trimpath \ -ldflags="-s -w -X ${globals}.Version=${VERSION} \ -X ${globals}.Commit=${commit} \ -X ${globals}.CommitDate=${commit_date}" \ -o /vaultik ./cmd/vaultik # Runtime stage, and the last one: a plain `docker build .` builds this # stage's chain and nothing else. # alpine:3.21, 2026-02-25 FROM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709 RUN apk add --no-cache ca-certificates # Copy binary from builder COPY --from=builder /vaultik /usr/local/bin/vaultik # Create non-root user RUN adduser -D -H -s /sbin/nologin vaultik USER vaultik ENTRYPOINT ["/usr/local/bin/vaultik"]