From df831077e528a5ce57406f8127864a6acd6262b4 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 19 Jan 2019 16:57:58 -0200 Subject: [PATCH] fix: improved semver parsing --- internal/pipe/docker/docker_test.go | 10 +++++ internal/pipe/release/release.go | 8 +--- internal/pipe/release/release_test.go | 22 ++++++++-- internal/pipe/semver/semver.go | 41 ++++++++++++++++++ internal/pipe/semver/semver_test.go | 62 +++++++++++++++++++++++++++ internal/pipeline/pipeline.go | 3 ++ internal/tmpl/tmpl.go | 14 ++---- internal/tmpl/tmpl_test.go | 27 ++++++------ pkg/context/context.go | 9 ++++ 9 files changed, 161 insertions(+), 35 deletions(-) create mode 100644 internal/pipe/semver/semver.go create mode 100644 internal/pipe/semver/semver_test.go diff --git a/internal/pipe/docker/docker_test.go b/internal/pipe/docker/docker_test.go index 197917da7..675ebbc4d 100644 --- a/internal/pipe/docker/docker_test.go +++ b/internal/pipe/docker/docker_test.go @@ -580,6 +580,11 @@ func TestRunPipe(t *testing.T) { CurrentTag: "v1.0.0", Commit: "a1b2c3d4", } + ctx.Semver = context.Semver{ + Major: 1, + Minor: 0, + Patch: 0, + } for _, os := range []string{"linux", "darwin"} { for _, arch := range []string{"amd64", "386"} { for _, bin := range []string{"mybin", "anotherbin"} { @@ -818,6 +823,11 @@ func Test_processImageTemplates(t *testing.T) { CurrentTag: "v1.0.0", Commit: "a1b2c3d4", } + ctx.Semver = context.Semver{ + Major: 1, + Minor: 0, + Patch: 0, + } assert.NoError(t, Pipe{}.Default(ctx)) assert.Len(t, ctx.Config.Dockers, 1) diff --git a/internal/pipe/release/release.go b/internal/pipe/release/release.go index 925f4a45c..80ed35052 100644 --- a/internal/pipe/release/release.go +++ b/internal/pipe/release/release.go @@ -4,7 +4,6 @@ import ( "os" "time" - "github.com/Masterminds/semver" "github.com/apex/log" "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/client" @@ -40,12 +39,7 @@ func (Pipe) Default(ctx *context.Context) error { // Check if we have to check the git tag for an indicator to mark as pre release switch ctx.Config.Release.Prerelease { case "auto": - sv, err := semver.NewVersion(ctx.Git.CurrentTag) - if err != nil { - return errors.Wrapf(err, "failed to parse tag %s as semver", ctx.Git.CurrentTag) - } - - if sv.Prerelease() != "" { + if ctx.Semver.Prerelease != "" { ctx.PreRelease = true } log.Debugf("pre-release was detected for tag %s: %v", ctx.Git.CurrentTag, ctx.PreRelease) diff --git a/internal/pipe/release/release_test.go b/internal/pipe/release/release_test.go index 4f3d2e562..b81273e30 100644 --- a/internal/pipe/release/release_test.go +++ b/internal/pipe/release/release_test.go @@ -189,7 +189,11 @@ func TestDefaultPreReleaseAuto(t *testing.T) { Prerelease: "auto", }, }) - ctx.Git.CurrentTag = "v1.0.0" + ctx.Semver = context.Semver{ + Major: 1, + Minor: 0, + Patch: 0, + } assert.NoError(t, Pipe{}.Default(ctx)) assert.Equal(t, false, ctx.PreRelease) }) @@ -200,12 +204,17 @@ func TestDefaultPreReleaseAuto(t *testing.T) { Prerelease: "auto", }, }) - ctx.Git.CurrentTag = "v1.0.1-rc1" + ctx.Semver = context.Semver{ + Major: 1, + Minor: 0, + Patch: 0, + Prerelease: "rc1", + } assert.NoError(t, Pipe{}.Default(ctx)) assert.Equal(t, true, ctx.PreRelease) }) - t.Run("auto-rc", func(t *testing.T) { + t.Run("auto-rc-github-setup", func(t *testing.T) { var ctx = context.New(config.Project{ Release: config.Release{ GitHub: config.Repo{ @@ -215,7 +224,12 @@ func TestDefaultPreReleaseAuto(t *testing.T) { Prerelease: "auto", }, }) - ctx.Git.CurrentTag = "v1.0.1-rc1" + ctx.Semver = context.Semver{ + Major: 1, + Minor: 0, + Patch: 0, + Prerelease: "rc1", + } assert.NoError(t, Pipe{}.Default(ctx)) assert.Equal(t, true, ctx.PreRelease) }) diff --git a/internal/pipe/semver/semver.go b/internal/pipe/semver/semver.go new file mode 100644 index 000000000..505059849 --- /dev/null +++ b/internal/pipe/semver/semver.go @@ -0,0 +1,41 @@ +package semver + +import ( + "github.com/Masterminds/semver" + "github.com/apex/log" + "github.com/goreleaser/goreleaser/internal/pipe" + "github.com/goreleaser/goreleaser/pkg/context" + "github.com/pkg/errors" +) + +// Pipe is a global hook pipe +type Pipe struct{} + +// String is the name of this pipe +func (Pipe) String() string { + return "Parsing tag" +} + +// Run executes the hooks +func (Pipe) Run(ctx *context.Context) error { + sv, err := semver.NewVersion(ctx.Git.CurrentTag) + if err != nil { + if ctx.Snapshot { + return pipe.ErrSnapshotEnabled + } + if ctx.SkipValidate { + log.WithError(err). + WithField("tag", ctx.Git.CurrentTag). + Warn("current tag is not a semantic tag") + return pipe.ErrSkipValidateEnabled + } + return errors.Wrapf(err, "failed to parse tag %s as semver", ctx.Git.CurrentTag) + } + ctx.Semver = context.Semver{ + Major: sv.Major(), + Minor: sv.Minor(), + Patch: sv.Patch(), + Prerelease: sv.Prerelease(), + } + return nil +} diff --git a/internal/pipe/semver/semver_test.go b/internal/pipe/semver/semver_test.go new file mode 100644 index 000000000..acf4d3a8b --- /dev/null +++ b/internal/pipe/semver/semver_test.go @@ -0,0 +1,62 @@ +package semver + +import ( + "testing" + + "github.com/goreleaser/goreleaser/internal/pipe" + + "github.com/goreleaser/goreleaser/pkg/config" + "github.com/goreleaser/goreleaser/pkg/context" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDescription(t *testing.T) { + assert.NotEmpty(t, Pipe{}.String()) +} + +func TestValidSemver(t *testing.T) { + var ctx = context.New(config.Project{}) + ctx.Git.CurrentTag = "v1.5.2-rc1" + require.NoError(t, Pipe{}.Run(ctx)) + require.Equal(t, context.Semver{ + Major: 1, + Minor: 5, + Patch: 2, + Prerelease: "rc1", + }, ctx.Semver) +} + +func TestInvalidSemver(t *testing.T) { + var ctx = context.New(config.Project{}) + ctx.Git.CurrentTag = "aaaav1.5.2-rc1" + var err = Pipe{}.Run(ctx) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to parse tag aaaav1.5.2-rc1 as semver") +} + +func TestInvalidSemverOnSnapshots(t *testing.T) { + var ctx = context.New(config.Project{}) + ctx.Git.CurrentTag = "aaaav1.5.2-rc1" + ctx.Snapshot = true + require.EqualError(t, Pipe{}.Run(ctx), pipe.ErrSnapshotEnabled.Error()) + require.Equal(t, context.Semver{ + Major: 0, + Minor: 0, + Patch: 0, + Prerelease: "", + }, ctx.Semver) +} + +func TestInvalidSemverSkipValidate(t *testing.T) { + var ctx = context.New(config.Project{}) + ctx.Git.CurrentTag = "aaaav1.5.2-rc1" + ctx.SkipValidate = true + require.EqualError(t, Pipe{}.Run(ctx), pipe.ErrSkipValidateEnabled.Error()) + require.Equal(t, context.Semver{ + Major: 0, + Minor: 0, + Patch: 0, + Prerelease: "", + }, ctx.Semver) +} diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index bf736424a..712a616a9 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -4,6 +4,8 @@ package pipeline import ( "fmt" + "github.com/goreleaser/goreleaser/internal/pipe/semver" + "github.com/goreleaser/goreleaser/internal/pipe/archive" "github.com/goreleaser/goreleaser/internal/pipe/before" "github.com/goreleaser/goreleaser/internal/pipe/build" @@ -36,6 +38,7 @@ type Piper interface { var Pipeline = []Piper{ before.Pipe{}, // run global hooks before build git.Pipe{}, // get and validate git repo state + semver.Pipe{}, // parse current tag to a semver defaults.Pipe{}, // load default configs snapshot.Pipe{}, // snapshot version handling dist.Pipe{}, // ensure ./dist is clean diff --git a/internal/tmpl/tmpl.go b/internal/tmpl/tmpl.go index 3f479e8d3..f6b21c5c5 100644 --- a/internal/tmpl/tmpl.go +++ b/internal/tmpl/tmpl.go @@ -6,10 +6,8 @@ import ( "text/template" "time" - "github.com/Masterminds/semver" "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/pkg/context" - "github.com/pkg/errors" ) // Template holds data that can be applied to a template string @@ -57,6 +55,10 @@ func New(ctx *context.Context) *Template { env: ctx.Env, date: time.Now().UTC().Format(time.RFC3339), timestamp: time.Now().UTC().Unix(), + major: ctx.Semver.Major, + minor: ctx.Semver.Minor, + patch: ctx.Semver.Patch, + // TODO: no reason not to add prerelease here too I guess }, } } @@ -90,14 +92,6 @@ func (t *Template) Apply(s string) (string, error) { return "", err } - sv, err := semver.NewVersion(t.fields[tag].(string)) - if err != nil { - return "", errors.Wrap(err, "tmpl") - } - t.fields[major] = sv.Major() - t.fields[minor] = sv.Minor() - t.fields[patch] = sv.Patch() - err = tmpl.Execute(&out, t.fields) return out.String(), err } diff --git a/internal/tmpl/tmpl_test.go b/internal/tmpl/tmpl_test.go index f0505cd67..e7623eb4d 100644 --- a/internal/tmpl/tmpl_test.go +++ b/internal/tmpl/tmpl_test.go @@ -16,8 +16,13 @@ func TestWithArtifact(t *testing.T) { ctx.Env = map[string]string{ "FOO": "bar", } - ctx.Version = "1.0.0" - ctx.Git.CurrentTag = "v1.0.0" + ctx.Version = "1.2.3" + ctx.Git.CurrentTag = "v1.2.3" + ctx.Semver = context.Semver{ + Major: 1, + Minor: 2, + Patch: 3, + } ctx.Git.Commit = "commit" ctx.Git.FullCommit = "fullcommit" ctx.Git.ShortCommit = "shortcommit" @@ -26,8 +31,9 @@ func TestWithArtifact(t *testing.T) { "Linux": "{{.Os}}", "amd64": "{{.Arch}}", "6": "{{.Arm}}", - "1.0.0": "{{.Version}}", - "v1.0.0": "{{.Tag}}", + "1.2.3": "{{.Version}}", + "v1.2.3": "{{.Tag}}", + "1-2-3": "{{.Major}}-{{.Minor}}-{{.Patch}}", "commit": "{{.Commit}}", "fullcommit": "{{.FullCommit}}", "shortcommit": "{{.ShortCommit}}", @@ -136,7 +142,9 @@ func TestFuncMap(t *testing.T) { } func TestInvalidTemplate(t *testing.T) { - _, err := New(context.New(config.Project{})).Apply("{{{.Foo}") + ctx := context.New(config.Project{}) + ctx.Git.CurrentTag = "v1.1.1" + _, err := New(ctx).Apply("{{{.Foo}") assert.EqualError(t, err, "template: tmpl:1: unexpected \"{\" in command") } @@ -147,12 +155,3 @@ func TestEnvNotFound(t *testing.T) { assert.Empty(t, result) assert.EqualError(t, err, `template: tmpl:1:6: executing "tmpl" at <.Env.FOO>: map has no entry for key "FOO"`) } - -// This should actually never happen... -func TestInvalidSemver(t *testing.T) { - var ctx = context.New(config.Project{}) - ctx.Git.CurrentTag = "v1_2_3" - result, err := New(ctx).Apply("{{.Major}}") - assert.Empty(t, result) - assert.EqualError(t, err, `tmpl: Invalid Semantic Version`) -} diff --git a/pkg/context/context.go b/pkg/context/context.go index ce830c130..ca6d7726c 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -43,6 +43,15 @@ type Context struct { Debug bool PreRelease bool Parallelism int + Semver Semver +} + +// Semver represents a semantic version +type Semver struct { + Major int64 + Minor int64 + Patch int64 + Prerelease string } // New context