From 1198e283de00a32746be4dca6ac9ce688d7bfb9e Mon Sep 17 00:00:00 2001 From: Chao Li Date: Thu, 4 Jul 2019 11:40:48 +0800 Subject: [PATCH] feat: Add --tag-filter-pattern flag. This flag specifies a regular expression and only matched tags will be included in change log. Closes #43 --- .gitignore | 5 ++ README.md | 30 +++++++---- chglog.go | 3 +- chglog_test.go | 61 +++++++++++++++++++++ cmd/git-chglog/config.go | 1 + cmd/git-chglog/context.go | 21 ++++---- cmd/git-chglog/main.go | 104 ++++++++++++++++++++---------------- cmd/git-chglog/main_test.go | 40 ++++++++++++++ tag_reader.go | 11 +++- tag_reader_test.go | 17 +++++- 10 files changed, 225 insertions(+), 68 deletions(-) create mode 100644 cmd/git-chglog/main_test.go diff --git a/.gitignore b/.gitignore index 6182e02..cfc9cb9 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ *.dll *.so *.dylib +git-chglog # Test binary, build with `go test -c` *.test @@ -31,6 +32,10 @@ Icon # Thumbnails ._* +# Intellij IDEA +*.iml +.idea + # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd diff --git a/README.md b/README.md index d948bcc..2c69f0b 100644 --- a/README.md +++ b/README.md @@ -166,15 +166,16 @@ USAGE: 4. - Commit contained in . OPTIONS: - --init generate the git-chglog configuration file in interactive - --config value, -c value specifies a different configuration file to pick up (default: ".chglog/config.yml") - --output value, -o value output path and filename for the changelogs. If not specified, output to stdout - --next-tag value treat unreleased commits as specified tags (EXPERIMENTAL) - --silent disable stdout output - --no-color disable color output [$NO_COLOR] - --no-emoji disable emoji output [$NO_EMOJI] - --help, -h show help - --version, -v print the version + --init generate the git-chglog configuration file in interactive + --config value, -c value specifies a different configuration file to pick up (default: ".chglog/config.yml") + --output value, -o value output path and filename for the changelogs. If not specified, output to stdout + --next-tag value treat unreleased commits as specified tags (EXPERIMENTAL) + --silent disable stdout output + --no-color disable color output [$NO_COLOR] + --no-emoji disable emoji output [$NO_EMOJI] + --tag-filter-pattern value, -p value regular expression of tag filter. Is specified, only matched tags will be picked + --help, -h show help + --version, -v print the version EXAMPLE: @@ -512,7 +513,16 @@ See godoc [RenderData][doc-render-data] for available variables. This is a step that is necessary for project operation in many cases. - +
+ Can I generated CHANGELOG based on certain tags? + + Yes, it can be solved by use the `--tag-filter-pattern` flag. + + For example, the following command will only include tags starting with "v": + ```bash + $ git-chglog --tag-filter-pattern '^v' + ``` +
## TODO diff --git a/chglog.go b/chglog.go index fbe86fa..a45f201 100644 --- a/chglog.go +++ b/chglog.go @@ -18,6 +18,7 @@ import ( type Options struct { Processor Processor NextTag string // Treat unreleased commits as specified tags (EXPERIMENTAL) + TagFilterPattern string // Filter tag by regexp 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`) @@ -108,7 +109,7 @@ func NewGenerator(config *Config) *Generator { return &Generator{ client: client, config: config, - tagReader: newTagReader(client), + tagReader: newTagReader(client, config.Options.TagFilterPattern), tagSelector: newTagSelector(), commitParser: newCommitParser(client, config), commitExtractor: newCommitExtractor(config.Options), diff --git a/chglog_test.go b/chglog_test.go index b233b45..18cf487 100644 --- a/chglog_test.go +++ b/chglog_test.go @@ -391,3 +391,64 @@ func TestGeneratorWithNextTag(t *testing.T) { [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())) } + +func TestGeneratorWithTagFiler(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 dev-1.0.0", "") + tag("dev-1.0.0") + + commit("2018-02-01 00:00:00", "feat(core): version v1.0.0", "") + tag("v1.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{ + TagFilterPattern: "^v", + 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] + + + +## v1.0.0 - 2018-02-01 +### Features +- **core:** version v1.0.0 +- **core:** version dev-1.0.0 + + +[Unreleased]: https://github.com/git-chglog/git-chglog/compare/v1.0.0...HEAD`, strings.TrimSpace(buf.String())) + +} diff --git a/cmd/git-chglog/config.go b/cmd/git-chglog/config.go index cef822a..6f13b72 100644 --- a/cmd/git-chglog/config.go +++ b/cmd/git-chglog/config.go @@ -258,6 +258,7 @@ func (config *Config) Convert(ctx *CLIContext) *chglog.Config { }, Options: &chglog.Options{ NextTag: ctx.NextTag, + TagFilterPattern: ctx.TagFilterPattern, 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 09c5cb5..79b497e 100644 --- a/cmd/git-chglog/context.go +++ b/cmd/git-chglog/context.go @@ -6,16 +6,17 @@ import ( // CLIContext ... type CLIContext struct { - WorkingDir string - Stdout io.Writer - Stderr io.Writer - ConfigPath string - OutputPath string - Silent bool - NoColor bool - NoEmoji bool - Query string - NextTag string + WorkingDir string + Stdout io.Writer + Stderr io.Writer + ConfigPath string + OutputPath string + Silent bool + NoColor bool + NoEmoji bool + Query string + NextTag string + TagFilterPattern string } // InitContext ... diff --git a/cmd/git-chglog/main.go b/cmd/git-chglog/main.go index ac26bf6..d4582ba 100644 --- a/cmd/git-chglog/main.go +++ b/cmd/git-chglog/main.go @@ -11,7 +11,7 @@ import ( "github.com/urfave/cli" ) -func main() { +func CreateApp(actionFunc cli.ActionFunc) *cli.App { ttl := color.New(color.FgYellow).SprintFunc() cli.AppHelpTemplate = fmt.Sprintf(` @@ -114,63 +114,77 @@ func main() { EnvVar: "NO_EMOJI", }, + // tag-filter-pattern + cli.StringFlag{ + Name: "tag-filter-pattern, p", + Usage: "Regular expression of tag filter. Is specified, only matched tags will be picked", + }, + // help & version cli.HelpFlag, cli.VersionFlag, } - app.Action = func(c *cli.Context) error { - wd, err := os.Getwd() - if err != nil { - fmt.Fprintln(os.Stderr, "failed to get working directory", err) - os.Exit(ExitCodeError) - } - - // initializer - if c.Bool("init") { - initializer := NewInitializer( - &InitContext{ - WorkingDir: wd, - Stdout: colorable.NewColorableStdout(), - Stderr: colorable.NewColorableStderr(), - }, - fs, - NewQuestioner( - gitcmd.New(&gitcmd.Config{ - Bin: "git", - }), - fs, - ), - NewConfigBuilder(), - templateBuilderFactory, - ) - - os.Exit(initializer.Run()) - } - - // chglog - chglogCLI := NewCLI( - &CLIContext{ + app.Action = actionFunc + + return app +} + +func AppAction(c *cli.Context) error { + wd, err := os.Getwd() + if err != nil { + fmt.Fprintln(os.Stderr, "failed to get working directory", err) + os.Exit(ExitCodeError) + } + + // initializer + if c.Bool("init") { + initializer := NewInitializer( + &InitContext{ WorkingDir: wd, Stdout: colorable.NewColorableStdout(), Stderr: colorable.NewColorableStderr(), - ConfigPath: c.String("config"), - OutputPath: c.String("output"), - Silent: c.Bool("silent"), - NoColor: c.Bool("no-color"), - NoEmoji: c.Bool("no-emoji"), - Query: c.Args().First(), - NextTag: c.String("next-tag"), }, fs, - NewConfigLoader(), - NewGenerator(), + NewQuestioner( + gitcmd.New(&gitcmd.Config{ + Bin: "git", + }), + fs, + ), + NewConfigBuilder(), + templateBuilderFactory, ) - os.Exit(chglogCLI.Run()) - - return nil + os.Exit(initializer.Run()) } + // chglog + chglogCLI := NewCLI( + &CLIContext{ + WorkingDir: wd, + Stdout: colorable.NewColorableStdout(), + Stderr: colorable.NewColorableStderr(), + ConfigPath: c.String("config"), + OutputPath: c.String("output"), + Silent: c.Bool("silent"), + NoColor: c.Bool("no-color"), + NoEmoji: c.Bool("no-emoji"), + Query: c.Args().First(), + NextTag: c.String("next-tag"), + TagFilterPattern: c.String("tag-filter-pattern"), + }, + fs, + NewConfigLoader(), + NewGenerator(), + ) + + os.Exit(chglogCLI.Run()) + + return nil +} + +func main() { + app := CreateApp(AppAction) app.Run(os.Args) } diff --git a/cmd/git-chglog/main_test.go b/cmd/git-chglog/main_test.go new file mode 100644 index 0000000..a07284f --- /dev/null +++ b/cmd/git-chglog/main_test.go @@ -0,0 +1,40 @@ +package main + +import ( + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" + "testing" +) + +var gAssert *assert.Assertions + +func mock_app_action(c *cli.Context) error { + assert := gAssert + assert.Equal("c.yml", c.String("config")) + assert.Equal("^v", c.String("tag-filter-pattern")) + assert.Equal("o.md", c.String("output")) + assert.Equal("v5", c.String("next-tag")) + assert.True(c.Bool("silent")) + assert.True(c.Bool("no-color")) + assert.True(c.Bool("no-emoji")) + return nil +} + +func TestCreateApp(t *testing.T) { + assert := assert.New(t) + assert.True(true) + gAssert = assert + + app := CreateApp(mock_app_action) + args := []string { + "git-chglog", + "--silent", + "--no-color", + "--no-emoji", + "--config", "c.yml", + "--output", "o.md", + "--next-tag", "v5", + "--tag-filter-pattern", "^v", + } + app.Run(args) +} diff --git a/tag_reader.go b/tag_reader.go index ddfa44c..a1d00d8 100644 --- a/tag_reader.go +++ b/tag_reader.go @@ -2,6 +2,7 @@ package chglog import ( "fmt" + "regexp" "sort" "strings" "time" @@ -13,12 +14,14 @@ type tagReader struct { client gitcmd.Client format string separator string + reFilter *regexp.Regexp } -func newTagReader(client gitcmd.Client) *tagReader { +func newTagReader(client gitcmd.Client, filterPattern string) *tagReader { return &tagReader{ client: client, separator: "@@__CHGLOG__@@", + reFilter: regexp.MustCompile(filterPattern), } } @@ -56,6 +59,12 @@ func (r *tagReader) ReadAll() ([]*Tag, error) { date = t } + if r.reFilter != nil { + if !r.reFilter.MatchString(name) { + continue + } + } + tags = append(tags, &Tag{ Name: name, Subject: subject, diff --git a/tag_reader_test.go b/tag_reader_test.go index 3d292aa..5262117 100644 --- a/tag_reader_test.go +++ b/tag_reader_test.go @@ -28,7 +28,7 @@ func TestTagReader(t *testing.T) { }, } - actual, err := newTagReader(client).ReadAll() + actual, err := newTagReader(client, "").ReadAll() assert.Nil(err) assert.Equal( @@ -103,4 +103,19 @@ func TestTagReader(t *testing.T) { }, actual, ) + + actual_filtered, err_filtered := newTagReader(client, "^v").ReadAll() + assert.Nil(err_filtered) + assert.Equal( + []*Tag{ + &Tag{ + Name: "v2.0.4-beta.1", + Subject: "Release v2.0.4-beta.1", + Date: time.Date(2018, 2, 1, 0, 0, 0, 0, time.UTC), + Next: nil, + Previous: nil, + }, + }, + actual_filtered, + ) }