From 16cb4d8277bcfaf0b09e7a1c7f2104f447f4e0f4 Mon Sep 17 00:00:00 2001 From: Ivan Novikov Date: Fri, 15 Nov 2019 13:22:11 +0000 Subject: [PATCH] feat: Added new flags to support release notes header and footer. (#1212) * Added new flags to support release notes header and footer. Created two flags for release generation. --release-footer --release-header These flags can help you to add custom changelog text before/after changes that are generated by git log. * Fix changelog.go to avoid lint errors * Fix test typo * Added tests for main, fixed bug with no passing options to release ctx * Add @caarlos0 suggestions --- internal/pipe/changelog/changelog.go | 32 ++++++++- internal/pipe/changelog/changelog_test.go | 65 +++++++++++++++++++ .../pipe/changelog/testdata/release-footer.md | 1 + .../pipe/changelog/testdata/release-header.md | 1 + main.go | 46 +++++++------ main_test.go | 46 ++++++++++++- pkg/context/context.go | 34 +++++----- 7 files changed, 186 insertions(+), 39 deletions(-) create mode 100644 internal/pipe/changelog/testdata/release-footer.md create mode 100644 internal/pipe/changelog/testdata/release-header.md diff --git a/internal/pipe/changelog/changelog.go b/internal/pipe/changelog/changelog.go index 17827e147bc..e78f63943f0 100644 --- a/internal/pipe/changelog/changelog.go +++ b/internal/pipe/changelog/changelog.go @@ -35,6 +35,8 @@ func (Pipe) Run(ctx *context.Context) error { if err != nil { return err } + log.WithField("file", ctx.ReleaseNotes).Info("loaded custom release notes") + log.WithField("file", ctx.ReleaseNotes).Debugf("custom release notes: \n%s", notes) ctx.ReleaseNotes = notes } if ctx.Config.Changelog.Skip { @@ -46,9 +48,25 @@ func (Pipe) Run(ctx *context.Context) error { if ctx.ReleaseNotes != "" { return nil } + if ctx.ReleaseHeader != "" { + header, err := loadFromFile(ctx.ReleaseHeader) + if err != nil { + return err + } + ctx.ReleaseHeader = header + } + if ctx.ReleaseFooter != "" { + footer, err := loadFromFile(ctx.ReleaseFooter) + if err != nil { + return err + } + ctx.ReleaseFooter = footer + } + if err := checkSortDirection(ctx.Config.Changelog.Sort); err != nil { return err } + entries, err := buildChangelog(ctx) if err != nil { return err @@ -61,7 +79,17 @@ func (Pipe) Run(ctx *context.Context) error { log.Debug("is gitlab or gitea changelog") changelogStringJoiner = " \n" } - ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v\n", strings.Join(entries, changelogStringJoiner)) + + ctx.ReleaseNotes = strings.Join( + []string{ + ctx.ReleaseHeader, + "## Changelog", + strings.Join(entries, changelogStringJoiner), + ctx.ReleaseFooter, + }, + "\n\n", + ) + var path = filepath.Join(ctx.Config.Dist, "CHANGELOG.md") log.WithField("changelog", path).Info("writing") return ioutil.WriteFile(path, []byte(ctx.ReleaseNotes), 0644) @@ -72,8 +100,6 @@ func loadFromFile(file string) (string, error) { if err != nil { return "", err } - log.WithField("file", file).Info("loaded custom release notes") - log.WithField("file", file).Debugf("custom release notes: \n%s", string(bts)) return string(bts), nil } diff --git a/internal/pipe/changelog/changelog_test.go b/internal/pipe/changelog/changelog_test.go index 5e3f206571e..bd43133a78b 100644 --- a/internal/pipe/changelog/changelog_test.go +++ b/internal/pipe/changelog/changelog_test.go @@ -2,6 +2,7 @@ package changelog import ( "io/ioutil" + "os" "path/filepath" "testing" @@ -46,6 +47,18 @@ func TestChangelogSkip(t *testing.T) { testlib.AssertSkipped(t, Pipe{}.Run(ctx)) } +func TestReleaseHeaderProvidedViaFlagDoesntExist(t *testing.T) { + var ctx = context.New(config.Project{}) + ctx.ReleaseHeader = "testdata/header.nope" + require.EqualError(t, Pipe{}.Run(ctx), "open testdata/header.nope: no such file or directory") +} + +func TestReleaseFooterProvidedViaFlagDoesntExist(t *testing.T) { + var ctx = context.New(config.Project{}) + ctx.ReleaseFooter = "testdata/footer.nope" + require.EqualError(t, Pipe{}.Run(ctx), "open testdata/footer.nope: no such file or directory") +} + func TestSnapshot(t *testing.T) { var ctx = context.New(config.Project{}) ctx.Snapshot = true @@ -285,3 +298,55 @@ func TestChangelogOnBranchWithSameNameAsTag(t *testing.T) { require.Contains(t, ctx.ReleaseNotes, msg) } } + +func TestChangeLogWithReleaseHeader(t *testing.T) { + current, err := os.Getwd() + require.NoError(t, err) + tmpdir, back := testlib.Mktmp(t) + defer back() + require.NoError(t, os.Symlink(current+"/testdata", tmpdir+"/testdata")) + 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") + testlib.GitCheckoutBranch(t, "v0.0.1") + var ctx = context.New(config.Project{}) + ctx.Git.CurrentTag = "v0.0.1" + ctx.ReleaseHeader = "testdata/release-header.md" + require.NoError(t, Pipe{}.Run(ctx)) + require.Contains(t, ctx.ReleaseNotes, "## Changelog") + require.Contains(t, ctx.ReleaseNotes, "test header") +} + +func TestChangeLogWithReleaseFooter(t *testing.T) { + current, err := os.Getwd() + require.NoError(t, err) + tmpdir, back := testlib.Mktmp(t) + defer back() + require.NoError(t, os.Symlink(current+"/testdata", tmpdir+"/testdata")) + 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") + testlib.GitCheckoutBranch(t, "v0.0.1") + var ctx = context.New(config.Project{}) + ctx.Git.CurrentTag = "v0.0.1" + ctx.ReleaseFooter = "testdata/release-footer.md" + require.NoError(t, Pipe{}.Run(ctx)) + require.Contains(t, ctx.ReleaseNotes, "## Changelog") + require.Contains(t, ctx.ReleaseNotes, "test footer") +} diff --git a/internal/pipe/changelog/testdata/release-footer.md b/internal/pipe/changelog/testdata/release-footer.md new file mode 100644 index 00000000000..15b9a75ff41 --- /dev/null +++ b/internal/pipe/changelog/testdata/release-footer.md @@ -0,0 +1 @@ +test footer diff --git a/internal/pipe/changelog/testdata/release-header.md b/internal/pipe/changelog/testdata/release-header.md new file mode 100644 index 00000000000..865d74877e2 --- /dev/null +++ b/internal/pipe/changelog/testdata/release-header.md @@ -0,0 +1 @@ +test header diff --git a/main.go b/main.go index 5e3546b0754..e37e7dc287f 100644 --- a/main.go +++ b/main.go @@ -16,7 +16,7 @@ import ( "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" "github.com/goreleaser/goreleaser/pkg/defaults" - kingpin "gopkg.in/alecthomas/kingpin.v2" + "gopkg.in/alecthomas/kingpin.v2" ) // nolint: gochecknoglobals @@ -28,15 +28,17 @@ var ( ) type releaseOptions struct { - Config string - ReleaseNotes string - Snapshot bool - SkipPublish bool - SkipSign bool - SkipValidate bool - RmDist bool - Parallelism int - Timeout time.Duration + Config string + ReleaseNotes string + ReleaseHeader string + ReleaseFooter string + Snapshot bool + SkipPublish bool + SkipSign bool + SkipValidate bool + RmDist bool + Parallelism int + Timeout time.Duration } func main() { @@ -56,6 +58,8 @@ func main() { var checkCmd = app.Command("check", "Checks if configuration is valid").Alias("c") var releaseCmd = app.Command("release", "Releases the current project").Alias("r").Default() var releaseNotes = releaseCmd.Flag("release-notes", "Load custom release notes from a markdown file").PlaceHolder("notes.md").String() + var releaseHeader = releaseCmd.Flag("release-header", "Load custom release notes header from a markdown file").PlaceHolder("notes-header.md").String() + var releaseFooter = releaseCmd.Flag("release-footer", "Load custom release notes footer from a markdown file").PlaceHolder("notes-footer.md").String() var snapshot = releaseCmd.Flag("snapshot", "Generate an unversioned snapshot release, skipping all validations and without publishing any artifacts").Bool() var skipPublish = releaseCmd.Flag("skip-publish", "Skips publishing artifacts").Bool() var skipSign = releaseCmd.Flag("skip-sign", "Skips signing the artifacts").Bool() @@ -96,15 +100,17 @@ func main() { start := time.Now() log.Infof(color.New(color.Bold).Sprintf("releasing using goreleaser %s...", version)) var options = releaseOptions{ - Config: *config, - ReleaseNotes: *releaseNotes, - Snapshot: *snapshot, - SkipPublish: *skipPublish, - SkipValidate: *skipValidate, - SkipSign: *skipSign, - RmDist: *rmDist, - Parallelism: *parallelism, - Timeout: *timeout, + Config: *config, + ReleaseNotes: *releaseNotes, + ReleaseHeader: *releaseHeader, + ReleaseFooter: *releaseFooter, + Snapshot: *snapshot, + SkipPublish: *skipPublish, + SkipValidate: *skipValidate, + SkipSign: *skipSign, + RmDist: *rmDist, + Parallelism: *parallelism, + Timeout: *timeout, } if err := releaseProject(options); err != nil { log.WithError(err).Errorf(color.New(color.Bold).Sprintf("release failed after %0.2fs", time.Since(start).Seconds())) @@ -141,6 +147,8 @@ func releaseProject(options releaseOptions) error { ctx.Parallelism = options.Parallelism log.Debugf("parallelism: %v", ctx.Parallelism) ctx.ReleaseNotes = options.ReleaseNotes + ctx.ReleaseHeader = options.ReleaseHeader + ctx.ReleaseFooter = options.ReleaseFooter ctx.Snapshot = options.Snapshot ctx.SkipPublish = ctx.Snapshot || options.SkipPublish ctx.SkipValidate = ctx.Snapshot || options.SkipValidate diff --git a/main_test.go b/main_test.go index e041a8525b8..bc7618d768b 100644 --- a/main_test.go +++ b/main_test.go @@ -11,7 +11,7 @@ import ( "github.com/goreleaser/goreleaser/pkg/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" ) func init() { @@ -107,6 +107,50 @@ func TestCustomReleaseNotesFile(t *testing.T) { assert.NoError(t, releaseProject(params)) } +func TestCustomReleaseHeaderFileDontExist(t *testing.T) { + _, back := setup(t) + defer back() + params := testParams() + params.ReleaseHeader = "/header/that/dont/exist" + params.Snapshot = false + assert.Error(t, releaseProject(params)) +} + +func TestCustomReleaseHeaderFile(t *testing.T) { + _, back := setup(t) + defer back() + releaseHeader, err := ioutil.TempFile("", "") + assert.NoError(t, err) + createFile(t, releaseHeader.Name(), "some release header") + params := testParams() + params.ReleaseHeader = releaseHeader.Name() + params.Snapshot = false + params.SkipPublish = true + assert.NoError(t, releaseProject(params)) +} + +func TestCustomReleaseFooterFileDontExist(t *testing.T) { + _, back := setup(t) + defer back() + params := testParams() + params.ReleaseFooter = "/footer/that/dont/exist" + params.Snapshot = false + assert.Error(t, releaseProject(params)) +} + +func TestCustomReleaseFooterFile(t *testing.T) { + _, back := setup(t) + defer back() + releaseFooter, err := ioutil.TempFile("", "") + assert.NoError(t, err) + createFile(t, releaseFooter.Name(), "some release footer") + params := testParams() + params.ReleaseFooter = releaseFooter.Name() + params.Snapshot = false + params.SkipPublish = true + assert.NoError(t, releaseProject(params)) +} + func TestBrokenPipe(t *testing.T) { _, back := setup(t) defer back() diff --git a/pkg/context/context.go b/pkg/context/context.go index be9d81186b9..e09bf33d55b 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -53,22 +53,24 @@ const ( // Context carries along some data through the pipes type Context struct { ctx.Context - Config config.Project - Env Env - Token string - TokenType TokenType - Git GitInfo - Artifacts artifact.Artifacts - ReleaseNotes string - Version string - Snapshot bool - SkipPublish bool - SkipSign bool - SkipValidate bool - RmDist bool - PreRelease bool - Parallelism int - Semver Semver + Config config.Project + Env Env + Token string + TokenType TokenType + Git GitInfo + Artifacts artifact.Artifacts + ReleaseNotes string + ReleaseHeader string + ReleaseFooter string + Version string + Snapshot bool + SkipPublish bool + SkipSign bool + SkipValidate bool + RmDist bool + PreRelease bool + Parallelism int + Semver Semver } // Semver represents a semantic version