Skip to content

Commit 48db607

Browse files
committed
Merge origin/main into feat/issue-auto-implement-setup; keep workflow fix (no workflows: write)
Made-with: Cursor
2 parents f3e86c1 + ec899e5 commit 48db607

2 files changed

Lines changed: 138 additions & 2 deletions

File tree

pkg/version/version.go

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"strconv"
78
"strings"
89

910
"github.com/google/go-github/v28/github"
@@ -37,8 +38,113 @@ func CheckLatestVersion() {
3738
}
3839
}
3940

40-
func needsToUpgrade(version, latest string) bool {
41-
return latest != "" && (strings.TrimPrefix(latest, "v") != strings.TrimPrefix(version, "v"))
41+
func needsToUpgrade(current, latest string) bool {
42+
if latest == "" {
43+
return false
44+
}
45+
46+
current = strings.TrimPrefix(current, "v")
47+
latest = strings.TrimPrefix(latest, "v")
48+
49+
currentIsPreRelease := strings.Contains(current, "-")
50+
latestIsPreRelease := strings.Contains(latest, "-")
51+
52+
// Don't suggest upgrading from a GA release to a pre-release version.
53+
if !currentIsPreRelease && latestIsPreRelease {
54+
return false
55+
}
56+
57+
return semverGreater(latest, current)
58+
}
59+
60+
// semverGreater returns true if a is semantically greater than b.
61+
// Both a and b must not have a "v" prefix.
62+
func semverGreater(a, b string) bool {
63+
aNums, aPre := parseVersion(a)
64+
bNums, bPre := parseVersion(b)
65+
66+
maxLen := len(aNums)
67+
if len(bNums) > maxLen {
68+
maxLen = len(bNums)
69+
}
70+
for i := 0; i < maxLen; i++ {
71+
var av, bv int
72+
if i < len(aNums) {
73+
av = aNums[i]
74+
}
75+
if i < len(bNums) {
76+
bv = bNums[i]
77+
}
78+
if av != bv {
79+
return av > bv
80+
}
81+
}
82+
83+
// Same base version: GA (no pre-release) beats any pre-release.
84+
if aPre == "" && bPre != "" {
85+
return true
86+
}
87+
if aPre != "" && bPre == "" {
88+
return false
89+
}
90+
91+
return comparePreRelease(aPre, bPre) > 0
92+
}
93+
94+
// parseVersion splits a version string (without "v" prefix) into its
95+
// numeric components and an optional pre-release identifier.
96+
func parseVersion(v string) (nums []int, pre string) {
97+
parts := strings.SplitN(v, "-", 2)
98+
if len(parts) > 1 {
99+
pre = parts[1]
100+
}
101+
for _, s := range strings.Split(parts[0], ".") {
102+
n, _ := strconv.Atoi(s)
103+
nums = append(nums, n)
104+
}
105+
return
106+
}
107+
108+
// comparePreRelease compares two pre-release strings dot-by-dot.
109+
// Returns a positive value if a > b, zero if equal, negative if a < b.
110+
func comparePreRelease(a, b string) int {
111+
if a == b {
112+
return 0
113+
}
114+
aParts := strings.Split(a, ".")
115+
bParts := strings.Split(b, ".")
116+
117+
maxLen := len(aParts)
118+
if len(bParts) > maxLen {
119+
maxLen = len(bParts)
120+
}
121+
122+
for i := 0; i < maxLen; i++ {
123+
if i >= len(aParts) {
124+
return -1
125+
}
126+
if i >= len(bParts) {
127+
return 1
128+
}
129+
aN, aErr := strconv.Atoi(aParts[i])
130+
bN, bErr := strconv.Atoi(bParts[i])
131+
if aErr == nil && bErr == nil {
132+
if aN != bN {
133+
if aN > bN {
134+
return 1
135+
}
136+
return -1
137+
}
138+
} else {
139+
if aParts[i] > bParts[i] {
140+
return 1
141+
}
142+
if aParts[i] < bParts[i] {
143+
return -1
144+
}
145+
}
146+
}
147+
return 0
42148
}
43149

44150
func getLatestVersion() string {

pkg/version/version_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,39 @@ import (
77
)
88

99
func TestNeedsToUpgrade(t *testing.T) {
10+
// Basic same-version checks (v-prefix normalisation).
1011
require.False(t, needsToUpgrade("4.2.4.2", "v4.2.4.2"))
1112
require.False(t, needsToUpgrade("4.2.4.2", "4.2.4.2"))
13+
14+
// GA to newer GA — should upgrade.
1215
require.True(t, needsToUpgrade("4.2.4.2", "4.2.4.3"))
1316
require.True(t, needsToUpgrade("4.2.4.2", "v4.2.4.3"))
1417
require.True(t, needsToUpgrade("v4.2.4.2", "v4.2.4.3"))
18+
19+
// Minor/major version bump where numeric comparison matters
20+
// (string comparison would wrongly treat 1.9.x > 1.10.x).
21+
require.True(t, needsToUpgrade("1.9.1", "1.10.0"))
22+
require.False(t, needsToUpgrade("1.10.0", "1.9.1"))
23+
24+
// GA current, pre-release latest — must NOT suggest upgrade.
25+
require.False(t, needsToUpgrade("1.9.1", "1.10.0-beta.4"))
26+
require.False(t, needsToUpgrade("1.9.1", "v1.10.0-beta.4"))
27+
require.False(t, needsToUpgrade("1.10.0", "1.10.1-beta.1"))
28+
29+
// Pre-release current, newer GA latest — should upgrade.
30+
require.True(t, needsToUpgrade("1.9.0-beta.4", "1.9.0"))
31+
require.True(t, needsToUpgrade("1.10.0-beta.4", "1.10.0"))
32+
33+
// Pre-release current, older GA latest — should NOT upgrade.
34+
require.False(t, needsToUpgrade("1.10.0-beta.4", "1.9.1"))
35+
36+
// Pre-release to newer pre-release — should upgrade.
37+
require.True(t, needsToUpgrade("1.9.0-beta.3", "1.9.0-beta.4"))
38+
require.True(t, needsToUpgrade("1.9.0-beta.9", "1.9.0-beta.10"))
39+
40+
// Pre-release to older pre-release — should NOT upgrade.
41+
require.False(t, needsToUpgrade("1.9.0-beta.4", "1.9.0-beta.3"))
42+
43+
// Same pre-release — should NOT upgrade.
44+
require.False(t, needsToUpgrade("1.9.0-beta.4", "1.9.0-beta.4"))
1545
}

0 commit comments

Comments
 (0)