From 77ac28997d6fde906fb2b512fac4d64ae24a98d6 Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Tue, 17 Feb 2026 15:10:58 +0100 Subject: [PATCH 1/2] ignore git describe suffix for dev builds when comparing versions Entire-Checkpoint: ae622cf9b522 --- cmd/entire/cli/versioncheck/versioncheck.go | 51 +++++++++++++++++++ .../cli/versioncheck/versioncheck_test.go | 34 +++++++++++++ 2 files changed, 85 insertions(+) diff --git a/cmd/entire/cli/versioncheck/versioncheck.go b/cmd/entire/cli/versioncheck/versioncheck.go index 11c832c6d..ad66e75a6 100644 --- a/cmd/entire/cli/versioncheck/versioncheck.go +++ b/cmd/entire/cli/versioncheck/versioncheck.go @@ -223,6 +223,11 @@ func parseGitHubRelease(body []byte) (string, error) { // isOutdated compares current and latest versions using semantic versioning. // Returns true if current < latest. func isOutdated(current, latest string) bool { + // Strip git describe suffix (e.g., "v0.4.4-76-g230b49bf-dirty" → "v0.4.4"). + // git describe appends "-N-gHASH" (and optionally "-dirty") to the tag, + // which semver misinterprets as a prerelease (sorting before the release). + current = stripGitDescribeSuffix(current) + // Ensure versions have "v" prefix for semver package if !strings.HasPrefix(current, "v") { current = "v" + current @@ -235,6 +240,52 @@ func isOutdated(current, latest string) bool { return semver.Compare(current, latest) < 0 } +// stripGitDescribeSuffix removes the git describe suffix from a version string. +// "v0.4.4-76-g230b49bf-dirty" → "v0.4.4" +// "v0.4.4-76-g230b49bf" → "v0.4.4" +// "v0.4.4" → "v0.4.4" (no change) +func stripGitDescribeSuffix(version string) string { + // git describe format: --g[-dirty] + // The "-g" prefix on the hash is the distinguishing marker. + // Look for "--g" pattern. + v := version + if strings.HasSuffix(v, "-dirty") { + v = strings.TrimSuffix(v, "-dirty") + } + // Find the last "-g" segment + lastG := strings.LastIndex(v, "-g") + if lastG < 0 { + return version + } + // Verify the part after "-g" is hex characters (git hash) + hash := v[lastG+2:] + if len(hash) == 0 { + return version + } + for _, c := range hash { + if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + return version + } + } + // Now strip the "--g" part: find the dash before the commit count + prefix := v[:lastG] + lastDash := strings.LastIndex(prefix, "-") + if lastDash < 0 { + return version + } + // Verify the part between the dashes is a number (commit count) + count := prefix[lastDash+1:] + if len(count) == 0 { + return version + } + for _, c := range count { + if c < '0' || c > '9' { + return version + } + } + return prefix[:lastDash] +} + // updateCommand returns the appropriate update instruction based on how the binary was installed. func updateCommand() string { execPath, err := os.Executable() diff --git a/cmd/entire/cli/versioncheck/versioncheck_test.go b/cmd/entire/cli/versioncheck/versioncheck_test.go index 61992db72..faebc1187 100644 --- a/cmd/entire/cli/versioncheck/versioncheck_test.go +++ b/cmd/entire/cli/versioncheck/versioncheck_test.go @@ -37,6 +37,13 @@ func TestIsOutdated(t *testing.T) { // Pre-release versions (semver uses hyphen) {"1.0.0-rc1", "1.0.0", true, "prerelease in current"}, {"1.0.0", "1.0.1-rc1", true, "prerelease in latest is still newer"}, + + // Git describe format (should strip suffix before comparing) + {"v0.4.4-76-g230b49bf-dirty", "v0.4.4", false, "git describe dirty build is not outdated"}, + {"v0.4.4-76-g230b49bf", "v0.4.4", false, "git describe build is not outdated"}, + {"v0.4.4-76-g230b49bf-dirty", "v0.4.5", true, "git describe build outdated by newer release"}, + {"v0.4.4-1-gabcdef0", "v0.4.4", false, "git describe 1 commit ahead"}, + {"v1.0.0-100-g1234567890ab-dirty", "v1.0.0", false, "git describe long hash dirty"}, } for _, tt := range tests { @@ -49,6 +56,33 @@ func TestIsOutdated(t *testing.T) { } } +func TestStripGitDescribeSuffix(t *testing.T) { + tests := []struct { + input string + want string + }{ + {"v0.4.4-76-g230b49bf-dirty", "v0.4.4"}, + {"v0.4.4-76-g230b49bf", "v0.4.4"}, + {"v0.4.4-1-gabcdef0", "v0.4.4"}, + {"v1.0.0-100-g1234567890ab-dirty", "v1.0.0"}, + {"v0.4.4", "v0.4.4"}, + {"1.0.0", "1.0.0"}, + {"v1.0.0-rc1", "v1.0.0-rc1"}, // real prerelease, not git describe + {"v1.0.0-beta.1", "v1.0.0-beta.1"}, // real prerelease with dots + {"v1.0.0-dirty", "v1.0.0-dirty"}, // just dirty without commit info + {"v1.0.0-1-gXYZ", "v1.0.0-1-gXYZ"}, // non-hex hash, keep as-is + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got := stripGitDescribeSuffix(tt.input) + if got != tt.want { + t.Errorf("stripGitDescribeSuffix(%q) = %q, want %q", tt.input, got, tt.want) + } + }) + } +} + func TestCacheReadWrite(t *testing.T) { // Create a temporary directory for the test tmpDir := t.TempDir() From 98f41bbf8c4c5b8feb5c999d48fe97cbb1c621a3 Mon Sep 17 00:00:00 2001 From: Victor Gutierrez Calderon Date: Wed, 18 Feb 2026 14:24:08 +1100 Subject: [PATCH 2/2] skip version checks on prerelease versions --- cmd/entire/cli/versioncheck/versioncheck.go | 57 ++----------------- .../cli/versioncheck/versioncheck_test.go | 33 +---------- 2 files changed, 9 insertions(+), 81 deletions(-) diff --git a/cmd/entire/cli/versioncheck/versioncheck.go b/cmd/entire/cli/versioncheck/versioncheck.go index ad66e75a6..e5ec0a771 100644 --- a/cmd/entire/cli/versioncheck/versioncheck.go +++ b/cmd/entire/cli/versioncheck/versioncheck.go @@ -223,11 +223,6 @@ func parseGitHubRelease(body []byte) (string, error) { // isOutdated compares current and latest versions using semantic versioning. // Returns true if current < latest. func isOutdated(current, latest string) bool { - // Strip git describe suffix (e.g., "v0.4.4-76-g230b49bf-dirty" → "v0.4.4"). - // git describe appends "-N-gHASH" (and optionally "-dirty") to the tag, - // which semver misinterprets as a prerelease (sorting before the release). - current = stripGitDescribeSuffix(current) - // Ensure versions have "v" prefix for semver package if !strings.HasPrefix(current, "v") { current = "v" + current @@ -236,56 +231,16 @@ func isOutdated(current, latest string) bool { latest = "v" + latest } + // Skip notification for prerelease versions (development builds). + // We don't publish prerelease versions, so these are development builds and shouldn't trigger update notifications. + if semver.Prerelease(current) != "" { + return false + } + // semver.Compare returns -1 if current < latest return semver.Compare(current, latest) < 0 } -// stripGitDescribeSuffix removes the git describe suffix from a version string. -// "v0.4.4-76-g230b49bf-dirty" → "v0.4.4" -// "v0.4.4-76-g230b49bf" → "v0.4.4" -// "v0.4.4" → "v0.4.4" (no change) -func stripGitDescribeSuffix(version string) string { - // git describe format: --g[-dirty] - // The "-g" prefix on the hash is the distinguishing marker. - // Look for "--g" pattern. - v := version - if strings.HasSuffix(v, "-dirty") { - v = strings.TrimSuffix(v, "-dirty") - } - // Find the last "-g" segment - lastG := strings.LastIndex(v, "-g") - if lastG < 0 { - return version - } - // Verify the part after "-g" is hex characters (git hash) - hash := v[lastG+2:] - if len(hash) == 0 { - return version - } - for _, c := range hash { - if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { - return version - } - } - // Now strip the "--g" part: find the dash before the commit count - prefix := v[:lastG] - lastDash := strings.LastIndex(prefix, "-") - if lastDash < 0 { - return version - } - // Verify the part between the dashes is a number (commit count) - count := prefix[lastDash+1:] - if len(count) == 0 { - return version - } - for _, c := range count { - if c < '0' || c > '9' { - return version - } - } - return prefix[:lastDash] -} - // updateCommand returns the appropriate update instruction based on how the binary was installed. func updateCommand() string { execPath, err := os.Executable() diff --git a/cmd/entire/cli/versioncheck/versioncheck_test.go b/cmd/entire/cli/versioncheck/versioncheck_test.go index faebc1187..1b47d193b 100644 --- a/cmd/entire/cli/versioncheck/versioncheck_test.go +++ b/cmd/entire/cli/versioncheck/versioncheck_test.go @@ -35,14 +35,14 @@ func TestIsOutdated(t *testing.T) { {"1.0.0", "v1.0.1", true, "mixed v prefix reversed"}, // Pre-release versions (semver uses hyphen) - {"1.0.0-rc1", "1.0.0", true, "prerelease in current"}, + {"1.0.0-rc1", "1.0.0", false, "prerelease in current"}, {"1.0.0", "1.0.1-rc1", true, "prerelease in latest is still newer"}, // Git describe format (should strip suffix before comparing) {"v0.4.4-76-g230b49bf-dirty", "v0.4.4", false, "git describe dirty build is not outdated"}, {"v0.4.4-76-g230b49bf", "v0.4.4", false, "git describe build is not outdated"}, - {"v0.4.4-76-g230b49bf-dirty", "v0.4.5", true, "git describe build outdated by newer release"}, - {"v0.4.4-1-gabcdef0", "v0.4.4", false, "git describe 1 commit ahead"}, + {"v0.4.4-76-g230b49bf-dirty", "v0.4.5", false, "git describe build is not outdated"}, + {"v0.4.4-1-gabcdef0", "v0.4.4", false, "git describe 1 commit ahead and is not outdated"}, {"v1.0.0-100-g1234567890ab-dirty", "v1.0.0", false, "git describe long hash dirty"}, } @@ -56,33 +56,6 @@ func TestIsOutdated(t *testing.T) { } } -func TestStripGitDescribeSuffix(t *testing.T) { - tests := []struct { - input string - want string - }{ - {"v0.4.4-76-g230b49bf-dirty", "v0.4.4"}, - {"v0.4.4-76-g230b49bf", "v0.4.4"}, - {"v0.4.4-1-gabcdef0", "v0.4.4"}, - {"v1.0.0-100-g1234567890ab-dirty", "v1.0.0"}, - {"v0.4.4", "v0.4.4"}, - {"1.0.0", "1.0.0"}, - {"v1.0.0-rc1", "v1.0.0-rc1"}, // real prerelease, not git describe - {"v1.0.0-beta.1", "v1.0.0-beta.1"}, // real prerelease with dots - {"v1.0.0-dirty", "v1.0.0-dirty"}, // just dirty without commit info - {"v1.0.0-1-gXYZ", "v1.0.0-1-gXYZ"}, // non-hex hash, keep as-is - } - - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - got := stripGitDescribeSuffix(tt.input) - if got != tt.want { - t.Errorf("stripGitDescribeSuffix(%q) = %q, want %q", tt.input, got, tt.want) - } - }) - } -} - func TestCacheReadWrite(t *testing.T) { // Create a temporary directory for the test tmpDir := t.TempDir()