From f8f4ccb8b764ed22270ec86d43f4406e6fb57bc8 Mon Sep 17 00:00:00 2001 From: tsuyoshiwada Date: Sat, 5 May 2018 19:40:25 +0900 Subject: [PATCH] feat: Add `--next-tag` flag (experimental) --- chglog.go | 63 ++++++++++++++++++++++--- chglog_test.go | 99 +++++++++++++++++++++++++++++++++++++-- cmd/git-chglog/config.go | 1 + cmd/git-chglog/context.go | 1 + cmd/git-chglog/main.go | 6 +++ 5 files changed, 160 insertions(+), 10 deletions(-) diff --git a/chglog.go b/chglog.go index f62e63e5..1be130f8 100644 --- a/chglog.go +++ b/chglog.go @@ -16,6 +16,7 @@ import ( // Options is an option used to process commits type Options struct { Processor Processor + NextTag string // Treat unreleased commits as specified tags (EXPERIMENTAL) CommitFilters map[string][]string // Filter by using `Commit` properties and values. Filtering is not done by specifying an empty value CommitSortBy string // Property name to use for sorting `Commit` (e.g. `Scope`) CommitGroupBy string // Property name of `Commit` to be grouped into `CommitGroup` (e.g. `Type`) @@ -150,18 +151,30 @@ func (gen *Generator) Generate(w io.Writer, query string) error { } func (gen *Generator) readVersions(tags []*Tag, first string) ([]*Version, error) { + next := gen.config.Options.NextTag versions := []*Version{} for i, tag := range tags { - var rev string + var ( + isNext = next == tag.Name + rev string + ) - if i+1 < len(tags) { - rev = tags[i+1].Name + ".." + tag.Name - } else { - if first != "" { - rev = first + ".." + tag.Name + if isNext { + if tag.Previous != nil { + rev = tag.Previous.Name + "..HEAD" } else { - rev = tag.Name + rev = "HEAD" + } + } else { + if i+1 < len(tags) { + rev = tags[i+1].Name + ".." + tag.Name + } else { + if first != "" { + rev = first + ".." + tag.Name + } else { + rev = tag.Name + } } } @@ -180,12 +193,21 @@ func (gen *Generator) readVersions(tags []*Tag, first string) ([]*Version, error RevertCommits: revertCommits, NoteGroups: noteGroups, }) + + // Instead of `getTags()`, assign the date to the tag + if isNext { + tag.Date = commits[0].Author.Date + } } return versions, nil } func (gen *Generator) readUnreleased(tags []*Tag) (*Unreleased, error) { + if gen.config.Options.NextTag != "" { + return &Unreleased{}, nil + } + rev := "HEAD" if len(tags) > 0 { @@ -216,6 +238,33 @@ func (gen *Generator) getTags(query string) ([]*Tag, string, error) { return nil, "", err } + next := gen.config.Options.NextTag + if next != "" { + for _, tag := range tags { + if next == tag.Name { + return nil, "", fmt.Errorf("\"%s\" tag already exists", next) + } + } + + var previous *RelateTag + if len(tags) > 0 { + previous = &RelateTag{ + Name: tags[0].Name, + Subject: tags[0].Subject, + Date: tags[0].Date, + } + } + + // Assign the date with `readVersions()` + tags = append([]*Tag{ + &Tag{ + Name: next, + Subject: next, + Previous: previous, + }, + }, tags...) + } + if len(tags) == 0 { return nil, "", errors.New("git-tag does not exist") } diff --git a/chglog_test.go b/chglog_test.go index 6d329801..b233b45d 100644 --- a/chglog_test.go +++ b/chglog_test.go @@ -13,8 +13,9 @@ import ( ) var ( - cwd string - testRepoRoot = ".tmp" + cwd string + testRepoRoot = ".tmp" + internalTimeFormat = "2006-01-02 15:04:05" ) type commitFunc = func(date, subject, body string) @@ -48,7 +49,7 @@ func setup(dir string, setupRepo func(commitFunc, tagFunc, gitcmd.Client)) { if body != "" { msg += "\n\n" + body } - t, _ := time.Parse("2006-01-02 15:04:05", date) + t, _ := time.Parse(internalTimeFormat, date) d := t.Format("Mon Jan 2 15:04:05 2006 +0000") git.Exec("commit", "--allow-empty", "--date", d, "-m", msg) } @@ -298,3 +299,95 @@ Online breaking change message. [2.0.0-beta.0]: https://github.com/git-chglog/git-chglog/compare/1.1.0...2.0.0-beta.0 [1.1.0]: https://github.com/git-chglog/git-chglog/compare/1.0.0...1.1.0`, strings.TrimSpace(buf.String())) } + +func TestGeneratorWithNextTag(t *testing.T) { + assert := assert.New(t) + testName := "type_scope_subject" + + setup(testName, func(commit commitFunc, tag tagFunc, _ gitcmd.Client) { + commit("2018-01-01 00:00:00", "feat(core): version 1.0.0", "") + tag("1.0.0") + + commit("2018-02-01 00:00:00", "feat(core): version 2.0.0", "") + tag("2.0.0") + + commit("2018-03-01 00:00:00", "feat(core): version 3.0.0", "") + }) + + gen := NewGenerator(&Config{ + Bin: "git", + WorkingDir: filepath.Join(testRepoRoot, testName), + Template: filepath.Join(cwd, "testdata", testName+".md"), + Info: &Info{ + Title: "CHANGELOG Example", + RepositoryURL: "https://github.com/git-chglog/git-chglog", + }, + Options: &Options{ + NextTag: "3.0.0", + CommitFilters: map[string][]string{ + "Type": []string{ + "feat", + }, + }, + CommitSortBy: "Scope", + CommitGroupBy: "Type", + CommitGroupSortBy: "Title", + CommitGroupTitleMaps: map[string]string{ + "feat": "Features", + }, + HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$", + HeaderPatternMaps: []string{ + "Type", + "Scope", + "Subject", + }, + }, + }) + + buf := &bytes.Buffer{} + err := gen.Generate(buf, "") + + assert.Nil(err) + assert.Equal(` +## [Unreleased] + + + +## [3.0.0] - 2018-03-01 +### Features +- **core:** version 3.0.0 + + + +## [2.0.0] - 2018-02-01 +### Features +- **core:** version 2.0.0 + + + +## 1.0.0 - 2018-01-01 +### Features +- **core:** version 1.0.0 + + +[Unreleased]: https://github.com/git-chglog/git-chglog/compare/3.0.0...HEAD +[3.0.0]: https://github.com/git-chglog/git-chglog/compare/2.0.0...3.0.0 +[2.0.0]: https://github.com/git-chglog/git-chglog/compare/1.0.0...2.0.0`, strings.TrimSpace(buf.String())) + + buf = &bytes.Buffer{} + err = gen.Generate(buf, "3.0.0") + + assert.Nil(err) + assert.Equal(` +## [Unreleased] + + + +## [3.0.0] - 2018-03-01 +### Features +- **core:** version 3.0.0 + + +[Unreleased]: https://github.com/git-chglog/git-chglog/compare/3.0.0...HEAD +[3.0.0]: https://github.com/git-chglog/git-chglog/compare/2.0.0...3.0.0`, strings.TrimSpace(buf.String())) +} diff --git a/cmd/git-chglog/config.go b/cmd/git-chglog/config.go index a81340ee..cef822a2 100644 --- a/cmd/git-chglog/config.go +++ b/cmd/git-chglog/config.go @@ -257,6 +257,7 @@ func (config *Config) Convert(ctx *CLIContext) *chglog.Config { RepositoryURL: info.RepositoryURL, }, Options: &chglog.Options{ + NextTag: ctx.NextTag, CommitFilters: opts.Commits.Filters, CommitSortBy: opts.Commits.SortBy, CommitGroupBy: opts.CommitGroups.GroupBy, diff --git a/cmd/git-chglog/context.go b/cmd/git-chglog/context.go index c6a8740e..09c5cb59 100644 --- a/cmd/git-chglog/context.go +++ b/cmd/git-chglog/context.go @@ -15,6 +15,7 @@ type CLIContext struct { NoColor bool NoEmoji bool Query string + NextTag string } // InitContext ... diff --git a/cmd/git-chglog/main.go b/cmd/git-chglog/main.go index fe435507..fcbbdc2c 100644 --- a/cmd/git-chglog/main.go +++ b/cmd/git-chglog/main.go @@ -89,6 +89,11 @@ func main() { Usage: "output path and filename for the changelogs. If not specified, output to stdout", }, + cli.StringFlag{ + Name: "next-tag", + Usage: "treat unreleased commits as specified tags (EXPERIMENTAL)", + }, + // silent cli.BoolFlag{ Name: "silent", @@ -155,6 +160,7 @@ func main() { NoColor: c.Bool("no-color"), NoEmoji: c.Bool("no-emoji"), Query: c.Args().First(), + NextTag: c.String("next-tag"), }, fs, NewConfigLoader(),