Skip to content

Commit

Permalink
Added -target-release flag to control which component to bump if pre-…
Browse files Browse the repository at this point in the history
…release
  • Loading branch information
yobeonline committed Feb 12, 2024
1 parent c36a67b commit 4cfda54
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 22 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +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).

## [6.8.1] - ?
## [6.9.0] - ?
### Added
* New flag `-target-revision` that can be used to select which version component will be bumped
if version sontains pre-release. Possible values are `patch` (default), `minor` and `major`.

### Fixed
* An error was issued when invoked from subfolder of the repository whereas `git-describe` ususally succeeds in such cases.

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 |
| `-target-release` | 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 -target-release minor
3.6.0-dev.22+8eaec5d3

$ git-semver -target-release 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
targetRelease version.TargetRelease
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.targetRelease = version.DefaultTargetRelease

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.targetRelease, "target-release", "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.targetRelease)
if err != nil {
fmt.Fprintln(cfg.stderr, err)
return 1
Expand Down
70 changes: 62 additions & 8 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 TargetRelease int

const (
TargetPatch TargetRelease = iota
TargetMinor
TargetMajor
)

func (t *TargetRelease) String() string {
switch *t {
case TargetPatch:
return "patch"
case TargetMinor:
return "minor"
case TargetMajor:
return "major"
default:
panic(fmt.Errorf("unexpected TargetRevision value %v", *t))
}
}

func (t *TargetRelease) Set(value string) error {
switch value {
case "patch":
*t = TargetPatch
case "minor":
*t = TargetMinor
case "major":
*t = TargetMajor
default:
return errors.New(`parse error`)
}
return nil
}

const DefaultTargetRelease = TargetPatch

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 TargetRelease) (string, error) {
re := regexp.MustCompile(
`(?P<major>x)(?P<minor>\.y)?(?P<patch>\.z)?(?P<pre>-p)?(?P<meta>\+m)?`)

Expand All @@ -63,33 +102,48 @@ func (v Version) Format(format string) (string, error) {

var buf buffer

major := v.Major
minor := v.Minor
patch := v.Patch

if v.Commits > 0 && v.preRelease == "" {
switch target {
case TargetMajor:
major++
minor = 0
patch = 0
case TargetMinor:
minor++
patch = 0
case TargetPatch:
patch++
}
}

names := re.SubexpNames()
for i := 0; i < len(matches); i++ {
if len(matches[i]) == 0 {
continue
}
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, DefaultTargetRelease)
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 TargetRelease
}{
{
FullFormat,
"",
"1.2.4-dev.10+fcf2c8f",
DefaultTargetRelease,
},
{
NoMetaFormat,
"",
"1.2.4-dev.10",
DefaultTargetRelease,
},
{
NoPreFormat,
"",
"1.2.4",
DefaultTargetRelease,
},
{
NoPatchFormat,
"",
"1.2",
DefaultTargetRelease,
},
{
NoMinorFormat,
"v",
"v1",
DefaultTargetRelease,
},
{
"x.y-p",
"v",
"v1.2-dev.10",
DefaultTargetRelease,
},
{
FullFormat,
"",
"1.2.4-dev.10+fcf2c8f",
TargetPatch,
},
{
FullFormat,
"",
"1.3.0-dev.10+fcf2c8f",
TargetMinor,
},
{
FullFormat,
"",
"2.0.0-dev.10+fcf2c8f",
TargetMajor,
},
} {
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", DefaultTargetRelease)
assert.EqualError(t, err, "invalid format: q")
assert.Equal(t, "", s)
}

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

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

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

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

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

{
var v TargetRelease
assert.NoError(t, v.Set("patch"))
assert.Equal(t, TargetPatch, v)
}

{
var v TargetRelease
assert.NoError(t, v.Set("minor"))
assert.Equal(t, TargetMinor, v)
}

{
var v TargetRelease
assert.NoError(t, v.Set("major"))
assert.Equal(t, TargetMajor, v)
}
}

0 comments on commit 4cfda54

Please sign in to comment.