From e880a1463887bc54e4007d5ec1f4bfad2b9dfb18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Galle?= Date: Thu, 2 Apr 2020 10:59:35 +0200 Subject: [PATCH] init; add increment logic --- .editorconfig | 8 ++ .gitignore | 14 +++ Magefile.go | 15 +++ README.md | 15 +++ cmd/verit.go | 5 + cmd/verit_test.go | 1 + go.mod | 8 ++ go.sum | 8 ++ pkg/git/git.go | 1 + pkg/version/version.go | 71 +++++++++++++ pkg/version/version_test.go | 202 ++++++++++++++++++++++++++++++++++++ 11 files changed, 348 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 Magefile.go create mode 100644 README.md create mode 100644 cmd/verit.go create mode 100644 cmd/verit_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/git/git.go create mode 100644 pkg/version/version.go create mode 100644 pkg/version/version_test.go diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..97e7975 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30c9484 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +/build diff --git a/Magefile.go b/Magefile.go new file mode 100644 index 0000000..325a25e --- /dev/null +++ b/Magefile.go @@ -0,0 +1,15 @@ +//+build mage + +package main + +import ( + "github.com/magefile/mage/sh" +) + +func Build() error { + return sh.RunV("go", "build", "-o", "build/verit", "cmd/verit.go") +} + +func Test() error { + return sh.RunV("go", "test", "./...") +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..b260b2e --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# VerIt +``` +$ git init && echo "hello" > README.md && git add . && git commit -m "initial commit" +$ verit 0.x.0 +0.1.0 +# end of (successful) build +$ git push --tags +$ verit 0.x.0 +0.2.0 +# manually bump major version 0 -> 1, don't move the x for doing just that! +$ verit 1.x.0 +1.0.0 +$ verit 1.x.0 +1.1.0 +``` diff --git a/cmd/verit.go b/cmd/verit.go new file mode 100644 index 0000000..1e71dd1 --- /dev/null +++ b/cmd/verit.go @@ -0,0 +1,5 @@ +package main + +func main() { + print("hello world") +} diff --git a/cmd/verit_test.go b/cmd/verit_test.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/cmd/verit_test.go @@ -0,0 +1 @@ +package main diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..939a89b --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module verit + +go 1.14 + +require ( + github.com/Masterminds/semver/v3 v3.0.3 + github.com/magefile/mage v1.9.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ddffde9 --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14= +github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/blang/semver v1.1.0 h1:ol1rO7QQB5uy7umSNV7VAmLugfLRD+17sYJujRNYPhg= +github.com/blang/semver v2.2.0+incompatible h1:DIb+hEi/XKX6t9Cvy5+oSlANqmc0eenMxbNBvLqpV2A= +github.com/blang/semver v2.2.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= +github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= diff --git a/pkg/git/git.go b/pkg/git/git.go new file mode 100644 index 0000000..cd99cdb --- /dev/null +++ b/pkg/git/git.go @@ -0,0 +1 @@ +package git diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 0000000..b42185f --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,71 @@ +package version + +import ( + "fmt" + "github.com/Masterminds/semver/v3" + "strings" +) + +const positionMajor = 0 +const positionMinor = 1 +const positionPatch = 2 + +func Next(current string, incrementPattern string) (string, error) { + currentVersion, err := semver.StrictNewVersion(current) + if err != nil { + return "", err + } + // Also, check if incrementPattern was a valid semver version if "x"s were numbers + incrementPatternWithoutXs := strings.ReplaceAll(incrementPattern, "x", "0") + incrementVersion, err := semver.StrictNewVersion(incrementPatternWithoutXs) + if err != nil { + return "", err + } + + // only need to consider major.minor.patch (="version core") + versionCore := incrementPattern + var versionExtension string + if indexExtension := strings.IndexAny(incrementPattern, "+-"); indexExtension != -1 { + versionCore = incrementPattern[:indexExtension] + versionExtension = incrementPattern[indexExtension:] + } + + // ensure there is at most one x and determine its index + positionX := -1 + var countX int + for i, s := range strings.Split(versionCore, ".") { + if s == "x" { + positionX = i + countX++ + } + if countX > 1 { + return "", fmt.Errorf("only one x allowed") + } + } + + // set and maybe increment new version + nextMajor := incrementVersion.Major() + nextMinor := incrementVersion.Minor() + nextPatch := incrementVersion.Patch() + + switch positionX { + case positionMajor: + if currentVersion.Minor() == incrementVersion.Minor() && currentVersion.Patch() == incrementVersion.Patch() { + nextMajor = currentVersion.Major() + 1 + } + case positionMinor: + if currentVersion.Major() == incrementVersion.Major() && currentVersion.Patch() == incrementVersion.Patch() { + nextMinor = currentVersion.Minor() + 1 + } + case positionPatch: + if currentVersion.Major() == incrementVersion.Major() && currentVersion.Minor() == incrementVersion.Minor() { + nextPatch = currentVersion.Patch() + 1 + } + } + var result string + newVersion, err := semver.StrictNewVersion(fmt.Sprintf("%v.%v.%v%v", nextMajor, nextMinor, nextPatch, versionExtension)) + if err == nil { + result = newVersion.String() + } + return result, err +} diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go new file mode 100644 index 0000000..bcf5cdf --- /dev/null +++ b/pkg/version/version_test.go @@ -0,0 +1,202 @@ +package version + +import ( + "testing" +) + +func TestNext(t *testing.T) { + type args struct { + current string + incrementPattern string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "reject non semver", + args: args{ + current: "0.0", + incrementPattern: "0.x.0", + }, + want: "", + wantErr: true, + }, + { + name: "reject non semver increment", + args: args{ + current: "0.0.0", + incrementPattern: "0.x", + }, + want: "", + wantErr: true, + }, + { + name: "increment major", + args: args{ + current: "0.0.0", + incrementPattern: "x.0.0", + }, + want: "1.0.0", + wantErr: false, + }, + { + name: "increment minor", + args: args{ + current: "0.0.0", + incrementPattern: "0.x.0", + }, + want: "0.1.0", + wantErr: false, + }, + { + name: "explicit bump", + args: args{ + current: "0.1.0", + incrementPattern: "1.x.0", + }, + want: "1.0.0", + wantErr: false, + }, + { + name: "increment patch", + args: args{ + current: "0.0.0", + incrementPattern: "0.0.x", + }, + want: "0.0.1", + wantErr: false, + }, + { + name: "too many 'x's", + args: args{ + current: "0.0.0", + incrementPattern: "0.x.x", + }, + want: "", + wantErr: true, + }, + { + name: "too many 'x's, one bordering build", + args: args{ + current: "0.0.0", + incrementPattern: "0.x.x+build", + }, + want: "", + wantErr: true, + }, + { + name: "too many 'x's, one bordering pre-release", + args: args{ + current: "0.0.0", + incrementPattern: "0.x.x-alpha", + }, + want: "", + wantErr: true, + }, + { + name: "missing x", + args: args{ + current: "0.0.0", + incrementPattern: "0.0.0", + }, + want: "0.0.0", + wantErr: false, + }, + { + name: "missing x with x in build", + args: args{ + current: "0.0.0", + incrementPattern: "0.0.0+build.x", + }, + want: "0.0.0+build.x", + wantErr: false, + }, + { + name: "starting pre-release", + args: args{ + current: "0.0.0", + incrementPattern: "0.0.x-alpha.x", + }, + want: "0.0.1-alpha.x", + wantErr: false, + }, + { + name: "pre-release and build", + args: args{ + current: "0.0.0-alpha+build", + incrementPattern: "0.0.x-alpha+build", + }, + want: "0.0.1-alpha+build", + wantErr: false, + }, + { + name: "x only in pre-release", + args: args{ + current: "0.0.1-alpha.0", + incrementPattern: "0.0.1-alpha.x", + }, + want: "0.0.1-alpha.x", + wantErr: false, + }, + { + name: "incrementing and changing pre-release", + args: args{ + current: "0.0.0-alpha.0", + incrementPattern: "0.0.x-alpha.1", + }, + want: "0.0.1-alpha.1", + wantErr: false, + }, + { + name: "ignore extra x in pre-release", + args: args{ + current: "0.0.0-alpha.0", + incrementPattern: "0.0.x-alpha.x", + }, + want: "0.0.1-alpha.x", + wantErr: false, + }, + { + name: "handle build", + args: args{ + current: "0.0.0+build", + incrementPattern: "0.0.x+build", + }, + want: "0.0.1+build", + wantErr: false, + }, + { + name: "ignore extra x in build", + args: args{ + current: "0.0.0+build.x", + incrementPattern: "0.0.x+build.x", + }, + want: "0.0.1+build.x", + wantErr: false, + }, + { + name: "ignore x in build", + args: args{ + current: "0.0.0+build.x", + incrementPattern: "0.0.0+build.x", + }, + want: "0.0.0+build.x", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Next(tt.args.current, tt.args.incrementPattern) + if (err != nil) != tt.wantErr { + t.Errorf("Next() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Next() got = %v, want %v", got, tt.want) + } + }) + } +}