diff --git a/Dockerfile b/Dockerfile index c52265d..52b5e5a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -214,16 +214,24 @@ RUN version="$(make version VERSION="$VERSION")"; \ # (Alpine.js, extracted from its tarball in 3p/) first. RUN make build VERSION="$VERSION" -# Rebuild with static linking for Alpine runtime. -# make build already verified compilation. -# The CGO binary from `make build` is dynamically linked against glibc, -# which doesn't exist on Alpine (musl). Rebuild with static linking so -# the binary runs on Alpine without glibc. +# Rebuild with static linking for the Alpine runtime. The cgo binary the +# step above builds is dynamically linked against glibc, which Alpine +# (musl) does not have. # -# The static flags go in through GO_LDFLAGS rather than a -ldflags of -# their own: the build target composes them with the -X that stamps the -# version, so this relink cannot silently drop the stamp. -RUN CGO_ENABLED=1 make build VERSION="$VERSION" GO_LDFLAGS='-extldflags "-static"' +# A statically linked glibc still loads shared libraries at run time for +# getaddrinfo and dlopen, and Alpine has none to load, so the build tags +# keep both out of the binary: netgo and osusergo select Go's own DNS +# resolver and user lookup instead of glibc's, and +# sqlite_omit_load_extension leaves out SQLite extension loading, which +# webhooker never uses. +# +# The static flags and tags go in through GO_LDFLAGS and GO_TAGS rather +# than a -ldflags of their own: the build target composes them with the +# -X that stamps the version, so this relink cannot silently drop the +# stamp. +RUN CGO_ENABLED=1 make build VERSION="$VERSION" \ + GO_TAGS=netgo,osusergo,sqlite_omit_load_extension \ + GO_LDFLAGS='-extldflags "-static"' # Runtime stage # alpine:3.21, 2026-03-17 diff --git a/Makefile b/Makefile index ea25515..b2f2f36 100644 --- a/Makefile +++ b/Makefile @@ -16,14 +16,16 @@ VERSION ?= $(shell script/version) # exists to correct. override VERSION := $(or $(strip $(VERSION)),$(shell script/version)) -# Extra linker flags for the build target. The static relink in the -# Dockerfile adds -extldflags here rather than passing its own -ldflags, -# so composing flags cannot drop the version stamp. +# Extra linker flags and build tags for the build target. The static +# relink in the Dockerfile passes -extldflags and its build tags through +# these rather than a -ldflags of its own, so composing flags cannot drop +# the version stamp. # # The build target itself always passes -trimpath and -s -w, as the Go # Dockerfile in REPO_POLICIES.md does: no build paths, symbol table or # debug information in the binary. GO_LDFLAGS ?= +GO_TAGS ?= bootstrap: @script/bootstrap @@ -53,7 +55,7 @@ check: @script/check build: assets - go build -trimpath -ldflags '$(strip -s -w -X main.version=$(VERSION) $(GO_LDFLAGS))' -o bin/webhooker ./cmd/webhooker + go build -trimpath -tags '$(GO_TAGS)' -ldflags '$(strip -s -w -X main.version=$(VERSION) $(GO_LDFLAGS))' -o bin/webhooker ./cmd/webhooker run: build ./bin/webhooker diff --git a/README.md b/README.md index cbdcc85..80ed3db 100644 --- a/README.md +++ b/README.md @@ -3419,12 +3419,14 @@ fixed independently of the compiler's: phases and the `css-check` and `markdown-check` stages passing (it copies a file from each), runs `make build` (which extracts Alpine.js from `3p/` first), and then rebuilds the binary with `CGO_ENABLED=1` and static linking - so it runs on musl. Both builds go through `make build`, the relink adding - its `-extldflags` via `GO_LDFLAGS`, so neither can drop the `-X` that stamps - the version. The version is the `VERSION` build arg if one is given, - otherwise derived from the `.git` in the context, and the stage fails if a - context with `.git` would stamp an empty version, `dev` or `unknown` (see - [Version stamping](#version-stamping)). + so it runs on musl, with the build tags `netgo`, `osusergo` and + `sqlite_omit_load_extension` so it needs none of glibc's shared libraries at + run time. Both builds go through `make build`, the relink adding its + `-extldflags` via `GO_LDFLAGS` and its tags via `GO_TAGS`, so neither can + drop the `-X` that stamps the version. The version is the `VERSION` build arg + if one is given, otherwise derived from the `.git` in the context, and the + stage fails if a context with `.git` would stamp an empty version, `dev` or + `unknown` (see [Version stamping](#version-stamping)). 7. **Runtime stage** (`alpine:3.21`) — copies the static binary and `deploy/docker-entrypoint.sh`, creates the `/var/lib/webhooker` directory for all SQLite databases, exposes port 8080, and includes a health check against