From b1897ce7199373c3fdddfb55c4bcded6655110b1 Mon Sep 17 00:00:00 2001 From: Pasha Sviderski Date: Wed, 22 Apr 2026 15:41:55 +1000 Subject: [PATCH] fix(nightly): inject correct semver 0..0-nightly-abc1234 for nightly builds --- .goreleaser.yaml | 8 +++++ internal/grpcversion/interceptor.go | 14 ++++++-- internal/grpcversion/interceptor_test.go | 41 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 6d4c35c6..57e464a9 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -11,6 +11,14 @@ before: report_sizes: true +git: + # Skip the stale 'nightly' tag so {{.Version}} resolves to the latest semver tag for --snapshot builds. + ignore_tags: + - nightly +snapshot: + # Semver-compliant nightly version, e.g. "0.19.0-nightly-abc1234" when the latest tag is v0.18.0. + version_template: "{{ incminor .Version }}-nightly-{{.ShortCommit}}" + builds: - id: uncloud main: ./cmd/uncloud diff --git a/internal/grpcversion/interceptor.go b/internal/grpcversion/interceptor.go index c8560444..0bf19f00 100644 --- a/internal/grpcversion/interceptor.go +++ b/internal/grpcversion/interceptor.go @@ -38,10 +38,11 @@ const ( ) var ( - // currentVersion is the version of this binary (CLI or daemon). - currentVersion = semver.MustParse(version.String()) // zeroVersion is used when no version is specified (treated as 0.0.0). zeroVersion = semver.MustParse("0.0.0") + // currentVersion is the version of this binary (CLI or daemon). It's injected via an ldflag at build time. + // Fall back to zeroVersion if the injected string isn't valid semver. + currentVersion = parseVersionOrZero(version.String()) // Pre-parsed minimum versions for comparison. minClientVersion = semver.MustParse(MinClientVersion) minServerVersion = semver.MustParse(MinServerVersion) @@ -55,6 +56,15 @@ var ( WarnWriter io.Writer = os.Stderr ) +// parseVersionOrZero parses v as a semver, returning zeroVersion if parsing fails. +func parseVersionOrZero(v string) *semver.Version { + parsed, err := semver.NewVersion(v) + if err != nil { + return zeroVersion + } + return parsed +} + func extractVersion(md metadata.MD, key string) *semver.Version { if md == nil { return zeroVersion diff --git a/internal/grpcversion/interceptor_test.go b/internal/grpcversion/interceptor_test.go index de28ec2c..e9150417 100644 --- a/internal/grpcversion/interceptor_test.go +++ b/internal/grpcversion/interceptor_test.go @@ -12,6 +12,47 @@ import ( "google.golang.org/grpc/status" ) +func TestParseVersionOrZero(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "valid semver", + input: "1.2.3", + expected: "1.2.3", + }, + { + name: "valid semver with prerelease", + input: "0.19.0-nightly-abc1234", + expected: "0.19.0-nightly-abc1234", + }, + { + name: "dev version", + input: "999.0.0-dev", + expected: "999.0.0-dev", + }, + { + name: "invalid ldflag-injected string falls back to zero", + input: "nightly-SNAPSHOT-abc1234", + expected: "0.0.0", + }, + { + name: "empty string falls back to zero", + input: "", + expected: "0.0.0", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := parseVersionOrZero(tt.input) + assert.Equal(t, tt.expected, got.String()) + }) + } +} + func TestExtractVersion(t *testing.T) { tests := []struct { name string