Skip to content

Commit c808587

Browse files
authored
cli: do not show new pre-releases on stable (#2813)
1 parent 2bf1200 commit c808587

File tree

2 files changed

+334
-2
lines changed

2 files changed

+334
-2
lines changed

cmd/headscale/cli/root.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"runtime"
77
"slices"
8+
"strings"
89

910
"github.com/juanfont/headscale/hscontrol/types"
1011
"github.com/rs/zerolog"
@@ -75,8 +76,9 @@ func initConfig() {
7576
if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") &&
7677
!versionInfo.Dirty {
7778
githubTag := &latest.GithubTag{
78-
Owner: "juanfont",
79-
Repository: "headscale",
79+
Owner: "juanfont",
80+
Repository: "headscale",
81+
TagFilterFunc: filterPreReleasesIfStable(func() string { return versionInfo.Version }),
8082
}
8183
res, err := latest.Check(githubTag, versionInfo.Version)
8284
if err == nil && res.Outdated {
@@ -91,6 +93,43 @@ func initConfig() {
9193
}
9294
}
9395

96+
var prereleases = []string{"alpha", "beta", "rc", "dev"}
97+
98+
func isPreReleaseVersion(version string) bool {
99+
for _, unstable := range prereleases {
100+
if strings.Contains(version, unstable) {
101+
return true
102+
}
103+
}
104+
return false
105+
}
106+
107+
// filterPreReleasesIfStable returns a function that filters out
108+
// pre-release tags if the current version is stable.
109+
// If the current version is a pre-release, it does not filter anything.
110+
// versionFunc is a function that returns the current version string, it is
111+
// a func for testability.
112+
func filterPreReleasesIfStable(versionFunc func() string) func(string) bool {
113+
return func(tag string) bool {
114+
version := versionFunc()
115+
116+
// If we are on a pre-release version, then we do not filter anything
117+
// as we want to recommend the user the latest pre-release.
118+
if isPreReleaseVersion(version) {
119+
return false
120+
}
121+
122+
// If we are on a stable release, filter out pre-releases.
123+
for _, ignore := range prereleases {
124+
if strings.Contains(tag, ignore) {
125+
return true
126+
}
127+
}
128+
129+
return false
130+
}
131+
}
132+
94133
var rootCmd = &cobra.Command{
95134
Use: "headscale",
96135
Short: "headscale - a Tailscale control server",

cmd/headscale/cli/root_test.go

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
package cli
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestFilterPreReleasesIfStable(t *testing.T) {
8+
tests := []struct {
9+
name string
10+
currentVersion string
11+
tag string
12+
expectedFilter bool
13+
description string
14+
}{
15+
{
16+
name: "stable version filters alpha tag",
17+
currentVersion: "0.23.0",
18+
tag: "v0.24.0-alpha.1",
19+
expectedFilter: true,
20+
description: "When on stable release, alpha tags should be filtered",
21+
},
22+
{
23+
name: "stable version filters beta tag",
24+
currentVersion: "0.23.0",
25+
tag: "v0.24.0-beta.2",
26+
expectedFilter: true,
27+
description: "When on stable release, beta tags should be filtered",
28+
},
29+
{
30+
name: "stable version filters rc tag",
31+
currentVersion: "0.23.0",
32+
tag: "v0.24.0-rc.1",
33+
expectedFilter: true,
34+
description: "When on stable release, rc tags should be filtered",
35+
},
36+
{
37+
name: "stable version allows stable tag",
38+
currentVersion: "0.23.0",
39+
tag: "v0.24.0",
40+
expectedFilter: false,
41+
description: "When on stable release, stable tags should not be filtered",
42+
},
43+
{
44+
name: "alpha version allows alpha tag",
45+
currentVersion: "0.23.0-alpha.1",
46+
tag: "v0.24.0-alpha.2",
47+
expectedFilter: false,
48+
description: "When on alpha release, alpha tags should not be filtered",
49+
},
50+
{
51+
name: "alpha version allows beta tag",
52+
currentVersion: "0.23.0-alpha.1",
53+
tag: "v0.24.0-beta.1",
54+
expectedFilter: false,
55+
description: "When on alpha release, beta tags should not be filtered",
56+
},
57+
{
58+
name: "alpha version allows rc tag",
59+
currentVersion: "0.23.0-alpha.1",
60+
tag: "v0.24.0-rc.1",
61+
expectedFilter: false,
62+
description: "When on alpha release, rc tags should not be filtered",
63+
},
64+
{
65+
name: "alpha version allows stable tag",
66+
currentVersion: "0.23.0-alpha.1",
67+
tag: "v0.24.0",
68+
expectedFilter: false,
69+
description: "When on alpha release, stable tags should not be filtered",
70+
},
71+
{
72+
name: "beta version allows alpha tag",
73+
currentVersion: "0.23.0-beta.1",
74+
tag: "v0.24.0-alpha.1",
75+
expectedFilter: false,
76+
description: "When on beta release, alpha tags should not be filtered",
77+
},
78+
{
79+
name: "beta version allows beta tag",
80+
currentVersion: "0.23.0-beta.2",
81+
tag: "v0.24.0-beta.3",
82+
expectedFilter: false,
83+
description: "When on beta release, beta tags should not be filtered",
84+
},
85+
{
86+
name: "beta version allows rc tag",
87+
currentVersion: "0.23.0-beta.1",
88+
tag: "v0.24.0-rc.1",
89+
expectedFilter: false,
90+
description: "When on beta release, rc tags should not be filtered",
91+
},
92+
{
93+
name: "beta version allows stable tag",
94+
currentVersion: "0.23.0-beta.1",
95+
tag: "v0.24.0",
96+
expectedFilter: false,
97+
description: "When on beta release, stable tags should not be filtered",
98+
},
99+
{
100+
name: "rc version allows alpha tag",
101+
currentVersion: "0.23.0-rc.1",
102+
tag: "v0.24.0-alpha.1",
103+
expectedFilter: false,
104+
description: "When on rc release, alpha tags should not be filtered",
105+
},
106+
{
107+
name: "rc version allows beta tag",
108+
currentVersion: "0.23.0-rc.1",
109+
tag: "v0.24.0-beta.1",
110+
expectedFilter: false,
111+
description: "When on rc release, beta tags should not be filtered",
112+
},
113+
{
114+
name: "rc version allows rc tag",
115+
currentVersion: "0.23.0-rc.2",
116+
tag: "v0.24.0-rc.3",
117+
expectedFilter: false,
118+
description: "When on rc release, rc tags should not be filtered",
119+
},
120+
{
121+
name: "rc version allows stable tag",
122+
currentVersion: "0.23.0-rc.1",
123+
tag: "v0.24.0",
124+
expectedFilter: false,
125+
description: "When on rc release, stable tags should not be filtered",
126+
},
127+
{
128+
name: "stable version with patch filters alpha",
129+
currentVersion: "0.23.1",
130+
tag: "v0.24.0-alpha.1",
131+
expectedFilter: true,
132+
description: "Stable version with patch number should filter alpha tags",
133+
},
134+
{
135+
name: "stable version with patch allows stable",
136+
currentVersion: "0.23.1",
137+
tag: "v0.24.0",
138+
expectedFilter: false,
139+
description: "Stable version with patch number should allow stable tags",
140+
},
141+
{
142+
name: "tag with alpha substring in version number",
143+
currentVersion: "0.23.0",
144+
tag: "v1.0.0-alpha.1",
145+
expectedFilter: true,
146+
description: "Tags with alpha in version string should be filtered on stable",
147+
},
148+
{
149+
name: "tag with beta substring in version number",
150+
currentVersion: "0.23.0",
151+
tag: "v1.0.0-beta.1",
152+
expectedFilter: true,
153+
description: "Tags with beta in version string should be filtered on stable",
154+
},
155+
{
156+
name: "tag with rc substring in version number",
157+
currentVersion: "0.23.0",
158+
tag: "v1.0.0-rc.1",
159+
expectedFilter: true,
160+
description: "Tags with rc in version string should be filtered on stable",
161+
},
162+
{
163+
name: "empty tag on stable version",
164+
currentVersion: "0.23.0",
165+
tag: "",
166+
expectedFilter: false,
167+
description: "Empty tags should not be filtered",
168+
},
169+
{
170+
name: "dev version allows all tags",
171+
currentVersion: "0.23.0-dev",
172+
tag: "v0.24.0-alpha.1",
173+
expectedFilter: false,
174+
description: "Dev versions should not filter any tags (pre-release allows all)",
175+
},
176+
{
177+
name: "stable version filters dev tag",
178+
currentVersion: "0.23.0",
179+
tag: "v0.24.0-dev",
180+
expectedFilter: true,
181+
description: "When on stable release, dev tags should be filtered",
182+
},
183+
{
184+
name: "dev version allows dev tag",
185+
currentVersion: "0.23.0-dev",
186+
tag: "v0.24.0-dev.1",
187+
expectedFilter: false,
188+
description: "When on dev release, dev tags should not be filtered",
189+
},
190+
{
191+
name: "dev version allows stable tag",
192+
currentVersion: "0.23.0-dev",
193+
tag: "v0.24.0",
194+
expectedFilter: false,
195+
description: "When on dev release, stable tags should not be filtered",
196+
},
197+
}
198+
199+
for _, tt := range tests {
200+
t.Run(tt.name, func(t *testing.T) {
201+
result := filterPreReleasesIfStable(func() string { return tt.currentVersion })(tt.tag)
202+
if result != tt.expectedFilter {
203+
t.Errorf("%s: got %v, want %v\nDescription: %s\nCurrent version: %s, Tag: %s",
204+
tt.name,
205+
result,
206+
tt.expectedFilter,
207+
tt.description,
208+
tt.currentVersion,
209+
tt.tag,
210+
)
211+
}
212+
})
213+
}
214+
}
215+
216+
func TestIsPreReleaseVersion(t *testing.T) {
217+
tests := []struct {
218+
name string
219+
version string
220+
expected bool
221+
description string
222+
}{
223+
{
224+
name: "stable version",
225+
version: "0.23.0",
226+
expected: false,
227+
description: "Stable version should not be pre-release",
228+
},
229+
{
230+
name: "alpha version",
231+
version: "0.23.0-alpha.1",
232+
expected: true,
233+
description: "Alpha version should be pre-release",
234+
},
235+
{
236+
name: "beta version",
237+
version: "0.23.0-beta.1",
238+
expected: true,
239+
description: "Beta version should be pre-release",
240+
},
241+
{
242+
name: "rc version",
243+
version: "0.23.0-rc.1",
244+
expected: true,
245+
description: "RC version should be pre-release",
246+
},
247+
{
248+
name: "version with alpha substring",
249+
version: "0.23.0-alphabetical",
250+
expected: true,
251+
description: "Version containing 'alpha' should be pre-release",
252+
},
253+
{
254+
name: "version with beta substring",
255+
version: "0.23.0-betamax",
256+
expected: true,
257+
description: "Version containing 'beta' should be pre-release",
258+
},
259+
{
260+
name: "dev version",
261+
version: "0.23.0-dev",
262+
expected: true,
263+
description: "Dev version should be pre-release",
264+
},
265+
{
266+
name: "empty version",
267+
version: "",
268+
expected: false,
269+
description: "Empty version should not be pre-release",
270+
},
271+
{
272+
name: "version with patch number",
273+
version: "0.23.1",
274+
expected: false,
275+
description: "Stable version with patch should not be pre-release",
276+
},
277+
}
278+
279+
for _, tt := range tests {
280+
t.Run(tt.name, func(t *testing.T) {
281+
result := isPreReleaseVersion(tt.version)
282+
if result != tt.expected {
283+
t.Errorf("%s: got %v, want %v\nDescription: %s\nVersion: %s",
284+
tt.name,
285+
result,
286+
tt.expected,
287+
tt.description,
288+
tt.version,
289+
)
290+
}
291+
})
292+
}
293+
}

0 commit comments

Comments
 (0)