Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added -bump-to-next flag to control which component to bump if pre-release #36

Merged
merged 4 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Added
## [6.9.0] - tbd
### Added
* New flag `-bump-to-next` that can be used to select which version component will be bumped
if version contains pre-release. Possible values are `patch` (default), `minor` and `major`.
* Added [devbox](https://www.jetpack.io/devbox/) configuration

### Changed
Expand Down
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,17 @@ plus character. A valid format string is e.g.: `x.y+m`

The output and parsing of `git-semver` can be controlled with the following options.

| Name | Description |
| --- | --- |
| `-format` | Format string as described [here](#formatting) |
| `-no-minor` | Exclude minor version and all following components |
| `-no-patch` | Exclude patch version and all following components |
| `-no-pre` | Exclude pre-release version and all following components |
| `-no-meta`/`-no-hash` | Exclude build metadata |
| `-prefix` | Prefix string for version e.g.: v |
| `-set-meta` | Set buildmeta to this value |
| `-guard` | Ignore shorthand formats for pre-release versions |
| Name | Description |
| --- | --- |
| `-format` | Format string as described [here](#formatting) |
| `-no-minor` | Exclude minor version and all following components |
| `-no-patch` | Exclude patch version and all following components |
| `-no-pre` | Exclude pre-release version and all following components |
| `-no-meta`/`-no-hash` | Exclude build metadata |
| `-prefix` | Prefix string for version e.g.: v |
| `-set-meta` | Set buildmeta to this value |
| `-guard` | Ignore shorthand formats for pre-release versions |
| `-bump-to-next` | Bump `patch` (default), `minor` or `major` of pre-release versions |


#### Examples
Expand All @@ -132,6 +133,12 @@ v3.5.2

$ git-semver -set-meta custom
3.5.2+custom

$ git-semver -bump-to-next minor
3.6.0-dev.22+8eaec5d3

$ git-semver -bump-to-next major
4.0.0-dev.22+8eaec5d3
```

### Release safeguard
Expand Down
7 changes: 6 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Config struct {
excludeMinor bool
guardRelease bool
matchPattern string
versionComp version.VersionComp
args []string
stderr io.Writer
stdout io.Writer
Expand All @@ -34,6 +35,8 @@ func parseFlags(progname string, args []string) (*Config, string, error) {
cfg Config
)

cfg.versionComp = version.DefaultVersionComp

flags := flag.NewFlagSet(progname, flag.ContinueOnError)
flags.SetOutput(&buf)
flags.StringVar(&cfg.prefix, "prefix", "", "prefix of version string e.g. v (default: none)")
Expand All @@ -47,6 +50,7 @@ func parseFlags(progname string, args []string) (*Config, string, error) {
flags.BoolVar(&cfg.excludeMinor, "no-minor", false, "exclude pre-release version (default: false)")
flags.BoolVar(&cfg.excludePrefix, "no-prefix", false, "exclude version prefix (default: false)")
flags.BoolVar(&cfg.guardRelease, "guard", false, "ignore shorthand options if version contains pre-release (default: false)")
flags.Var(&cfg.versionComp, "bump-to-next", "set which version component (major, minor or patch) will be bumped if version contains pre-release (default: patch)")
flags.Usage = func() {
fmt.Fprintf(flags.Output(), "Usage: %s [opts] [<repo>]\n\nOptions:\n", progname)
flags.PrintDefaults()
Expand All @@ -56,6 +60,7 @@ func parseFlags(progname string, args []string) (*Config, string, error) {
if err != nil {
return nil, buf.String(), err
}

cfg.args = flags.Args()
cfg.stderr = os.Stderr
cfg.stdout = os.Stdout
Expand Down Expand Up @@ -112,7 +117,7 @@ func handle(cfg *Config, repoPath string) int {
if cfg.excludePrefix {
v.Prefix = ""
}
s, err := v.Format(selectFormat(cfg, v))
s, err := v.Format(selectFormat(cfg, v), cfg.versionComp)
if err != nil {
fmt.Fprintln(cfg.stderr, err)
return 1
Expand Down
73 changes: 64 additions & 9 deletions version/version.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package version

import (
"errors"
"fmt"
"regexp"
"strconv"
Expand All @@ -19,6 +20,44 @@ const (
NoMinorFormat = "x"
)

// Enum that specifies which version component should be bumped
type VersionComp int

const (
Patch VersionComp = iota
Minor
Major
)

func (t *VersionComp) String() string {
switch *t {
case Patch:
return "patch"
case Minor:
return "minor"
case Major:
return "major"
default:
panic(fmt.Errorf("unexpected TargetRevision value %v", *t))
}
}

func (t *VersionComp) Set(value string) error {
switch value {
case "patch":
*t = Patch
case "minor":
*t = Minor
case "major":
*t = Major
default:
return errors.New(`parse error`)
}
return nil
}

const DefaultVersionComp = Patch

type buffer []byte

func (b *buffer) AppendInt(i int, sep byte) {
Expand Down Expand Up @@ -52,7 +91,7 @@ type Version struct {
// * m -> metadata
// x, y and z are separated by a dot. p is seprated by a hyphen and m by a plus sing.
// E.g.: x.y.z-p+m or x.y
func (v Version) Format(format string) (string, error) {
func (v Version) Format(format string, target VersionComp) (string, error) {
re := regexp.MustCompile(
`(?P<major>x)(?P<minor>\.y)?(?P<patch>\.z)?(?P<pre>-p)?(?P<meta>\+m)?`)

Expand All @@ -61,7 +100,26 @@ func (v Version) Format(format string) (string, error) {
return "", fmt.Errorf("invalid format: %s", format)
}

var buf buffer
var (
buf buffer
major = v.Major
minor = v.Minor
patch = v.Patch
)

if v.Commits > 0 && v.preRelease == "" {
switch target {
case Major:
major++
minor = 0
patch = 0
case Minor:
minor++
patch = 0
case Patch:
patch++
}
}

names := re.SubexpNames()
for i := 0; i < len(matches); i++ {
Expand All @@ -70,26 +128,23 @@ func (v Version) Format(format string) (string, error) {
}
switch names[i] {
case "major":
buf.AppendInt(v.Major, '.')
buf.AppendInt(major, '.')
case "minor":
buf.AppendInt(v.Minor, '.')
buf.AppendInt(minor, '.')
case "patch":
patch := v.Patch
if v.Commits > 0 && v.preRelease == "" {
patch++
}
buf.AppendInt(patch, '.')
case "pre":
buf.AppendString(v.PreRelease(), '-')
case "meta":
buf.AppendString(v.Meta, '+')
}
}

return v.Prefix + string(buf), nil
}

func (v Version) String() string {
result, err := v.Format(FullFormat)
result, err := v.Format(FullFormat, DefaultVersionComp)
if err != nil {
return ""
}
Expand Down
76 changes: 74 additions & 2 deletions version/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,48 +129,120 @@ func TestFormat(t *testing.T) {
f string
p string
s string
t VersionComp
}{
{
FullFormat,
"",
"1.2.4-dev.10+fcf2c8f",
DefaultVersionComp,
},
{
NoMetaFormat,
"",
"1.2.4-dev.10",
DefaultVersionComp,
},
{
NoPreFormat,
"",
"1.2.4",
DefaultVersionComp,
},
{
NoPatchFormat,
"",
"1.2",
DefaultVersionComp,
},
{
NoMinorFormat,
"v",
"v1",
DefaultVersionComp,
},
{
"x.y-p",
"v",
"v1.2-dev.10",
DefaultVersionComp,
},
{
FullFormat,
"",
"1.2.4-dev.10+fcf2c8f",
Patch,
},
{
FullFormat,
"",
"1.3.0-dev.10+fcf2c8f",
Minor,
},
{
FullFormat,
"",
"2.0.0-dev.10+fcf2c8f",
Major,
},
} {
v.Prefix = test.p
s, err := v.Format(test.f)
s, err := v.Format(test.f, test.t)
assert.NoError(err)
assert.Equal(test.s, s)
}
}

func TestInvalidFormat(t *testing.T) {
v := Version{Major: 1, Minor: 2, Patch: 3}
s, err := v.Format("q")
s, err := v.Format("q", DefaultVersionComp)
assert.EqualError(t, err, "invalid format: q")
assert.Equal(t, "", s)
}

func TestVersionCompToString(t *testing.T) {
assert.PanicsWithError(t, "unexpected TargetRevision value 8", func() {
v := VersionComp(8)
_ = v.String()
})

{
v := Patch
assert.Equal(t, "patch", v.String())
}

{
v := Minor
assert.Equal(t, "minor", v.String())
}

{
v := Major
assert.Equal(t, "major", v.String())
}
}

func TestVersionCompFromString(t *testing.T) {
{
var v VersionComp
assert.EqualError(t, v.Set("foo"), "parse error")
}

{
var v VersionComp
assert.NoError(t, v.Set("patch"))
assert.Equal(t, Patch, v)
}

{
var v VersionComp
assert.NoError(t, v.Set("minor"))
assert.Equal(t, Minor, v)
}

{
var v VersionComp
assert.NoError(t, v.Set("major"))
assert.Equal(t, Major, v)
}
}
Loading