feat: Add --tag-filter-pattern flag.

This flag specifies a regular expression and only matched tags will
be included in change log.

Closes #43
This commit is contained in:
Chao Li 2019-07-04 11:40:48 +08:00
parent 63a4e63702
commit 1198e283de
10 changed files with 222 additions and 65 deletions

5
.gitignore vendored
View file

@ -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

View file

@ -166,15 +166,16 @@ USAGE:
4. <name> - Commit contained in <name>.
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.
</details>
<details>
<summary>Can I generated CHANGELOG based on certain tags?</summary>
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'
```
</details>
## TODO

View file

@ -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),

View file

@ -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(`<a name="unreleased"></a>
## [Unreleased]
<a name="v1.0.0"></a>
## 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()))
}

View file

@ -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,

View file

@ -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 ...

View file

@ -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)
}
app.Action = actionFunc
// 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,
)
return app
}
os.Exit(initializer.Run())
}
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)
}
// chglog
chglogCLI := NewCLI(
&CLIContext{
// 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)
}

View file

@ -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)
}

View file

@ -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,

View file

@ -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,
)
}