Skip to content

Commit 7702829

Browse files
authored
feat: add ability to compare semver versions (#2047)
Towards #2041 It's possible the configured value in config.yaml is too old. We will need to be able to compare 2 semver strings.
1 parent 8c3ff57 commit 7702829

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

internal/semver/semver.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,51 @@ func Parse(versionString string) (*Version, error) {
9191
return v, nil
9292
}
9393

94+
// Compare returns an integer comparing two versions.
95+
// The result is -1, 0, or 1 depending on whether v is less than, equal to, or greater than other.
96+
func (v *Version) Compare(other *Version) int {
97+
if v.Major < other.Major {
98+
return -1
99+
}
100+
if v.Major > other.Major {
101+
return 1
102+
}
103+
if v.Minor < other.Minor {
104+
return -1
105+
}
106+
if v.Minor > other.Minor {
107+
return 1
108+
}
109+
if v.Patch < other.Patch {
110+
return -1
111+
}
112+
if v.Patch > other.Patch {
113+
return 1
114+
}
115+
// a pre-release version is less than a non-pre-release version
116+
if v.Prerelease != "" && other.Prerelease == "" {
117+
return -1
118+
}
119+
if v.Prerelease == "" && other.Prerelease != "" {
120+
return 1
121+
}
122+
// lexical comparison between prerelease type (e.g. "alpha" vs "beta")
123+
if v.Prerelease < other.Prerelease {
124+
return -1
125+
}
126+
if v.Prerelease > other.Prerelease {
127+
return 1
128+
}
129+
// prerelease number (e.g. "alpha1" vs "alpha2")
130+
if v.PrereleaseNumber < other.PrereleaseNumber {
131+
return -1
132+
}
133+
if v.PrereleaseNumber > other.PrereleaseNumber {
134+
return 1
135+
}
136+
return 0
137+
}
138+
94139
// String formats a Version struct into a string.
95140
func (v *Version) String() string {
96141
version := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)

internal/semver/semver_test.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,148 @@ func TestDeriveNext(t *testing.T) {
241241
})
242242
}
243243
}
244+
245+
func TestCompare(t *testing.T) {
246+
for _, test := range []struct {
247+
name string
248+
versionA string
249+
versionB string
250+
want int
251+
}{
252+
{
253+
name: "equal",
254+
versionA: "1.2.3",
255+
versionB: "1.2.3",
256+
want: 0,
257+
},
258+
{
259+
name: "equal with pre-release",
260+
versionA: "1.2.3-alpha",
261+
versionB: "1.2.3-alpha",
262+
want: 0,
263+
},
264+
{
265+
name: "equal with pre-release and number",
266+
versionA: "1.2.3-alpha4",
267+
versionB: "1.2.3-alpha4",
268+
want: 0,
269+
},
270+
{
271+
name: "equal with pre-release and number, different separator",
272+
versionA: "1.2.3-alpha4",
273+
versionB: "1.2.3-alpha.4",
274+
want: 0,
275+
},
276+
{
277+
name: "less than patch",
278+
versionA: "1.2.3",
279+
versionB: "1.2.4",
280+
want: -1,
281+
},
282+
{
283+
name: "less than minor",
284+
versionA: "1.2.3",
285+
versionB: "1.3.0",
286+
want: -1,
287+
},
288+
{
289+
name: "less than major",
290+
versionA: "1.2.3",
291+
versionB: "2.0.0",
292+
want: -1,
293+
},
294+
{
295+
name: "less than prerelease",
296+
versionA: "1.2.3-alpha",
297+
versionB: "1.2.3-beta",
298+
want: -1,
299+
},
300+
{
301+
name: "less than prerelease number",
302+
versionA: "1.2.3-alpha1",
303+
versionB: "1.2.3-alpha2",
304+
want: -1,
305+
},
306+
{
307+
name: "less than prerelease number with separator",
308+
versionA: "1.2.3-alpha.1",
309+
versionB: "1.2.3-alpha.2",
310+
want: -1,
311+
},
312+
{
313+
name: "less than prerelease against stable",
314+
versionA: "1.2.3-alpha1",
315+
versionB: "1.2.3",
316+
want: -1,
317+
},
318+
{
319+
name: "less than prerelease without number",
320+
versionA: "1.2.3-alpha",
321+
versionB: "1.2.3-alpha1",
322+
want: -1,
323+
},
324+
{
325+
name: "greater than patch",
326+
versionA: "1.2.4",
327+
versionB: "1.2.3",
328+
want: 1,
329+
},
330+
{
331+
name: "greater than minor",
332+
versionA: "1.3.0",
333+
versionB: "1.2.3",
334+
want: 1,
335+
},
336+
{
337+
name: "greater than major",
338+
versionA: "2.0.0",
339+
versionB: "1.2.3",
340+
want: 1,
341+
},
342+
{
343+
name: "greater than prerelease",
344+
versionA: "1.2.3-beta",
345+
versionB: "1.2.3-alpha",
346+
want: 1,
347+
},
348+
{
349+
name: "greater than prerelease number",
350+
versionA: "1.2.3-alpha2",
351+
versionB: "1.2.3-alpha1",
352+
want: 1,
353+
},
354+
{
355+
name: "greater than prerelease number with separator",
356+
versionA: "1.2.3-alpha.2",
357+
versionB: "1.2.3-alpha.1",
358+
want: 1,
359+
},
360+
{
361+
name: "greater than prerelease against stable",
362+
versionA: "1.2.3",
363+
versionB: "1.2.3-alpha1",
364+
want: 1,
365+
},
366+
{
367+
name: "greater than prerelease without number",
368+
versionA: "1.2.3-alpha1",
369+
versionB: "1.2.3-alpha",
370+
want: 1,
371+
},
372+
} {
373+
t.Run(test.name, func(t *testing.T) {
374+
a, err := Parse(test.versionA)
375+
if err != nil {
376+
t.Fatalf("Parse() returned an error: %v", err)
377+
}
378+
b, err := Parse(test.versionB)
379+
if err != nil {
380+
t.Fatalf("Parse() returned an error: %v", err)
381+
}
382+
got := a.Compare(b)
383+
if diff := cmp.Diff(test.want, got); diff != "" {
384+
t.Errorf("TestCompare() returned diff (-want +got):\n%s", diff)
385+
}
386+
})
387+
}
388+
}

0 commit comments

Comments
 (0)