From 87d269dc457719491fdadd50e11dd2883c4ceb5c Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sun, 15 Oct 2017 20:21:35 -0200 Subject: [PATCH] refactor: turned changelog generation into a pipe I turned myself into a pipe morty! PipeRick!!! refs #284 --- config/config.go | 12 +++++ goreleaserlib/goreleaser.go | 2 + internal/git/git.go | 5 ++ pipeline/changelog/changelog.go | 69 ++++++++++++++++++++++++++++ pipeline/changelog/changelog_test.go | 65 ++++++++++++++++++++++++++ pipeline/git/exec.go | 12 ----- pipeline/git/git.go | 59 ++---------------------- pipeline/git/git_test.go | 62 +++---------------------- 8 files changed, 163 insertions(+), 123 deletions(-) create mode 100644 pipeline/changelog/changelog.go create mode 100644 pipeline/changelog/changelog_test.go delete mode 100644 pipeline/git/exec.go diff --git a/config/config.go b/config/config.go index ba90a50df..152c6de62 100644 --- a/config/config.go +++ b/config/config.go @@ -193,6 +193,17 @@ type Docker struct { XXX map[string]interface{} `yaml:",inline"` } +// Filters config +type Filters struct { + Include []string `yaml:",omitempty"` + Exclude []string `yaml:",omitempty"` +} + +// Changelog Config +type Changelog struct { + Filters Filters `yaml:",omitempty"` +} + // Project includes all project configuration type Project struct { ProjectName string `yaml:"project_name,omitempty"` @@ -205,6 +216,7 @@ type Project struct { Snapshot Snapshot `yaml:",omitempty"` Checksum Checksum `yaml:",omitempty"` Dockers []Docker `yaml:",omitempty"` + Changelog Changelog `yaml:",omitempty"` // this is a hack ¯\_(ツ)_/¯ SingleBuild Build `yaml:"build,omitempty"` diff --git a/goreleaserlib/goreleaser.go b/goreleaserlib/goreleaser.go index 4e0d2d821..4bbe7790a 100644 --- a/goreleaserlib/goreleaser.go +++ b/goreleaserlib/goreleaser.go @@ -13,6 +13,7 @@ import ( "github.com/goreleaser/goreleaser/pipeline/archive" "github.com/goreleaser/goreleaser/pipeline/brew" "github.com/goreleaser/goreleaser/pipeline/build" + "github.com/goreleaser/goreleaser/pipeline/changelog" "github.com/goreleaser/goreleaser/pipeline/checksums" "github.com/goreleaser/goreleaser/pipeline/cleandist" "github.com/goreleaser/goreleaser/pipeline/defaults" @@ -28,6 +29,7 @@ import ( var pipes = []pipeline.Pipe{ defaults.Pipe{}, // load default configs git.Pipe{}, // get and validate git repo state + changelog.Pipe{}, // builds the release changelog env.Pipe{}, // load and validate environment variables cleandist.Pipe{}, // ensure ./dist is clean build.Pipe{}, // build diff --git a/internal/git/git.go b/internal/git/git.go index 0d8c0c6e7..0012b6406 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -22,3 +22,8 @@ func Run(args ...string) (output string, err error) { } return string(bts), err } + +// Clean the output +func Clean(output string, err error) (string, error) { + return strings.Replace(strings.Split(output, "\n")[0], "'", "", -1), err +} diff --git a/pipeline/changelog/changelog.go b/pipeline/changelog/changelog.go new file mode 100644 index 000000000..5bb8ed49b --- /dev/null +++ b/pipeline/changelog/changelog.go @@ -0,0 +1,69 @@ +// Package changelog provides the release changelog to goreleaser. +package changelog + +import ( + "fmt" + + "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/git" + "github.com/goreleaser/goreleaser/pipeline" +) + +// Pipe for checksums +type Pipe struct{} + +// Description of the pipe +func (Pipe) Description() string { + return "Generating changelog" +} + +// Run the pipe +func (Pipe) Run(ctx *context.Context) (err error) { + if ctx.ReleaseNotes != "" { + return pipeline.Skip("release notes already provided via --release-notes") + } + var log string + if ctx.Git.CurrentTag == "" { + log, err = getChangelog(ctx.Git.Commit) + } else { + log, err = getChangelog(ctx.Git.CurrentTag) + } + if err != nil { + return err + } + ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v", log) + + return nil +} + +func getChangelog(tag string) (string, error) { + prev, err := previous(tag) + if err != nil { + return "", err + } + if !prev.Tag { + return gitLog(prev.SHA, tag) + } + return gitLog(fmt.Sprintf("%v..%v", prev.SHA, tag)) +} + +func gitLog(refs ...string) (string, error) { + var args = []string{"log", "--pretty=oneline", "--abbrev-commit"} + args = append(args, refs...) + return git.Run(args...) +} + +func previous(tag string) (result ref, err error) { + result.Tag = true + result.SHA, err = git.Clean(git.Run("describe", "--tags", "--abbrev=0", tag+"^")) + if err != nil { + result.Tag = false + result.SHA, err = git.Clean(git.Run("rev-list", "--max-parents=0", "HEAD")) + } + return +} + +type ref struct { + Tag bool + SHA string +} diff --git a/pipeline/changelog/changelog_test.go b/pipeline/changelog/changelog_test.go new file mode 100644 index 000000000..2f2150ae6 --- /dev/null +++ b/pipeline/changelog/changelog_test.go @@ -0,0 +1,65 @@ +package changelog + +import ( + "testing" + + "github.com/goreleaser/goreleaser/internal/testlib" + + "github.com/goreleaser/goreleaser/config" + + "github.com/goreleaser/goreleaser/context" + "github.com/stretchr/testify/assert" +) + +func TestDescription(t *testing.T) { + assert.NotEmpty(t, Pipe{}.Description()) +} + +func TestChangelogProvidedViaFlag(t *testing.T) { + var ctx = context.New(config.Project{}) + ctx.ReleaseNotes = "c0ff33 foo bar" + testlib.AssertSkipped(t, Pipe{}.Run(ctx)) +} + +func TestChangelog(t *testing.T) { + _, back := testlib.Mktmp(t) + defer back() + testlib.GitInit(t) + testlib.GitCommit(t, "first") + testlib.GitTag(t, "v0.0.1") + testlib.GitCommit(t, "added feature 1") + testlib.GitCommit(t, "fixed bug 2") + testlib.GitTag(t, "v0.0.2") + var ctx = context.New(config.Project{}) + ctx.Git.CurrentTag = "v0.0.2" + assert.NoError(t, Pipe{}.Run(ctx)) + assert.Equal(t, "v0.0.2", ctx.Git.CurrentTag) + assert.Contains(t, ctx.ReleaseNotes, "## Changelog") + assert.NotContains(t, ctx.ReleaseNotes, "first") + assert.Contains(t, ctx.ReleaseNotes, "added feature 1") + assert.Contains(t, ctx.ReleaseNotes, "fixed bug 2") +} + +func TestChangelogOfFirstRelease(t *testing.T) { + _, back := testlib.Mktmp(t) + defer back() + testlib.GitInit(t) + var msgs = []string{ + "initial commit", + "another one", + "one more", + "and finally this one", + } + for _, msg := range msgs { + testlib.GitCommit(t, msg) + } + testlib.GitTag(t, "v0.0.1") + var ctx = context.New(config.Project{}) + ctx.Git.CurrentTag = "v0.0.1" + assert.NoError(t, Pipe{}.Run(ctx)) + assert.Equal(t, "v0.0.1", ctx.Git.CurrentTag) + assert.Contains(t, ctx.ReleaseNotes, "## Changelog") + for _, msg := range msgs { + assert.Contains(t, ctx.ReleaseNotes, msg) + } +} diff --git a/pipeline/git/exec.go b/pipeline/git/exec.go deleted file mode 100644 index b320a6405..000000000 --- a/pipeline/git/exec.go +++ /dev/null @@ -1,12 +0,0 @@ -package git - -import ( - "strings" - - "github.com/goreleaser/goreleaser/internal/git" -) - -func cleanGit(args ...string) (output string, err error) { - output, err = git.Run(args...) - return strings.Replace(strings.Split(output, "\n")[0], "'", "", -1), err -} diff --git a/pipeline/git/git.go b/pipeline/git/git.go index c0b5df6ff..884aaec82 100644 --- a/pipeline/git/git.go +++ b/pipeline/git/git.go @@ -4,7 +4,6 @@ package git import ( "bytes" - "fmt" "regexp" "strings" "text/template" @@ -39,9 +38,6 @@ func (Pipe) Run(ctx *context.Context) (err error) { Commit: commit, } log.Infof("releasing %s, commit %s", tag, commit) - if err = setLog(ctx, tag, commit); err != nil { - return - } if err = setVersion(ctx, tag, commit); err != nil { return } @@ -65,23 +61,6 @@ func setVersion(ctx *context.Context, tag, commit string) (err error) { return } -func setLog(ctx *context.Context, tag, commit string) (err error) { - if ctx.ReleaseNotes != "" { - return - } - var log string - if tag == "" { - log, err = getChangelog(commit) - } else { - log, err = getChangelog(tag) - } - if err != nil { - return err - } - ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v", log) - return nil -} - type snapshotNameData struct { Commit string Tag string @@ -114,50 +93,18 @@ func validate(ctx *context.Context, commit, tag string) error { if !regexp.MustCompile("^[0-9.]+").MatchString(ctx.Version) { return ErrInvalidVersionFormat{ctx.Version} } - _, err = cleanGit("describe", "--exact-match", "--tags", "--match", tag) + _, err = git.Clean(git.Run("describe", "--exact-match", "--tags", "--match", tag)) if err != nil { return ErrWrongRef{commit, tag} } return nil } -func getChangelog(tag string) (string, error) { - prev, err := previous(tag) - if err != nil { - return "", err - } - if !prev.Tag { - return gitLog(prev.SHA, tag) - } - return gitLog(fmt.Sprintf("%v..%v", prev.SHA, tag)) -} - -func gitLog(refs ...string) (string, error) { - var args = []string{"log", "--pretty=oneline", "--abbrev-commit"} - args = append(args, refs...) - return git.Run(args...) -} - func getInfo() (tag, commit string, err error) { - tag, err = cleanGit("describe", "--tags", "--abbrev=0") + tag, err = git.Clean(git.Run("describe", "--tags", "--abbrev=0")) if err != nil { log.WithError(err).Info("failed to retrieve current tag") } - commit, err = cleanGit("show", "--format='%H'", "HEAD") + commit, err = git.Clean(git.Run("show", "--format='%H'", "HEAD")) return } - -func previous(tag string) (result ref, err error) { - result.Tag = true - result.SHA, err = cleanGit("describe", "--tags", "--abbrev=0", tag+"^") - if err != nil { - result.Tag = false - result.SHA, err = cleanGit("rev-list", "--max-parents=0", "HEAD") - } - return -} - -type ref struct { - Tag bool - SHA string -} diff --git a/pipeline/git/git_test.go b/pipeline/git/git_test.go index f3ad88926..ffe1dde24 100644 --- a/pipeline/git/git_test.go +++ b/pipeline/git/git_test.go @@ -167,8 +167,6 @@ func TestValidState(t *testing.T) { } assert.NoError(t, Pipe{}.Run(ctx)) assert.Equal(t, "v0.0.2", ctx.Git.CurrentTag) - assert.NotContains(t, "commit4", ctx.ReleaseNotes) - assert.NotContains(t, "commit3", ctx.ReleaseNotes) } func TestNoValidate(t *testing.T) { @@ -186,62 +184,16 @@ func TestNoValidate(t *testing.T) { testlib.AssertSkipped(t, Pipe{}.Run(ctx)) } -func TestChangelog(t *testing.T) { +func TestSnapshot(t *testing.T) { _, back := testlib.Mktmp(t) defer back() testlib.GitInit(t) - testlib.GitCommit(t, "first") - testlib.GitTag(t, "v0.0.1") - testlib.GitCommit(t, "added feature 1") - testlib.GitCommit(t, "fixed bug 2") - testlib.GitTag(t, "v0.0.2") + testlib.GitAdd(t) + testlib.GitCommit(t, "whatever") var ctx = &context.Context{ - Config: config.Project{}, + Config: config.Project{}, + Validate: true, + Snapshot: true, } - testlib.AssertSkipped(t, Pipe{}.Run(ctx)) - assert.Equal(t, "v0.0.2", ctx.Git.CurrentTag) - assert.Contains(t, ctx.ReleaseNotes, "## Changelog") - assert.NotContains(t, ctx.ReleaseNotes, "first") - assert.Contains(t, ctx.ReleaseNotes, "added feature 1") - assert.Contains(t, ctx.ReleaseNotes, "fixed bug 2") -} - -func TestChangelogOfFirstRelease(t *testing.T) { - _, back := testlib.Mktmp(t) - defer back() - testlib.GitInit(t) - var msgs = []string{ - "initial commit", - "another one", - "one more", - "and finally this one", - } - for _, msg := range msgs { - testlib.GitCommit(t, msg) - } - testlib.GitTag(t, "v0.0.1") - var ctx = &context.Context{ - Config: config.Project{}, - } - testlib.AssertSkipped(t, Pipe{}.Run(ctx)) - assert.Equal(t, "v0.0.1", ctx.Git.CurrentTag) - assert.Contains(t, ctx.ReleaseNotes, "## Changelog") - for _, msg := range msgs { - assert.Contains(t, ctx.ReleaseNotes, msg) - } -} - -func TestCustomReleaseNotes(t *testing.T) { - _, back := testlib.Mktmp(t) - defer back() - testlib.GitInit(t) - testlib.GitCommit(t, "first") - testlib.GitTag(t, "v0.0.1") - var ctx = &context.Context{ - Config: config.Project{}, - ReleaseNotes: "custom", - } - testlib.AssertSkipped(t, Pipe{}.Run(ctx)) - assert.Equal(t, "v0.0.1", ctx.Git.CurrentTag) - assert.Equal(t, ctx.ReleaseNotes, "custom") + assert.NoError(t, Pipe{}.Run(ctx)) }