fix: Add commit and branch to bd version output (github #503)

- Update Makefile install target to extract and pass git commit/branch via ldflags
- Add -X main.Commit and -X main.Branch to all build configurations in .goreleaser.yml
- Create scripts/install.sh helper for explicit version control during installation
- Add comprehensive tests for commit/branch resolution and output formatting

Fixes github #503: 'bd version' now reports as-built commit hash and branch
information regardless of installation method (make install, go install, or
released binaries from goreleaser).
This commit is contained in:
matt wilkie
2025-12-09 06:09:30 -07:00
committed by Steve Yegge
parent 48dec1fada
commit 0423fc689f
4 changed files with 322 additions and 97 deletions

File diff suppressed because one or more lines are too long

View File

@@ -22,6 +22,8 @@ builds:
- -s -w
- -X main.Version={{.Version}}
- -X main.Build={{.ShortCommit}}
- -X main.Commit={{.Commit}}
- -X main.Branch={{.Branch}}
- id: bd-linux-arm64
main: ./cmd/bd
@@ -38,6 +40,8 @@ builds:
- -s -w
- -X main.Version={{.Version}}
- -X main.Build={{.ShortCommit}}
- -X main.Commit={{.Commit}}
- -X main.Branch={{.Branch}}
- id: bd-darwin-amd64
main: ./cmd/bd
@@ -52,6 +56,8 @@ builds:
- -s -w
- -X main.Version={{.Version}}
- -X main.Build={{.ShortCommit}}
- -X main.Commit={{.Commit}}
- -X main.Branch={{.Branch}}
- id: bd-darwin-arm64
main: ./cmd/bd
@@ -66,6 +72,8 @@ builds:
- -s -w
- -X main.Version={{.Version}}
- -X main.Build={{.ShortCommit}}
- -X main.Commit={{.Commit}}
- -X main.Branch={{.Branch}}
- id: bd-windows-amd64
main: ./cmd/bd
@@ -82,6 +90,8 @@ builds:
- -s -w
- -X main.Version={{.Version}}
- -X main.Build={{.ShortCommit}}
- -X main.Commit={{.Commit}}
- -X main.Branch={{.Branch}}
- -buildmode=exe
archives:

View File

@@ -34,9 +34,11 @@ bench-quick:
go test -bench=. -benchtime=100ms -tags=bench -run=^$$ ./internal/storage/sqlite/ -timeout=15m
# Install bd to GOPATH/bin
install: build
install:
@echo "Installing bd to $$(go env GOPATH)/bin..."
go install ./cmd/bd
@bash -c 'commit=$$(git rev-parse HEAD 2>/dev/null || echo ""); \
branch=$$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ""); \
go install -ldflags="-X main.Commit=$$commit -X main.Branch=$$branch" ./cmd/bd'
# Clean build artifacts and benchmark profiles
clean:

View File

@@ -77,6 +77,127 @@ func TestVersionCommand(t *testing.T) {
jsonOutput = false
}
func TestResolveCommitHash(t *testing.T) {
// Save original Commit value
origCommit := Commit
defer func() { Commit = origCommit }()
t.Run("returns ldflag value when set", func(t *testing.T) {
testCommit := "abc123def456"
Commit = testCommit
result := resolveCommitHash()
if result != testCommit {
t.Errorf("Expected %q, got %q", testCommit, result)
}
})
t.Run("returns empty string when not set", func(t *testing.T) {
Commit = ""
result := resolveCommitHash()
// Result could be from git or empty - just verify it doesn't panic
if result == "" || len(result) >= 7 {
// Either empty or looks like a git hash
return
}
t.Errorf("Unexpected result format: %q", result)
})
}
func TestResolveBranch(t *testing.T) {
// Save original Branch value
origBranch := Branch
defer func() { Branch = origBranch }()
t.Run("returns ldflag value when set", func(t *testing.T) {
testBranch := "main"
Branch = testBranch
result := resolveBranch()
if result != testBranch {
t.Errorf("Expected %q, got %q", testBranch, result)
}
})
t.Run("returns empty string or git branch when not set", func(t *testing.T) {
Branch = ""
result := resolveBranch()
// Result could be from git or empty - just verify it doesn't panic
if result == "" || result == "main" || strings.Contains(result, "detached") {
return
}
t.Logf("Got branch: %q", result)
})
}
func TestVersionOutputWithCommitAndBranch(t *testing.T) {
// Save original values
oldStdout := os.Stdout
origCommit := Commit
origBranch := Branch
defer func() {
os.Stdout = oldStdout
Commit = origCommit
Branch = origBranch
}()
t.Run("text output includes commit and branch when available", func(t *testing.T) {
Commit = "7e709405b38c472d8cbc996c7cd26df7e3b438d0"
Branch = "main"
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("Failed to create pipe: %v", err)
}
os.Stdout = w
jsonOutput = false
versionCmd.Run(versionCmd, []string{})
w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
output := buf.String()
// Should contain both branch and commit
if !strings.Contains(output, "main@") {
t.Errorf("Expected output to contain 'main@', got: %s", output)
}
if !strings.Contains(output, "7e70940") { // first 7 chars of commit
t.Errorf("Expected output to contain commit hash, got: %s", output)
}
})
t.Run("json output includes commit and branch when available", func(t *testing.T) {
Commit = "7e709405b38c472d8cbc996c7cd26df7e3b438d0"
Branch = "main"
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("Failed to create pipe: %v", err)
}
os.Stdout = w
jsonOutput = true
versionCmd.Run(versionCmd, []string{})
w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
output := buf.String()
var result map[string]string
if err := json.Unmarshal([]byte(output), &result); err != nil {
t.Fatalf("Failed to parse JSON output: %v", err)
}
if result["commit"] != Commit {
t.Errorf("Expected commit %q, got %q", Commit, result["commit"])
}
if result["branch"] != Branch {
t.Errorf("Expected branch %q, got %q", Branch, result["branch"])
}
})
}
func TestVersionFlag(t *testing.T) {
// Save original stdout
oldStdout := os.Stdout