release-tag 0.2.5__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- completion/completion.go +75 -0
- git/git.go +123 -0
- go.mod +16 -0
- go.sum +18 -0
- main.go +127 -0
- release_tag-0.2.5.data/scripts/tag +0 -0
- release_tag-0.2.5.dist-info/METADATA +108 -0
- release_tag-0.2.5.dist-info/RECORD +12 -0
- release_tag-0.2.5.dist-info/WHEEL +4 -0
- release_tag-0.2.5.dist-info/licenses/LICENSE +21 -0
- semver/semver.go +127 -0
- semver/semver_test.go +261 -0
completion/completion.go
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
package completion
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
|
|
7
|
+
"github.com/spf13/cobra"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
func AddCompletionCmd(cmd *cobra.Command) *cobra.Command {
|
|
11
|
+
var completionCmd = &cobra.Command{
|
|
12
|
+
Use: "completion [bash|zsh|fish|powershell]",
|
|
13
|
+
Short: "Generate completion script",
|
|
14
|
+
Long: fmt.Sprintf(`To load completions:
|
|
15
|
+
|
|
16
|
+
Bash:
|
|
17
|
+
|
|
18
|
+
$ source <(%[1]s completion bash)
|
|
19
|
+
|
|
20
|
+
# To load completions for each session, execute once:
|
|
21
|
+
# Linux:
|
|
22
|
+
$ %[1]s completion bash > /etc/bash_completion.d/%[1]s
|
|
23
|
+
# macOS:
|
|
24
|
+
$ %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s
|
|
25
|
+
|
|
26
|
+
Zsh:
|
|
27
|
+
|
|
28
|
+
# If shell completion is not already enabled in your environment,
|
|
29
|
+
# you will need to enable it. You can execute the following once:
|
|
30
|
+
|
|
31
|
+
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
|
|
32
|
+
|
|
33
|
+
# To load completions for each session, execute once:
|
|
34
|
+
$ %[1]s completion zsh > "${fpath[1]}/_%[1]s"
|
|
35
|
+
|
|
36
|
+
# You will need to start a new shell for this setup to take effect.
|
|
37
|
+
|
|
38
|
+
fish:
|
|
39
|
+
|
|
40
|
+
$ %[1]s completion fish | source
|
|
41
|
+
|
|
42
|
+
# To load completions for each session, execute once:
|
|
43
|
+
$ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
|
|
44
|
+
|
|
45
|
+
PowerShell:
|
|
46
|
+
|
|
47
|
+
PS> %[1]s completion powershell | Out-String | Invoke-Expression
|
|
48
|
+
|
|
49
|
+
# To load completions for every new session, run:
|
|
50
|
+
PS> %[1]s completion powershell > %[1]s.ps1
|
|
51
|
+
# and source this file from your PowerShell profile.
|
|
52
|
+
`, cmd.Root().Name()),
|
|
53
|
+
DisableFlagsInUseLine: true,
|
|
54
|
+
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
|
55
|
+
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
|
56
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
57
|
+
var err error
|
|
58
|
+
switch args[0] {
|
|
59
|
+
case "bash":
|
|
60
|
+
err = cmd.Root().GenBashCompletion(os.Stdout)
|
|
61
|
+
case "zsh":
|
|
62
|
+
err = cmd.Root().GenZshCompletion(os.Stdout)
|
|
63
|
+
case "fish":
|
|
64
|
+
err = cmd.Root().GenFishCompletion(os.Stdout, true)
|
|
65
|
+
case "powershell":
|
|
66
|
+
err = cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
|
|
67
|
+
}
|
|
68
|
+
if err != nil {
|
|
69
|
+
fmt.Fprintf(os.Stderr, "Error generating completion: %v\n", err)
|
|
70
|
+
os.Exit(1)
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
return completionCmd
|
|
75
|
+
}
|
git/git.go
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
package git
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
"os/exec"
|
|
7
|
+
"strings"
|
|
8
|
+
|
|
9
|
+
"github.com/jmelahman/tag/semver"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func GetLatestSemverTag() (string, error) {
|
|
13
|
+
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
|
|
14
|
+
output, err := cmd.Output()
|
|
15
|
+
if err != nil {
|
|
16
|
+
return "v0.0.0", nil
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
tagsAt, err := ListTagsAt(strings.TrimSpace(string(output)))
|
|
20
|
+
if err != nil {
|
|
21
|
+
return "v0.0.0", nil
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var largestTag string
|
|
25
|
+
var largestVersion *semver.Version
|
|
26
|
+
|
|
27
|
+
for _, tag := range tagsAt {
|
|
28
|
+
if largestTag == "" {
|
|
29
|
+
largestTag = tag
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
version, err := semver.ParseSemver(tag)
|
|
33
|
+
if err != nil {
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if largestVersion == nil {
|
|
38
|
+
largestTag = tag
|
|
39
|
+
largestVersion = version
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if semver.CompareSemver(version, largestVersion) {
|
|
44
|
+
largestTag = tag
|
|
45
|
+
largestVersion = version
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return largestTag, nil
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// List all git tags
|
|
53
|
+
func ListTags() ([]string, error) {
|
|
54
|
+
cmd := exec.Command("git", "tag", "-l", "v[0-9]*.[0-9]*.[0-9]*")
|
|
55
|
+
cmd.Stderr = os.Stderr
|
|
56
|
+
tagsOutput, err := cmd.Output()
|
|
57
|
+
if err != nil {
|
|
58
|
+
return nil, err
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
tagList := strings.Split(strings.TrimSpace(string(tagsOutput)), "\n")
|
|
62
|
+
|
|
63
|
+
return tagList, nil
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func ListTagsAt(ref string) ([]string, error) {
|
|
67
|
+
cmd := exec.Command("git", "tag", "--points-at", ref)
|
|
68
|
+
cmd.Stderr = os.Stderr
|
|
69
|
+
tagsOutput, err := cmd.Output()
|
|
70
|
+
if err != nil {
|
|
71
|
+
return nil, err
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
tagList := strings.Split(strings.TrimSpace(string(tagsOutput)), "\n")
|
|
75
|
+
|
|
76
|
+
return tagList, nil
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
func TagExists(tag string) (bool, error) {
|
|
80
|
+
cmd := exec.Command("git", "show-ref", "--tags", "--quiet", tag)
|
|
81
|
+
if err := cmd.Run(); err != nil {
|
|
82
|
+
if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 1 {
|
|
83
|
+
return false, nil
|
|
84
|
+
}
|
|
85
|
+
return false, err
|
|
86
|
+
}
|
|
87
|
+
return true, nil
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func CreateAndPushTag(tag string, remote string) error {
|
|
91
|
+
cmd := exec.Command("git", "tag", tag)
|
|
92
|
+
cmd.Stderr = os.Stderr
|
|
93
|
+
if err := cmd.Run(); err != nil {
|
|
94
|
+
return fmt.Errorf("failed to create tag: %w", err)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
cmd = exec.Command("git", "push", "--quiet", remote, tag)
|
|
98
|
+
cmd.Stderr = os.Stderr
|
|
99
|
+
if err := cmd.Run(); err != nil {
|
|
100
|
+
return fmt.Errorf("failed to push tag to %s: %w", remote, err)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return nil
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func FetchSemverTags(remote string) error {
|
|
107
|
+
cmd := exec.Command("git", "fetch", "--quiet", "--prune", remote, "refs/tags/v*:refs/tags/v*")
|
|
108
|
+
cmd.Stderr = os.Stderr
|
|
109
|
+
if err := cmd.Run(); err != nil {
|
|
110
|
+
return fmt.Errorf("failed to fetch tags from %s: %w", remote, err)
|
|
111
|
+
}
|
|
112
|
+
return nil
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func IsHEADAlreadyTagged() (bool, error) {
|
|
116
|
+
cmd := exec.Command("git", "tag", "--points-at", "HEAD")
|
|
117
|
+
cmd.Stderr = os.Stderr
|
|
118
|
+
output, err := cmd.Output()
|
|
119
|
+
if err != nil {
|
|
120
|
+
return false, fmt.Errorf("failed to check tags for HEAD: %w", err)
|
|
121
|
+
}
|
|
122
|
+
return len(strings.TrimSpace(string(output))) > 0, nil
|
|
123
|
+
}
|
go.mod
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module github.com/jmelahman/tag
|
|
2
|
+
|
|
3
|
+
go 1.23.4
|
|
4
|
+
|
|
5
|
+
require (
|
|
6
|
+
github.com/spf13/cobra v1.8.1
|
|
7
|
+
github.com/stretchr/testify v1.10.0
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
require (
|
|
11
|
+
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
12
|
+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
13
|
+
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
14
|
+
github.com/spf13/pflag v1.0.5 // indirect
|
|
15
|
+
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
16
|
+
)
|
go.sum
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
2
|
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
3
|
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
4
|
+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
|
5
|
+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
6
|
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
7
|
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
8
|
+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
9
|
+
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
|
10
|
+
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
|
11
|
+
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
12
|
+
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
13
|
+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
|
14
|
+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
15
|
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
16
|
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
17
|
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
18
|
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
main.go
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bufio"
|
|
5
|
+
"fmt"
|
|
6
|
+
"os"
|
|
7
|
+
"strings"
|
|
8
|
+
|
|
9
|
+
"github.com/jmelahman/tag/completion"
|
|
10
|
+
"github.com/jmelahman/tag/git"
|
|
11
|
+
"github.com/jmelahman/tag/semver"
|
|
12
|
+
"github.com/spf13/cobra"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
var (
|
|
16
|
+
version = "dev"
|
|
17
|
+
commit = "none"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
func main() {
|
|
21
|
+
var major, minor, patch, push bool
|
|
22
|
+
var metadata, suffix, remote string
|
|
23
|
+
|
|
24
|
+
rootCmd := &cobra.Command{
|
|
25
|
+
Use: "tag",
|
|
26
|
+
Short: "Calculate the next semantic version tag",
|
|
27
|
+
Version: fmt.Sprintf("%s\ncommit %s", version, commit),
|
|
28
|
+
PreRunE: func(cmd *cobra.Command, args []string) error {
|
|
29
|
+
// Validate that only one version increment flag is set
|
|
30
|
+
incrementFlags := []bool{major, minor, patch}
|
|
31
|
+
var setFlags int
|
|
32
|
+
for _, flag := range incrementFlags {
|
|
33
|
+
if flag {
|
|
34
|
+
setFlags++
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if setFlags > 1 {
|
|
38
|
+
return fmt.Errorf("only one version increment flag (--major, --minor, or --patch) can be used at a time")
|
|
39
|
+
}
|
|
40
|
+
return nil
|
|
41
|
+
},
|
|
42
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
43
|
+
if err := git.FetchSemverTags(remote); err != nil {
|
|
44
|
+
fmt.Printf("Error fetching tags: %v\n", err)
|
|
45
|
+
os.Exit(1)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check if HEAD is already tagged
|
|
49
|
+
alreadyTagged, err := git.IsHEADAlreadyTagged()
|
|
50
|
+
if err != nil {
|
|
51
|
+
fmt.Printf("Error checking tags: %v\n", err)
|
|
52
|
+
os.Exit(1)
|
|
53
|
+
}
|
|
54
|
+
if alreadyTagged {
|
|
55
|
+
fmt.Println("Error: Current HEAD is already tagged")
|
|
56
|
+
os.Exit(1)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
latestTag, err := git.GetLatestSemverTag()
|
|
60
|
+
if err != nil {
|
|
61
|
+
fmt.Printf("Error: %v\n", err)
|
|
62
|
+
os.Exit(1)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
allTags, err := git.ListTags()
|
|
66
|
+
if err != nil {
|
|
67
|
+
fmt.Printf("Error: %v\n", err)
|
|
68
|
+
os.Exit(1)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
nextVersion, err := semver.CalculateNextVersion(latestTag, allTags, major, minor, patch, suffix)
|
|
72
|
+
if err != nil {
|
|
73
|
+
fmt.Printf("Error: %v\n", err)
|
|
74
|
+
os.Exit(1)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if metadata != "" {
|
|
78
|
+
nextVersion = fmt.Sprint(nextVersion, "+", metadata)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
tagExists, err := git.TagExists(nextVersion)
|
|
82
|
+
if err != nil {
|
|
83
|
+
fmt.Printf("Error: %v\n", err)
|
|
84
|
+
os.Exit(1)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if tagExists {
|
|
88
|
+
fmt.Printf("Next tag '%s' already exists.\n", nextVersion)
|
|
89
|
+
os.Exit(1)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if !push {
|
|
93
|
+
reader := bufio.NewReader(os.Stdin)
|
|
94
|
+
fmt.Printf("Push tag '%s' to %s? (y/N): ", nextVersion, remote)
|
|
95
|
+
response, _ := reader.ReadString('\n')
|
|
96
|
+
response = strings.TrimSpace(strings.ToLower(response))
|
|
97
|
+
|
|
98
|
+
if response == "" || response == "y" || response == "yes" {
|
|
99
|
+
push = true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if push {
|
|
104
|
+
if err := git.CreateAndPushTag(nextVersion, remote); err != nil {
|
|
105
|
+
fmt.Printf("Error: %v\n", err)
|
|
106
|
+
os.Exit(1)
|
|
107
|
+
}
|
|
108
|
+
fmt.Printf("Tag '%s' created and pushed to %s.\n", nextVersion, remote)
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
rootCmd.Flags().BoolVar(&major, "major", false, "increment the major version")
|
|
114
|
+
rootCmd.Flags().BoolVar(&minor, "minor", false, "increment the minor version")
|
|
115
|
+
rootCmd.Flags().BoolVar(&patch, "patch", false, "increment the patch version")
|
|
116
|
+
rootCmd.Flags().BoolVar(&push, "push", false, "create and push the tag to remote")
|
|
117
|
+
rootCmd.Flags().StringVar(&suffix, "suffix", "", "set the pre-release suffix (e.g., rc, alpha, beta)")
|
|
118
|
+
rootCmd.Flags().StringVar(&metadata, "metadata", "", "set the build metadata")
|
|
119
|
+
rootCmd.Flags().StringVar(&remote, "remote", "origin", "remote repository to push tag to")
|
|
120
|
+
|
|
121
|
+
rootCmd.AddCommand(completion.AddCompletionCmd(rootCmd))
|
|
122
|
+
|
|
123
|
+
if err := rootCmd.Execute(); err != nil {
|
|
124
|
+
fmt.Printf("Error: %v\n", err)
|
|
125
|
+
os.Exit(1)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: release-tag
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: Automatically create semantic version git tags
|
|
5
|
+
Project-URL: Repository, https://github.com/jmelahman/tag
|
|
6
|
+
Author-email: Jamison Lahman <jamison@lahman.dev>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2024 Jamison Lahman
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Keywords: git,git tags,release,releasing,semver,tagging
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: OS Independent
|
|
32
|
+
Classifier: Programming Language :: Go
|
|
33
|
+
Requires-Python: >=3.6
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# Tag Release
|
|
37
|
+
|
|
38
|
+
[](https://github.com/jmelahman/tag/actions)
|
|
39
|
+
[](https://github.com/jmelahman/tag/actions)
|
|
40
|
+
[](https://pkg.go.dev/github.com/jmelahman/tag)
|
|
41
|
+
[](https://aur.archlinux.org/packages/release-tag)
|
|
42
|
+
[]()
|
|
43
|
+
[](https://goreportcard.com/report/github.com/jmelahman/tag)
|
|
44
|
+
|
|
45
|
+
Automatically create [semantic version](https://semver.org/) git tags.
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
$ tag
|
|
49
|
+
Push tag 'v1.0.1' to origin? (y/N): y
|
|
50
|
+
Tag 'v1.0.1' created and pushed to origin.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
By default, `tag` will increment the smallest digit following [SemVer precedence](https://semver.org/#semantic-versioning-specification-semver).
|
|
56
|
+
Incrementing a specific is achieved by passing the respective flag: `--major`, `--minor`, `--patch`.
|
|
57
|
+
|
|
58
|
+
Tags can be automatically pushed to a remote repository by passing `--push`.
|
|
59
|
+
|
|
60
|
+
`tag` supports [pre-release](https://semver.org/#spec-item-9) versions.
|
|
61
|
+
Creating a pre-release tag is achieved by the using the `--suffix` flag.
|
|
62
|
+
For example, `--suffix="alpha"` will create a tag like `v1.0.0-alpha`.
|
|
63
|
+
If the previous tag was for a pre-release, that suffix is preferred.
|
|
64
|
+
This behavior can be overridden by passing `--patch` or `--suffix=""`.
|
|
65
|
+
Only incrementing the trailing pre-release identifier is currently supported.
|
|
66
|
+
|
|
67
|
+
`tag` authoritatively discourages duplicate tags for a single commit.
|
|
68
|
+
|
|
69
|
+
### Autocomplete
|
|
70
|
+
|
|
71
|
+
`tag` provides autocomplete for `bash`, `fish`, `powershell` and `zsh` shells.
|
|
72
|
+
For example, to enable autocomplete for the `bash` shell,
|
|
73
|
+
|
|
74
|
+
```shell
|
|
75
|
+
tag completion bash | sudo tee /etc/bash_completion.d/tag > /dev/null
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
_Note: bash completion requires the [bash-completion](https://github.com/scop/bash-completion/) package be installed._
|
|
79
|
+
|
|
80
|
+
For more information, see `tag completion <shell> --help` for your respective `<shell>`.
|
|
81
|
+
|
|
82
|
+
## Install
|
|
83
|
+
|
|
84
|
+
**AUR:**
|
|
85
|
+
|
|
86
|
+
`tag` is available from the [Arch User Repository](https://aur.archlinux.org/packages/release-tag).
|
|
87
|
+
|
|
88
|
+
```shell
|
|
89
|
+
yay -S release-tag
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**pip:**
|
|
93
|
+
|
|
94
|
+
`tag` is available as a [pypi package](https://pypi.org/project/release-tag/).
|
|
95
|
+
|
|
96
|
+
```shell
|
|
97
|
+
pip install release-tag
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**go:**
|
|
101
|
+
|
|
102
|
+
```shell
|
|
103
|
+
go install github.com/jmelahman/tag@latest
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**github:**
|
|
107
|
+
|
|
108
|
+
Prebuilt packages are available from [Github Releases](https://github.com/jmelahman/tag/releases).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
go.mod,sha256=yqrrzLAqT2bxEcjq-XEp7hRrtrLVgyozVrUUSo-UE9Y,371
|
|
2
|
+
go.sum,sha256=NhTyKeYVMD29_XicuWpg9bSSxCALHSPAHkAtC8AZO9I,1592
|
|
3
|
+
main.go,sha256=wrz-whOruIWj-1lRCyDNqS2DNHRf8o99hsW1l62MHGs,3377
|
|
4
|
+
completion/completion.go,sha256=5d7jq9-sQATbG2jqEnhmLvoBBKpkP0HrCs2B9EeTwDM,2055
|
|
5
|
+
git/git.go,sha256=LKx7fmBwa2jgRkZJwUfDeelke0ryTwPeQ2isfCBuB0E,2733
|
|
6
|
+
semver/semver.go,sha256=LQgpwqqne2NV312U8_6AMQvS-VSh6TD2Dg6AtuDmYOA,3046
|
|
7
|
+
semver/semver_test.go,sha256=XosZIXKOZeK1x6r9W1GUBdghXXKYtFeXD1YjwlHq8kU,5793
|
|
8
|
+
release_tag-0.2.5.data/scripts/tag,sha256=-Oqz4POzs3jNRktwBILkiVrMpav5-zS-iMihgerH75Q,4009472
|
|
9
|
+
release_tag-0.2.5.dist-info/METADATA,sha256=NUM0ZWh-xy_aGV79N5X_8ZuKz7TL_T0eebJbiCj2DzU,4378
|
|
10
|
+
release_tag-0.2.5.dist-info/WHEEL,sha256=Vhj7RpPOKCXcsr4rUs7q4pISuo5nT6VsOuaQwH9GILY,94
|
|
11
|
+
release_tag-0.2.5.dist-info/licenses/LICENSE,sha256=P_te03YIieIzk4EnP3-blVrdJGbhityi_YcHwxaL7gk,1071
|
|
12
|
+
release_tag-0.2.5.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Jamison Lahman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
semver/semver.go
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
package semver
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"regexp"
|
|
6
|
+
"strconv"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
type Version struct {
|
|
10
|
+
Major int
|
|
11
|
+
Minor int
|
|
12
|
+
Patch int
|
|
13
|
+
PreRelease string
|
|
14
|
+
PreReleaseNum int
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func ParseSemver(tag string) (*Version, error) {
|
|
18
|
+
re := regexp.MustCompile(`v(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z]+)(?:\.(\d+))?)?`)
|
|
19
|
+
matches := re.FindStringSubmatch(tag)
|
|
20
|
+
if matches == nil {
|
|
21
|
+
return nil, fmt.Errorf("invalid semver tag: %s", tag)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
major, _ := strconv.Atoi(matches[1])
|
|
25
|
+
minor, _ := strconv.Atoi(matches[2])
|
|
26
|
+
patch, _ := strconv.Atoi(matches[3])
|
|
27
|
+
preRelease := matches[4]
|
|
28
|
+
preReleaseNum := 0
|
|
29
|
+
if len(matches) > 5 && matches[5] != "" {
|
|
30
|
+
preReleaseNum, _ = strconv.Atoi(matches[5])
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return &Version{
|
|
34
|
+
Major: major,
|
|
35
|
+
Minor: minor,
|
|
36
|
+
Patch: patch,
|
|
37
|
+
PreRelease: preRelease,
|
|
38
|
+
PreReleaseNum: preReleaseNum,
|
|
39
|
+
}, nil
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func CompareSemver(v1, v2 *Version) bool {
|
|
43
|
+
if v1.Major > v2.Major {
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
if v1.Major < v2.Major {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
if v1.Minor > v2.Minor {
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
if v1.Minor < v2.Minor {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
if v1.Patch > v2.Patch {
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
58
|
+
if v1.Patch < v2.Patch {
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle pre-release versions
|
|
63
|
+
if v1.PreRelease == "" && v2.PreRelease != "" {
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
if v1.PreRelease != "" && v2.PreRelease == "" {
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
if v1.PreRelease == v2.PreRelease {
|
|
70
|
+
return v1.PreReleaseNum > v2.PreReleaseNum
|
|
71
|
+
}
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func CalculateNextVersion(tag string, allTags []string, incMajor, incMinor, incPatch bool, suffix string) (string, error) {
|
|
76
|
+
// Parse the current version
|
|
77
|
+
version, err := ParseSemver(tag)
|
|
78
|
+
if err != nil {
|
|
79
|
+
return "", err
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Increment version based on flags
|
|
83
|
+
if incMajor {
|
|
84
|
+
version.Major++
|
|
85
|
+
version.Minor = 0
|
|
86
|
+
version.Patch = 0
|
|
87
|
+
version.PreReleaseNum = 0
|
|
88
|
+
} else if incMinor {
|
|
89
|
+
version.Minor++
|
|
90
|
+
version.Patch = 0
|
|
91
|
+
version.PreReleaseNum = 0
|
|
92
|
+
} else if incPatch {
|
|
93
|
+
version.Patch++
|
|
94
|
+
version.PreReleaseNum = 0
|
|
95
|
+
} else if suffix == "" && version.PreRelease == "" {
|
|
96
|
+
version.Patch++
|
|
97
|
+
} else if suffix != "" && version.PreRelease != suffix {
|
|
98
|
+
// Find the largest PreReleaseNum for the given suffix
|
|
99
|
+
largestPreReleaseNum := 0
|
|
100
|
+
for _, existingTag := range allTags {
|
|
101
|
+
existingVersion, err := ParseSemver(existingTag)
|
|
102
|
+
if err == nil && existingVersion.PreRelease == suffix {
|
|
103
|
+
if existingVersion.PreReleaseNum > largestPreReleaseNum {
|
|
104
|
+
largestPreReleaseNum = existingVersion.PreReleaseNum
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if largestPreReleaseNum > 0 {
|
|
109
|
+
version.PreReleaseNum = largestPreReleaseNum + 1
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
version.PreReleaseNum++
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if suffix != "" {
|
|
116
|
+
version.PreRelease = suffix
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Construct the version string
|
|
120
|
+
if version.PreRelease != "" {
|
|
121
|
+
if version.PreReleaseNum > 0 {
|
|
122
|
+
return fmt.Sprintf("v%d.%d.%d-%s.%d", version.Major, version.Minor, version.Patch, version.PreRelease, version.PreReleaseNum), nil
|
|
123
|
+
}
|
|
124
|
+
return fmt.Sprintf("v%d.%d.%d-%s", version.Major, version.Minor, version.Patch, version.PreRelease), nil
|
|
125
|
+
}
|
|
126
|
+
return fmt.Sprintf("v%d.%d.%d", version.Major, version.Minor, version.Patch), nil
|
|
127
|
+
}
|
semver/semver_test.go
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
package semver
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"testing"
|
|
5
|
+
|
|
6
|
+
"github.com/stretchr/testify/assert"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
func TestParseSemver(t *testing.T) {
|
|
10
|
+
testCases := []struct {
|
|
11
|
+
name string
|
|
12
|
+
tag string
|
|
13
|
+
expectedVer *Version
|
|
14
|
+
expectError bool
|
|
15
|
+
}{
|
|
16
|
+
{
|
|
17
|
+
name: "Standard version",
|
|
18
|
+
tag: "v1.2.3",
|
|
19
|
+
expectedVer: &Version{
|
|
20
|
+
Major: 1,
|
|
21
|
+
Minor: 2,
|
|
22
|
+
Patch: 3,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "Pre-release version",
|
|
27
|
+
tag: "v1.2.3-rc",
|
|
28
|
+
expectedVer: &Version{
|
|
29
|
+
Major: 1,
|
|
30
|
+
Minor: 2,
|
|
31
|
+
Patch: 3,
|
|
32
|
+
PreRelease: "rc",
|
|
33
|
+
PreReleaseNum: 0,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "Pre-release version",
|
|
38
|
+
tag: "v1.2.3-rc.1",
|
|
39
|
+
expectedVer: &Version{
|
|
40
|
+
Major: 1,
|
|
41
|
+
Minor: 2,
|
|
42
|
+
Patch: 3,
|
|
43
|
+
PreRelease: "rc",
|
|
44
|
+
PreReleaseNum: 1,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "Build metadata version",
|
|
49
|
+
tag: "v1.2.3+21AF26D3",
|
|
50
|
+
expectedVer: &Version{
|
|
51
|
+
Major: 1,
|
|
52
|
+
Minor: 2,
|
|
53
|
+
Patch: 3,
|
|
54
|
+
PreRelease: "",
|
|
55
|
+
PreReleaseNum: 0,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "Invalid version",
|
|
60
|
+
tag: "invalid-tag",
|
|
61
|
+
expectError: true,
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for _, tc := range testCases {
|
|
66
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
67
|
+
version, err := ParseSemver(tc.tag)
|
|
68
|
+
|
|
69
|
+
if tc.expectError {
|
|
70
|
+
assert.Error(t, err)
|
|
71
|
+
} else {
|
|
72
|
+
assert.NoError(t, err)
|
|
73
|
+
assert.Equal(t, tc.expectedVer, version)
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
func TestCompareSemver(t *testing.T) {
|
|
80
|
+
testCases := []struct {
|
|
81
|
+
name string
|
|
82
|
+
v1 *Version
|
|
83
|
+
v2 *Version
|
|
84
|
+
expected bool
|
|
85
|
+
}{
|
|
86
|
+
{
|
|
87
|
+
name: "v1 major version higher",
|
|
88
|
+
v1: &Version{Major: 2, Minor: 0, Patch: 0},
|
|
89
|
+
v2: &Version{Major: 1, Minor: 9, Patch: 9},
|
|
90
|
+
expected: true,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "v1 minor version higher",
|
|
94
|
+
v1: &Version{Major: 1, Minor: 2, Patch: 0},
|
|
95
|
+
v2: &Version{Major: 1, Minor: 1, Patch: 9},
|
|
96
|
+
expected: true,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "v1 patch version higher",
|
|
100
|
+
v1: &Version{Major: 1, Minor: 1, Patch: 2},
|
|
101
|
+
v2: &Version{Major: 1, Minor: 1, Patch: 1},
|
|
102
|
+
expected: true,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "v1 pre-release version higher",
|
|
106
|
+
v1: &Version{Major: 1, Minor: 1, Patch: 1, PreRelease: "rc", PreReleaseNum: 2},
|
|
107
|
+
v2: &Version{Major: 1, Minor: 1, Patch: 1, PreRelease: "rc", PreReleaseNum: 1},
|
|
108
|
+
expected: true,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "Identical versions",
|
|
112
|
+
v1: &Version{Major: 1, Minor: 1, Patch: 1},
|
|
113
|
+
v2: &Version{Major: 1, Minor: 1, Patch: 1},
|
|
114
|
+
expected: false,
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for _, tc := range testCases {
|
|
119
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
120
|
+
result := CompareSemver(tc.v1, tc.v2)
|
|
121
|
+
assert.Equal(t, tc.expected, result)
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
func TestCalculateNextVersion(t *testing.T) {
|
|
127
|
+
testCases := []struct {
|
|
128
|
+
name string
|
|
129
|
+
currentTag string
|
|
130
|
+
incMajor bool
|
|
131
|
+
incMinor bool
|
|
132
|
+
incPatch bool
|
|
133
|
+
suffix string
|
|
134
|
+
allTags []string
|
|
135
|
+
expectedTag string
|
|
136
|
+
expectError bool
|
|
137
|
+
}{
|
|
138
|
+
{
|
|
139
|
+
name: "Invalid tag format",
|
|
140
|
+
currentTag: "invalid-tag",
|
|
141
|
+
expectError: true,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "Next tag already exists error",
|
|
145
|
+
currentTag: "v1.2.3",
|
|
146
|
+
allTags: []string{"v1.2.3", "v1.3.0"},
|
|
147
|
+
expectedTag: "v1.2.4",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "Patch increment",
|
|
151
|
+
currentTag: "v1.2.3",
|
|
152
|
+
expectedTag: "v1.2.4",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "Patch increment with incPatch",
|
|
156
|
+
currentTag: "v1.2.3",
|
|
157
|
+
incPatch: true,
|
|
158
|
+
expectedTag: "v1.2.4",
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: "Patch increment with suffix",
|
|
162
|
+
currentTag: "v1.2.3",
|
|
163
|
+
incPatch: true,
|
|
164
|
+
suffix: "alpha",
|
|
165
|
+
expectedTag: "v1.2.4-alpha",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "Minor increment",
|
|
169
|
+
currentTag: "v1.2.3",
|
|
170
|
+
incMinor: true,
|
|
171
|
+
expectedTag: "v1.3.0",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "Minor increment with suffix",
|
|
175
|
+
currentTag: "v1.2.3",
|
|
176
|
+
incMinor: true,
|
|
177
|
+
suffix: "alpha",
|
|
178
|
+
expectedTag: "v1.3.0-alpha",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "Major increment",
|
|
182
|
+
currentTag: "v1.2.3",
|
|
183
|
+
incMajor: true,
|
|
184
|
+
expectedTag: "v2.0.0",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "Major increment with suffix",
|
|
188
|
+
currentTag: "v1.2.3",
|
|
189
|
+
incMajor: true,
|
|
190
|
+
suffix: "alpha",
|
|
191
|
+
expectedTag: "v2.0.0-alpha",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: "Pre-release increment with pre-release version",
|
|
195
|
+
currentTag: "v1.2.3-rc.1",
|
|
196
|
+
suffix: "rc",
|
|
197
|
+
expectedTag: "v1.2.3-rc.2",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: "Pre-release increment with patch version",
|
|
201
|
+
currentTag: "v1.2.3-rc.1",
|
|
202
|
+
incPatch: true,
|
|
203
|
+
expectedTag: "v1.2.4-rc",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: "Pre-release increment with minor version",
|
|
207
|
+
currentTag: "v1.2.3-rc.1",
|
|
208
|
+
incMinor: true,
|
|
209
|
+
expectedTag: "v1.3.0-rc",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: "Pre-release increment with major version",
|
|
213
|
+
currentTag: "v1.2.3-rc.1",
|
|
214
|
+
incMajor: true,
|
|
215
|
+
expectedTag: "v2.0.0-rc",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "Pre-release without number",
|
|
219
|
+
currentTag: "v1.2.3-rc",
|
|
220
|
+
expectedTag: "v1.2.3-rc.1",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "Add suffix to version",
|
|
224
|
+
currentTag: "v1.2.3",
|
|
225
|
+
suffix: "beta",
|
|
226
|
+
expectedTag: "v1.2.3-beta",
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: "Add suffix to pre-release version",
|
|
230
|
+
currentTag: "v1.1.1-beta",
|
|
231
|
+
suffix: "beta",
|
|
232
|
+
expectedTag: "v1.1.1-beta.1",
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "Override existing pre-release with suffix",
|
|
236
|
+
currentTag: "v1.2.3-rc.1",
|
|
237
|
+
suffix: "beta",
|
|
238
|
+
expectedTag: "v1.2.3-beta.1",
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: "Override existing pre-release with suffix",
|
|
242
|
+
currentTag: "v1.2.3-rc.1",
|
|
243
|
+
suffix: "beta",
|
|
244
|
+
allTags: []string{"v1.2.3-rc.1", "v1.2.3-beta.2"},
|
|
245
|
+
expectedTag: "v1.2.3-beta.3",
|
|
246
|
+
},
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for _, tc := range testCases {
|
|
250
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
251
|
+
nextVersion, err := CalculateNextVersion(tc.currentTag, tc.allTags, tc.incMajor, tc.incMinor, tc.incPatch, tc.suffix)
|
|
252
|
+
|
|
253
|
+
if tc.expectError {
|
|
254
|
+
assert.Error(t, err)
|
|
255
|
+
} else {
|
|
256
|
+
assert.NoError(t, err)
|
|
257
|
+
assert.Equal(t, tc.expectedTag, nextVersion)
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
}
|