tangled
alpha
login
or
join now
selman.me
/
things
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
semver-parse: add bump subcommand
Selman Kayrancioglu
8 months ago
e6a3adf4
3a2d1e1b
+108
-14
1 changed file
expand all
collapse all
unified
split
semver-parse
main.go
+108
-14
semver-parse/main.go
reviewed
···
2
2
3
3
import (
4
4
"bufio"
5
5
+
"context"
5
6
"flag"
6
7
"fmt"
7
8
"io"
8
9
"os"
10
10
+
"regexp"
11
11
+
"strconv"
9
12
"strings"
10
13
11
14
"github.com/Masterminds/semver/v3"
15
15
+
"github.com/peterbourgon/ff/v3/ffcli"
12
16
)
13
17
14
18
func main() {
15
19
if err := realMain(
20
20
+
context.Background(),
16
21
os.Args,
17
22
os.Stdin,
18
23
os.Stdout,
19
24
os.Stderr,
20
25
); err != nil {
21
26
fmt.Fprintln(os.Stderr, err)
27
27
+
os.Exit(1)
22
28
}
23
29
}
24
30
25
31
func realMain(
26
26
-
_ []string,
32
32
+
ctx context.Context,
33
33
+
args []string,
27
34
stdin io.Reader,
28
35
stdout io.Writer,
29
36
stderr io.Writer,
30
37
) error {
31
31
-
fs := flag.NewFlagSet("semver", flag.ExitOnError)
38
38
+
exec := args[0]
39
39
+
40
40
+
fs := flag.NewFlagSet(exec, flag.ExitOnError)
32
41
flagCompact := fs.Bool("c", false, "compact output")
33
42
34
34
-
if err := fs.Parse(os.Args[1:]); err != nil {
35
35
-
return err
43
43
+
bumpCmd := &ffcli.Command{
44
44
+
Name: "bump",
45
45
+
ShortUsage: fmt.Sprintf("%v bump <patch|minor|major|prerelease>", exec),
46
46
+
ShortHelp: "Bump version by incrementing patch, minor, major, or prerelease",
47
47
+
Exec: func(ctx context.Context, args []string) error {
48
48
+
if len(args) != 1 {
49
49
+
return fmt.Errorf("bump requires exactly one argument: patch, minor, major, or prerelease")
50
50
+
}
51
51
+
52
52
+
bumpType := args[0]
53
53
+
if bumpType != "patch" && bumpType != "minor" && bumpType != "major" && bumpType != "prerelease" {
54
54
+
return fmt.Errorf("invalid bump type: %s (must be patch, minor, major, or prerelease)", bumpType)
55
55
+
}
56
56
+
57
57
+
scanner := bufio.NewScanner(stdin)
58
58
+
for scanner.Scan() {
59
59
+
line := scanner.Text()
60
60
+
version, err := semver.NewVersion(line)
61
61
+
if err != nil {
62
62
+
fmt.Fprintf(stderr, "%v: %v\n", err, line)
63
63
+
continue
64
64
+
}
65
65
+
66
66
+
var newVersion semver.Version
67
67
+
switch bumpType {
68
68
+
case "patch":
69
69
+
newVersion = version.IncPatch()
70
70
+
case "minor":
71
71
+
newVersion = version.IncMinor()
72
72
+
case "major":
73
73
+
newVersion = version.IncMajor()
74
74
+
case "prerelease":
75
75
+
bumped, err := bumpPrerelease(version)
76
76
+
if err != nil {
77
77
+
fmt.Fprintf(stderr, "%v: %v\n", err, line)
78
78
+
continue
79
79
+
}
80
80
+
newVersion = *bumped
81
81
+
}
82
82
+
83
83
+
fmt.Fprintln(stdout, newVersion.String())
84
84
+
}
85
85
+
86
86
+
return scanner.Err()
87
87
+
},
36
88
}
37
89
38
38
-
scanner := bufio.NewScanner(stdin)
39
39
-
for scanner.Scan() {
40
40
-
line := scanner.Text()
41
41
-
version, err := semver.NewVersion(line)
42
42
-
if err != nil {
43
43
-
fmt.Fprintf(stderr, "%v: %v\n", err, line)
44
44
-
continue
45
45
-
}
90
90
+
rootCmd := &ffcli.Command{
91
91
+
Name: exec,
92
92
+
ShortUsage: fmt.Sprintf("%v [flags] [<subcommand>]", exec),
93
93
+
FlagSet: fs,
94
94
+
Subcommands: []*ffcli.Command{bumpCmd},
95
95
+
Exec: func(ctx context.Context, args []string) error {
96
96
+
scanner := bufio.NewScanner(stdin)
97
97
+
for scanner.Scan() {
98
98
+
line := scanner.Text()
99
99
+
version, err := semver.NewVersion(line)
100
100
+
if err != nil {
101
101
+
fmt.Fprintf(stderr, "%v: %v\n", err, line)
102
102
+
continue
103
103
+
}
104
104
+
105
105
+
dump(stdout, version, *flagCompact)
106
106
+
}
107
107
+
108
108
+
return scanner.Err()
109
109
+
},
110
110
+
}
111
111
+
112
112
+
return rootCmd.ParseAndRun(ctx, args[1:])
113
113
+
}
114
114
+
115
115
+
func bumpPrerelease(v *semver.Version) (*semver.Version, error) {
116
116
+
prerelease := v.Prerelease()
117
117
+
118
118
+
if prerelease == "" && v.Metadata() == "" {
119
119
+
// If no prerelease and no metadata, start a new prerelease cycle.
120
120
+
// Increment patch and add -alpha.0.
121
121
+
newVer := v.IncPatch()
122
122
+
withPre, _ := newVer.SetPrerelease("alpha.0")
123
123
+
return &withPre, nil
124
124
+
}
46
125
47
47
-
dump(stdout, version, *flagCompact)
126
126
+
if prerelease == "" {
127
127
+
return nil, fmt.Errorf("version has metadata but no prerelease to bump")
48
128
}
49
129
50
50
-
return scanner.Err()
130
130
+
re := regexp.MustCompile(`^(alpha|beta|rc)(\.)?(\d+)$`)
131
131
+
if matches := re.FindStringSubmatch(prerelease); matches != nil {
132
132
+
prefix := matches[1]
133
133
+
separator := matches[2] // Will be "." or empty
134
134
+
num, _ := strconv.Atoi(matches[3])
135
135
+
newPre := fmt.Sprintf("%s%s%d", prefix, separator, num+1)
136
136
+
newVer, _ := semver.NewVersion(fmt.Sprintf("%d.%d.%d-%s", v.Major(), v.Minor(), v.Patch(), newPre))
137
137
+
if v.Metadata() != "" {
138
138
+
withMeta, _ := newVer.SetMetadata(v.Metadata())
139
139
+
newVer = &withMeta
140
140
+
}
141
141
+
return newVer, nil
142
142
+
}
143
143
+
144
144
+
return nil, fmt.Errorf("prerelease '%s' does not match convention (alpha.N, beta.N, rc.N, alphaN, betaN, or rcN)", prerelease)
51
145
}
52
146
53
147
func dump(w io.Writer, v *semver.Version, compact bool) {