From 0fa2b714bf8369c8ca9f29ec97cda33b6a150ed2 Mon Sep 17 00:00:00 2001 From: Justin Chung <20733699+justin13888@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:19:52 -0500 Subject: [PATCH] feat: extend --version with commit, date, dirty flag, and build profile Add Commit, Dirty, BuildDate, and BuildProfile ldflags variables so --version outputs granular build identification details. Falls back to debug.ReadBuildInfo() for dev builds without injected ldflags. Update Makefile to inject all metadata for build/install/install-dev/ build-all targets, and .goreleaser.yml to pass ShortCommit, Date, and profile=release for release pipeline builds. --- .goreleaser.yml | 2 +- Makefile | 32 ++++++++++++------ cmd/sidecar/main.go | 82 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 102 insertions(+), 14 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index dc02fefb..ea063825 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -4,7 +4,7 @@ builds: - main: ./cmd/sidecar binary: sidecar ldflags: - - -s -w -X main.Version={{ .Version }} + - -s -w -X main.Version={{ .Version }} -X main.Commit={{ .ShortCommit }} -X main.Dirty=false -X main.BuildDate={{ .Date }} -X main.BuildProfile=release env: - CGO_ENABLED=0 goos: diff --git a/Makefile b/Makefile index c9012da9..99cfe869 100644 --- a/Makefile +++ b/Makefile @@ -5,19 +5,31 @@ all: build LINT_BASE ?= main +# Build metadata injected into every binary via ldflags. +VERSION ?= $(shell git describe --tags --always 2>/dev/null || echo "dev") +COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") +IS_DIRTY := $(shell git diff --quiet 2>/dev/null && git diff --cached --quiet 2>/dev/null && echo "false" || echo "true") +BUILD_DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) +BUILD_PROFILE ?= debug + +LDFLAGS := -X main.Version=$(VERSION) \ + -X main.Commit=$(COMMIT) \ + -X main.Dirty=$(IS_DIRTY) \ + -X main.BuildDate=$(BUILD_DATE) \ + -X main.BuildProfile=$(BUILD_PROFILE) + # Build the binary build: - go build -o bin/sidecar ./cmd/sidecar + go build -ldflags "$(LDFLAGS)" -o bin/sidecar ./cmd/sidecar # Install to GOBIN install: - go install ./cmd/sidecar + go install -ldflags "$(LDFLAGS)" ./cmd/sidecar -# Install with version info from git +# Install with version info from git (explicit dev profile) install-dev: - $(eval VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")) - @echo "Installing sidecar with Version=$(VERSION)" - go install -ldflags "-X main.Version=$(VERSION)" ./cmd/sidecar + @echo "Installing sidecar version=$(VERSION) commit=$(COMMIT) dirty=$(IS_DIRTY)" + go install -ldflags "$(LDFLAGS)" ./cmd/sidecar # Run tests test: @@ -95,10 +107,10 @@ lint-all: # Build for multiple platforms (local testing only — GoReleaser handles release builds) build-all: - GOOS=darwin GOARCH=amd64 go build -o bin/sidecar-darwin-amd64 ./cmd/sidecar - GOOS=darwin GOARCH=arm64 go build -o bin/sidecar-darwin-arm64 ./cmd/sidecar - GOOS=linux GOARCH=amd64 go build -o bin/sidecar-linux-amd64 ./cmd/sidecar - GOOS=linux GOARCH=arm64 go build -o bin/sidecar-linux-arm64 ./cmd/sidecar + GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/sidecar-darwin-amd64 ./cmd/sidecar + GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/sidecar-darwin-arm64 ./cmd/sidecar + GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/sidecar-linux-amd64 ./cmd/sidecar + GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/sidecar-linux-arm64 ./cmd/sidecar # Test GoReleaser locally (creates snapshot build without publishing) goreleaser-snapshot: diff --git a/cmd/sidecar/main.go b/cmd/sidecar/main.go index d922cf0c..fdeed903 100644 --- a/cmd/sidecar/main.go +++ b/cmd/sidecar/main.go @@ -43,8 +43,14 @@ import ( "golang.org/x/term" ) -// Version is set at build time via ldflags -var Version = "" +// Build-time variables set via ldflags. +var ( + Version = "" // semantic version (e.g. v0.78.0) + Commit = "" // short git commit hash + Dirty = "" // "true" if working tree was dirty at build time + BuildDate = "" // RFC3339 build timestamp + BuildProfile = "" // "release" or "debug" +) var ( configPath = flag.String("config", "", "path to config file") @@ -80,7 +86,7 @@ func main() { // Handle version flag if *versionFlag || *shortVersion { - fmt.Printf("sidecar version %s\n", effectiveVersion(Version)) + printVersionInfo() os.Exit(0) } @@ -266,6 +272,76 @@ func effectiveVersion(v string) string { return "devel" } +// vcsInfo holds build-time VCS metadata resolved from ldflags or debug.ReadBuildInfo. +type vcsInfo struct { + commit string + dirty bool + date string + profile string +} + +// resolveVCSInfo returns build metadata, preferring ldflags values and falling +// back to debug.ReadBuildInfo when ldflags were not injected. +func resolveVCSInfo() vcsInfo { + info := vcsInfo{ + commit: Commit, + dirty: Dirty == "true", + date: BuildDate, + profile: BuildProfile, + } + + // Fill in anything missing from debug.ReadBuildInfo (dev builds). + if info.commit == "" || info.date == "" { + if bi, ok := debug.ReadBuildInfo(); ok { + for _, s := range bi.Settings { + switch s.Key { + case "vcs.revision": + if info.commit == "" { + if len(s.Value) > 7 { + info.commit = s.Value[:7] + } else { + info.commit = s.Value + } + } + case "vcs.modified": + if Dirty == "" { + info.dirty = s.Value == "true" + } + case "vcs.time": + if info.date == "" { + info.date = s.Value + } + } + } + } + } + + if info.profile == "" { + info.profile = "development" + } + + return info +} + +// printVersionInfo prints detailed build information to stdout. +func printVersionInfo() { + ver := effectiveVersion(Version) + vcs := resolveVCSInfo() + + fmt.Printf("sidecar %s\n", ver) + if vcs.commit != "" { + if vcs.dirty { + fmt.Printf(" commit: %s (dirty)\n", vcs.commit) + } else { + fmt.Printf(" commit: %s\n", vcs.commit) + } + } + if vcs.date != "" { + fmt.Printf(" date: %s\n", vcs.date) + } + fmt.Printf(" profile: %s\n", vcs.profile) +} + func init() { // Customize usage output flag.Usage = func() {