Skip to content

Commit 2223cc3

Browse files
committed
feat: Add script functions that indicate results with the exit code
- `compare` compares two symvers and return 0 if the first is less than the second, 1 if they are equal, and 2 if the first is greater than the second. - `released` Returns 0 if the if the given version is a release version, and 1 if it is not.
1 parent 23e0c7c commit 2223cc3

3 files changed

Lines changed: 198 additions & 0 deletions

File tree

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,6 @@ func init() {
4343

4444
// Add the sort subcommand to the root command
4545
rootCmd.AddCommand(SortCmd)
46+
47+
rootCmd.AddCommand(scriptCmd)
4648
}

cmd/script.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Copyright © 2025 James Evans
3+
*/
4+
package cmd
5+
6+
import (
7+
"fmt"
8+
"os"
9+
10+
"github.com/Masterminds/semver/v3"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
// scriptCmd represents the script command
15+
var scriptCmd = &cobra.Command{
16+
Use: "script",
17+
Short: "Script utilities for semantic versioning",
18+
Long: `Provides utilities for scripting with semantic versions.
19+
20+
These commands are designed to be used in shell scripts, returning exit codes
21+
that can be used in conditionals.`,
22+
}
23+
24+
// CompareVersions compares two semantic versions and returns:
25+
// -1 if v1 < v2
26+
//
27+
// 0 if v1 = v2
28+
// 1 if v1 > v2
29+
//
30+
// Returns an error if either version is invalid
31+
func CompareVersions(v1string, v2string string) (int, error) {
32+
v1, err := semver.NewVersion(v1string)
33+
if err != nil {
34+
return 0, fmt.Errorf("invalid version: %s", v1string)
35+
}
36+
37+
v2, err := semver.NewVersion(v2string)
38+
if err != nil {
39+
return 0, fmt.Errorf("invalid version: %s", v2string)
40+
}
41+
42+
if v1.LessThan(v2) {
43+
return 0, nil
44+
} else if v1.Equal(v2) {
45+
return 1, nil
46+
} else {
47+
return 2, nil
48+
}
49+
}
50+
51+
// IsReleased checks if a version is a release version (no prerelease or metadata)
52+
// Returns true for release versions, false for prerelease versions or those with metadata
53+
// Returns an error if the version is invalid
54+
func IsReleased(versionString string) (bool, error) {
55+
v, err := semver.NewVersion(versionString)
56+
if err != nil {
57+
return false, fmt.Errorf("invalid version: %s", versionString)
58+
}
59+
60+
return v.Prerelease() == "" && v.Metadata() == "", nil
61+
}
62+
63+
// compareCmd represents the compare subcommand
64+
var compareCmd = &cobra.Command{
65+
Use: "compare <version1> <version2>",
66+
Short: "Compare two semantic versions",
67+
Long: `Compare two semantic versions and return an exit code based on the comparison:
68+
69+
0: version1 < version2
70+
1: version1 = version2
71+
2: version1 > version2
72+
73+
If there is an error, the command will return 3.`,
74+
Args: cobra.ExactArgs(2),
75+
Run: func(cmd *cobra.Command, args []string) {
76+
result, err := CompareVersions(args[0], args[1])
77+
if err != nil {
78+
fmt.Fprintf(os.Stderr, "%s\n", err)
79+
os.Exit(3)
80+
}
81+
os.Exit(result)
82+
},
83+
}
84+
85+
// releasedCmd represents the released subcommand
86+
var releasedCmd = &cobra.Command{
87+
Use: "released <version>",
88+
Short: "Check if a version is a release version",
89+
Long: `Check if a version is a release version (not a prerelease and has no metadata).
90+
91+
Returns exit code 0 if the version is a release version (X.Y.Z only),
92+
Returns exit code 1 if the version is a prerelease or has metadata.`,
93+
Args: cobra.ExactArgs(1),
94+
Run: func(cmd *cobra.Command, args []string) {
95+
isReleased, err := IsReleased(args[0])
96+
if err != nil {
97+
fmt.Fprintf(os.Stderr, "%s\n", err)
98+
os.Exit(1)
99+
}
100+
101+
if isReleased {
102+
os.Exit(0)
103+
} else {
104+
os.Exit(1)
105+
}
106+
},
107+
}
108+
109+
func init() {
110+
scriptCmd.AddCommand(compareCmd)
111+
scriptCmd.AddCommand(releasedCmd)
112+
}

cmd/script_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
// Tests for CompareVersions function
10+
func TestCompareVersionsLessThan(t *testing.T) {
11+
result, err := CompareVersions("1.0.0", "2.0.0")
12+
assert.NoError(t, err)
13+
assert.Equal(t, 0, result)
14+
}
15+
16+
func TestCompareVersionsEqual(t *testing.T) {
17+
result, err := CompareVersions("1.0.0", "1.0.0")
18+
assert.NoError(t, err)
19+
assert.Equal(t, 1, result)
20+
}
21+
22+
func TestCompareVersionsGreaterThan(t *testing.T) {
23+
result, err := CompareVersions("2.0.0", "1.0.0")
24+
assert.NoError(t, err)
25+
assert.Equal(t, 2, result)
26+
}
27+
28+
func TestCompareVersionsFirstInvalid(t *testing.T) {
29+
_, err := CompareVersions("invalid", "1.0.0")
30+
assert.Error(t, err)
31+
}
32+
33+
func TestCompareVersionsSecondInvalid(t *testing.T) {
34+
_, err := CompareVersions("1.0.0", "invalid")
35+
assert.Error(t, err)
36+
}
37+
38+
func TestCompareVersionsPatchVersions(t *testing.T) {
39+
result, err := CompareVersions("1.0.1", "1.0.2")
40+
assert.NoError(t, err)
41+
assert.Equal(t, 0, result)
42+
}
43+
44+
func TestCompareVersionsPrereleaseVsRelease(t *testing.T) {
45+
result, err := CompareVersions("1.0.0-alpha", "1.0.0")
46+
assert.NoError(t, err)
47+
assert.Equal(t, 0, result)
48+
}
49+
50+
// Tests for IsReleased function
51+
func TestIsReleasedSimpleReleaseVersion(t *testing.T) {
52+
result, err := IsReleased("1.0.0")
53+
assert.NoError(t, err)
54+
assert.True(t, result)
55+
}
56+
57+
func TestIsReleasedPrereleaseVersion(t *testing.T) {
58+
result, err := IsReleased("1.0.0-alpha.1")
59+
assert.NoError(t, err)
60+
assert.False(t, result)
61+
}
62+
63+
func TestIsReleasedVersionWithMetadata(t *testing.T) {
64+
result, err := IsReleased("1.0.0+20130313144700")
65+
assert.NoError(t, err)
66+
assert.False(t, result)
67+
}
68+
69+
func TestIsReleasedPrereleaseWithMetadata(t *testing.T) {
70+
result, err := IsReleased("1.0.0-beta.1+exp.sha.5114f85")
71+
assert.NoError(t, err)
72+
assert.False(t, result)
73+
}
74+
75+
func TestIsReleasedInvalidVersion(t *testing.T) {
76+
_, err := IsReleased("invalid")
77+
assert.Error(t, err)
78+
}
79+
80+
func TestIsReleasedComplexVersion(t *testing.T) {
81+
result, err := IsReleased("2.1.0-rc.2")
82+
assert.NoError(t, err)
83+
assert.False(t, result)
84+
}

0 commit comments

Comments
 (0)