From ff44a5f79e8173b9c33301109d72cbb42c505a50 Mon Sep 17 00:00:00 2001 From: Chris Banks Date: Sat, 29 Jul 2023 22:04:42 +0100 Subject: [PATCH] Move version.go out of main and use BuildInfo. Move version.go from main to the router module (lib/) so that it can (later) be accessed from Router itself. Make it use standard debug.BuildInfo that the Go toolchain produced automatically, so we no longer have to mess about with shell scripts and linker flags. This eliminates some unnecessary differences between the Dockerfile and Makefile builds. --- .dockerignore | 14 ++++++++------ .gitignore | 2 +- Dockerfile | 18 ++++++++++++++++-- Makefile | 28 +++++++++++----------------- lib/version.go | 42 ++++++++++++++++++++++++++++++++++++++++++ main.go | 4 ++-- version.go | 13 ------------- 7 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 lib/version.go delete mode 100644 version.go diff --git a/.dockerignore b/.dockerignore index 1c8deaa6..c9ee771f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,8 @@ -.git -.gitignore -Dockerfile -README.md -docs -integration_tests +# +# Only files that are untracked by Git should be added here. +# +# The builder container needs to see a pristine checkout, otherwise +# vcs.modified in the BuildInfo will always be true, i.e. the build will always +# be marked as "dirty". +# +/router diff --git a/.gitignore b/.gitignore index aeed59a5..8421b565 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -router +/router __build diff --git a/Dockerfile b/Dockerfile index 6929371d..a2a9728d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,22 @@ -FROM golang:1.20-alpine AS builder +ARG go_registry="" +ARG go_version=1.20 +ARG go_tag_suffix=-alpine + +FROM ${go_registry}golang:${go_version}${go_tag_suffix} AS builder ARG TARGETARCH TARGETOS +ARG GOARCH=$TARGETARCH GOOS=$TARGETOS +ARG CGO_ENABLED=0 +ARG GOFLAGS="-trimpath" +ARG go_ldflags="-s -w" +# Go needs git for `-buildvcs`, but the alpine version lacks git :( It's still +# way cheaper to `apk add git` than to pull the Debian-based golang image. +# hadolint ignore=DL3018 +RUN apk add --no-cache git WORKDIR /src COPY . ./ -RUN CGO_ENABLED=0 GOARCH=$TARGETARCH GOOS=$TARGETOS go build -trimpath -ldflags="-s -w" +RUN go build -ldflags="$go_ldflags" && \ + ./router -version && \ + go version -m ./router FROM scratch COPY --from=builder /src/router /bin/router diff --git a/Makefile b/Makefile index bb4f75f0..e8bbb768 100644 --- a/Makefile +++ b/Makefile @@ -1,35 +1,29 @@ -.PHONY: all clean build lint test unit_tests integration_tests start_mongo stop_mongo +.PHONY: all clean build test lint unit_tests integration_tests start_mongo stop_mongo .NOTPARALLEL: -BINARY ?= router +TARGET_MODULE := router +GO_BUILD_ENV := CGO_ENABLED=0 SHELL := /bin/dash -ifdef RELEASE_VERSION -VERSION := $(RELEASE_VERSION) -else -VERSION := $(shell git describe --always | tr -d '\n'; test -z "`git status --porcelain`" || echo '-dirty') -endif - -all: build test +all: build clean: - rm -f $(BINARY) + rm -f $(TARGET_MODULE) build: - go build -ldflags "-X main.version=$(VERSION)" -o $(BINARY) + env $(GO_BUILD_ENV) go build + ./$(TARGET_MODULE) -version + +test: lint unit_tests integration_tests lint: golangci-lint run -test: lint unit_tests integration_tests - -unit_tests: build +unit_tests: go test -race $$(go list ./... | grep -v integration_tests) integration_tests: build start_mongo - ROUTER_PUBADDR=localhost:8080 \ - ROUTER_APIADDR=localhost:8081 \ - go test -race -v ./integration_tests + go test -race -v ./integration_tests start_mongo: ./mongo.sh start diff --git a/lib/version.go b/lib/version.go new file mode 100644 index 00000000..479dee03 --- /dev/null +++ b/lib/version.go @@ -0,0 +1,42 @@ +package router + +import ( + "fmt" + "runtime/debug" +) + +// VersionInfo returns human-readable version information in a format suitable +// for concatenation with other messages. +func VersionInfo() (v string) { + v = "(version info unavailable)" + + bi, ok := debug.ReadBuildInfo() + if !ok { + return + } + + rev, commitTime, dirty := buildSettings(bi.Settings) + if rev == "" { + return + } + + commitTimeOrDirty := "dirty" + if dirty == "false" { + commitTimeOrDirty = commitTime + } + return fmt.Sprintf("built from commit %.8s (%s) using %s", rev, commitTimeOrDirty, bi.GoVersion) +} + +func buildSettings(bs []debug.BuildSetting) (rev, commitTime, dirty string) { + for _, b := range bs { + switch b.Key { + case "vcs.modified": + dirty = b.Value + case "vcs.revision": + rev = b.Value + case "vcs.time": + commitTime = b.Value + } + } + return +} diff --git a/main.go b/main.go index f8c4eeb1..1f731124 100644 --- a/main.go +++ b/main.go @@ -35,7 +35,7 @@ ROUTER_BACKEND_HEADER_TIMEOUT=15s Timeout for backend response headers to be re ROUTER_FRONTEND_READ_TIMEOUT=60s See https://cs.opensource.google/go/go/+/master:src/net/http/server.go?q=symbol:ReadTimeout ROUTER_FRONTEND_WRITE_TIMEOUT=60s See https://cs.opensource.google/go/go/+/master:src/net/http/server.go?q=symbol:WriteTimeout ` - fmt.Fprintf(os.Stderr, helpstring, versionInfo(), os.Args[0]) + fmt.Fprintf(os.Stderr, helpstring, router.VersionInfo(), os.Args[0]) const ErrUsage = 64 os.Exit(ErrUsage) } @@ -89,7 +89,7 @@ func main() { flag.Usage = usage flag.Parse() if *returnVersion { - fmt.Printf("GOV.UK Router %s\n", versionInfo()) + fmt.Printf("GOV.UK Router %s\n", router.VersionInfo()) os.Exit(0) } diff --git a/version.go b/version.go deleted file mode 100644 index 1a7eefc3..00000000 --- a/version.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import ( - "fmt" - "runtime" -) - -// populated by -ldflags from Makefile -var version = "unknown" - -func versionInfo() string { - return fmt.Sprintf("build: %s (compiler: %s)", version, runtime.Version()) -}