-
-
Notifications
You must be signed in to change notification settings - Fork 20
/
tag.go
150 lines (137 loc) · 3.06 KB
/
tag.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package kimono
import (
"fmt"
"path/filepath"
"slices"
"strconv"
"strings"
"golang.org/x/mod/semver"
"github.com/rwxrob/bonzai/fn/each"
"github.com/rwxrob/bonzai/futil"
"github.com/rwxrob/bonzai/run"
)
type VerPart int
const (
Major VerPart = 0
Minor VerPart = 1
Patch VerPart = 2
)
func TagBump(part VerPart, mustPush bool) error {
latest := latestTag()
prefix := modulePrefix()
newVer, err := versionBump(latest, part)
if err != nil {
return fmt.Errorf(`failed to bump version: %w`, err)
}
newVerStr := fmt.Sprintf(`%s%s`, prefix, newVer)
if err := run.Exec(`git`, `tag`, newVerStr); err != nil {
return err
}
fmt.Println(newVerStr)
if mustPush {
if err := run.Exec(`git`, `push`, `origin`, newVerStr); err != nil {
return err
}
}
return nil
}
// TagList returns the list of tags for the current module.
func TagList(shorten bool) []string {
prefix := modulePrefix()
tags := run.Out(`git`, `tag`, `-l`, `--no-column`)
out := make([]string, 0)
each.Do(strings.Split(tags, "\n"), func(tag string) {
if isValidTag(tag, prefix) {
if shorten {
tag = strings.TrimPrefix(tag, prefix)
}
out = append(out, tag)
}
})
semver.Sort(out)
return out
}
// TagDelete deletes the given tag from local git repository.
func TagDelete(tag string, remote bool) error {
tags := TagList(false)
if !slices.Contains(tags, tag) {
return fmt.Errorf("tag '%s' not found", tag)
}
if err := run.Exec(`git`, `tag`, `-d`, tag); err != nil {
return fmt.Errorf(
"failed to delete local tag '%s': %w",
tag,
err,
)
}
return nil
}
func latestTag() string {
versions := TagList(true)
latest := ``
if len(versions) == 0 {
latest = `v0.0.0`
} else {
latest = versions[len(versions)-1]
}
return latest
}
// versionBump increases the given part of the version.
func versionBump(version string, part VerPart) (string, error) {
leading := version[:1]
versionN := version[1:]
if leading != `v` {
leading = `v`
versionN = version
}
versionParts := strings.Split(versionN, `.`)
switch part {
case Major:
major, err := strconv.Atoi(versionParts[0])
if err != nil {
return ``, err
}
versionParts[0] = strconv.Itoa(major + 1)
versionParts[1] = `0`
versionParts[2] = `0`
case Minor:
minor, err := strconv.Atoi(versionParts[1])
if err != nil {
return ``, err
}
versionParts[1] = strconv.Itoa(minor + 1)
versionParts[2] = `0`
case Patch:
patch, err := strconv.Atoi(versionParts[2])
if err != nil {
return ``, err
}
versionParts[2] = strconv.Itoa(patch + 1)
}
return fmt.Sprint(leading, strings.Join(versionParts, `.`)), nil
}
func isValidTag(tag, prefix string) bool {
return (len(prefix) > 0 && strings.HasPrefix(tag, prefix)) ||
(len(prefix) == 0 && semver.IsValid(tag))
}
func modulePrefix() string {
root, err := futil.HereOrAbove(`.git`)
if err != nil {
return ``
}
module, err := futil.HereOrAbove(`go.mod`)
if err != nil {
return ``
}
outprefix, err := filepath.Rel(
filepath.Dir(root),
filepath.Dir(module),
)
if err != nil {
return ``
}
if outprefix == `.` {
return ``
}
return outprefix + `/`
}