Skip to content

Commit 1797362

Browse files
authored
feat(main): forward buildTime ldflag + extract testable helper (#565)
## Summary Addresses copilot-review follow-ups from [#564](#564). ### 1. BuildTimestamp actually populates `FormatVersion()` previously reported `built at n/a` even on tagged releases because the template injected only `main.version` + `main.build`. [netresearch/.github#74](netresearch/.github#74) extended the template with `-X main.buildTime=<commit-timestamp>`; this PR adds `var buildTime string` and forwards it into `internal/version.BuildTimestamp`. ### 2. Testable forwarding helper Extracted the three `if X != "" { ... }` assignments into `forwardBuildMetadata(v, b, t)`. Three new test cases in `main_test.go`: - **all values forwarded** — confirms the happy path mutates internalversion.* - **empty inputs preserve existing values** — confirms local `go run` / untagged `go build` leaves defaults intact - **partial injection** — confirms only the non-empty inputs land (e.g. buildTime empty on workflow_dispatch backfill still populates version + build) Tests use snapshot/restore via `t.Cleanup` so they don't leak state. ### 3. Sync release.yml to latest template Picks up the `-X main.buildTime=` ldflag from #74. ## Test plan - [x] `go test -run TestForwardBuildMetadata -v ./cmd/ldap-manager/` passes (3 subtests).
2 parents 4f65e45 + 30931a0 commit 1797362

3 files changed

Lines changed: 100 additions & 10 deletions

File tree

.github/workflows/release.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,17 @@ jobs:
7171
goos: ${{ matrix.goos }}
7272
goarch: ${{ matrix.goarch }}
7373
goarm: ${{ matrix.goarm || '' }}
74-
ldflags: "-s -w -X main.version=${{ needs.create-release.outputs.tag }} -X main.build=${{ needs.create-release.outputs.sha }}"
74+
# Fleet ldflag convention: repos that want to surface release
75+
# metadata declare `var version, build, buildTime string` in their
76+
# main package. Each repo decides which to forward into its own
77+
# version package (ofelia uses main.* directly; ldap-manager
78+
# forwards into internal/version.*). Empty values are a silent
79+
# no-op for repos that don't declare the corresponding var.
80+
ldflags: >-
81+
-s -w
82+
-X main.version=${{ needs.create-release.outputs.tag }}
83+
-X main.build=${{ needs.create-release.outputs.sha }}
84+
-X main.buildTime=${{ github.event.head_commit.timestamp }}
7585
ref: ${{ needs.create-release.outputs.tag }}
7686
release-tag: ${{ needs.create-release.outputs.tag }}
7787
sbom: true

cmd/ldap-manager/main.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,38 @@ const (
2727
)
2828

2929
// Build-injected version metadata. Populated by release.yml's ldflags
30-
// (`-X main.version=<tag>`, `-X main.build=<commit-sha>`); forwarded
31-
// into internal/version at init() so FormatVersion() / the `version`
30+
// (`-X main.version=<tag>`, `-X main.build=<commit-sha>`,
31+
// `-X main.buildTime=<commit-timestamp>`); forwarded into
32+
// internal/version at init() so FormatVersion() / the `version`
3233
// subcommand report the release info without requiring each repo to
3334
// invent its own ldflag target-path convention.
3435
var (
35-
version = ""
36-
build = ""
36+
version = ""
37+
build = ""
38+
buildTime = ""
3739
)
3840

39-
func init() {
40-
if version != "" {
41-
internalversion.Version = version
41+
// forwardBuildMetadata copies the build-injected package-main vars into
42+
// the internal/version package. Extracted from init() to keep it
43+
// unit-testable without mutating global state via the init machinery.
44+
// Empty inputs are treated as "not injected" and leave the existing
45+
// internal/version defaults in place.
46+
func forwardBuildMetadata(v, b, t string) {
47+
if v != "" {
48+
internalversion.Version = v
49+
}
50+
if b != "" {
51+
internalversion.CommitHash = b
4252
}
43-
if build != "" {
44-
internalversion.CommitHash = build
53+
if t != "" {
54+
internalversion.BuildTimestamp = t
4555
}
4656
}
4757

58+
func init() {
59+
forwardBuildMetadata(version, build, buildTime)
60+
}
61+
4862
func main() {
4963
port := os.Getenv("PORT")
5064
if port == "" {

cmd/ldap-manager/main_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,74 @@ import (
1010
"strconv"
1111
"strings"
1212
"testing"
13+
14+
internalversion "github.com/netresearch/ldap-manager/internal/version"
1315
)
1416

17+
func TestForwardBuildMetadata(t *testing.T) {
18+
// Snapshot + restore so this test doesn't leak state to siblings.
19+
origV, origC, origT := internalversion.Version, internalversion.CommitHash, internalversion.BuildTimestamp
20+
t.Cleanup(func() {
21+
internalversion.Version = origV
22+
internalversion.CommitHash = origC
23+
internalversion.BuildTimestamp = origT
24+
})
25+
26+
t.Run("all values forwarded", func(t *testing.T) {
27+
internalversion.Version = "dev"
28+
internalversion.CommitHash = "n/a"
29+
internalversion.BuildTimestamp = "n/a"
30+
31+
forwardBuildMetadata("v1.2.3", "abc123", "2026-04-20T00:00:00Z")
32+
33+
if got, want := internalversion.Version, "v1.2.3"; got != want {
34+
t.Errorf("Version = %q, want %q", got, want)
35+
}
36+
if got, want := internalversion.CommitHash, "abc123"; got != want {
37+
t.Errorf("CommitHash = %q, want %q", got, want)
38+
}
39+
if got, want := internalversion.BuildTimestamp, "2026-04-20T00:00:00Z"; got != want {
40+
t.Errorf("BuildTimestamp = %q, want %q", got, want)
41+
}
42+
})
43+
44+
t.Run("empty inputs preserve existing values", func(t *testing.T) {
45+
internalversion.Version = "preserved-v"
46+
internalversion.CommitHash = "preserved-c"
47+
internalversion.BuildTimestamp = "preserved-t"
48+
49+
forwardBuildMetadata("", "", "")
50+
51+
if got := internalversion.Version; got != "preserved-v" {
52+
t.Errorf("Version mutated to %q", got)
53+
}
54+
if got := internalversion.CommitHash; got != "preserved-c" {
55+
t.Errorf("CommitHash mutated to %q", got)
56+
}
57+
if got := internalversion.BuildTimestamp; got != "preserved-t" {
58+
t.Errorf("BuildTimestamp mutated to %q", got)
59+
}
60+
})
61+
62+
t.Run("partial injection", func(t *testing.T) {
63+
internalversion.Version = "preserved"
64+
internalversion.CommitHash = "preserved"
65+
internalversion.BuildTimestamp = "preserved"
66+
67+
forwardBuildMetadata("v9.9.9", "", "2026-01-01T00:00:00Z")
68+
69+
if got, want := internalversion.Version, "v9.9.9"; got != want {
70+
t.Errorf("Version = %q, want %q", got, want)
71+
}
72+
if got := internalversion.CommitHash; got != "preserved" {
73+
t.Errorf("CommitHash mutated to %q", got)
74+
}
75+
if got, want := internalversion.BuildTimestamp, "2026-01-01T00:00:00Z"; got != want {
76+
t.Errorf("BuildTimestamp = %q, want %q", got, want)
77+
}
78+
})
79+
}
80+
1581
func TestIsValidPort(t *testing.T) {
1682
cases := []struct {
1783
in string

0 commit comments

Comments
 (0)