From ae3382b7c8175be9fec858e93058c10d89b1d8ad Mon Sep 17 00:00:00 2001 From: Derek Smith Date: Tue, 16 Mar 2021 21:24:36 -0500 Subject: [PATCH] chore(ci): add golangci-lint action and apply linting changes (#120) BREAKING CHANGE: `JiraIssueId` has been renamed to `JiraIssueID`. This impacts the value for `pattern_maps` in `config.yml`. * chore(ci): add golangci-lint action * chore(lint): address errcheck lint failures * chore(lint): address misspell lint failures * chore(lint): address gocritic lint failures * chore(lint): address golint lint failures * chore(lint): address structcheck lint failures * chore(lint): address gosimple lint failures * chore(lint): address gofmt lint failures * chore(ci): port to official golangci-lint github action * Update golangci configuration for better coverage Signed-off-by: Khosrow Moossavi * fix: file is not goimports-ed Signed-off-by: Khosrow Moossavi * fix: golint and exported functions comments Signed-off-by: Khosrow Moossavi * chore(lint): address gosec G304 warning * chore(lint): address uparam warnings * chore(lint): address scopelint lint failures * fix: cyclomatic complexity Signed-off-by: Khosrow Moossavi * chore(lint): address prealloc warning, noting that we are warning for now * chore(lint): address govet and errorlint failures * chore: clean up defer logic when checking errors Co-authored-by: Khosrow Moossavi --- .github/workflows/lint.yml | 18 +++ .golangci.yml | 155 ++++++++++++++++++++++ Makefile | 4 + README.md | 4 +- chglog.go | 39 +++--- chglog_test.go | 38 +++--- cmd/git-chglog/cli.go | 1 + cmd/git-chglog/cli_test.go | 7 +- cmd/git-chglog/config.go | 8 +- cmd/git-chglog/config_loader.go | 6 +- cmd/git-chglog/context.go | 4 +- cmd/git-chglog/custom_template_builder.go | 4 +- cmd/git-chglog/initializer.go | 5 +- cmd/git-chglog/main.go | 16 ++- cmd/git-chglog/main_test.go | 13 +- cmd/git-chglog/processor_factory_test.go | 3 +- cmd/git-chglog/questioner.go | 11 +- cmd/git-chglog/utils.go | 2 +- cmd/git-chglog/variables.go | 2 +- commit_extractor.go | 15 ++- commit_extractor_test.go | 24 ++-- commit_filter.go | 6 +- commit_filter_test.go | 10 +- commit_parser.go | 12 +- commit_parser_test.go | 34 ++--- example_test.go | 2 +- fields.go | 14 +- jira.go | 6 +- jira_test.go | 7 +- logger.go | 4 +- logger_test.go | 4 +- processor.go | 12 +- processor_test.go | 12 +- tag_reader.go | 3 +- tag_reader_test.go | 18 +-- tag_selector.go | 13 +- tag_selector_test.go | 32 ++--- 37 files changed, 391 insertions(+), 177 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .golangci.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..49f5fde0 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,18 @@ +name: lint + +on: + pull_request: + types: ['opened', 'synchronize'] + push: + branches: + - master +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: v1.38 diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..c409770d --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,155 @@ +# https://golangci-lint.run/usage/configuration/ +run: + timeout: 10m + deadline: 5m + + tests: true + +output: + format: tab + +linters-settings: + govet: + # report about shadowed variables + check-shadowing: true + + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.8 + + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + + goimports: + # put imports beginning with prefix after 3rd-party packages; + # it's a comma-separated list of prefixes + local-prefixes: github.com/git-chglog/git-chglog + + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 10 + + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + + dupl: + # tokens count to trigger issue, 150 by default + threshold: 100 + + goconst: + # minimal length of string constant, 3 by default + min-len: 3 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 5 + + lll: + # tab width in spaces. Default to 1. + tab-width: 1 + + unused: + # treat code as a program (not a library) and report unused exported identifiers; default is false. + # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find funcs usages. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 30 + + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + + gocritic: + # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks. + # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". + enabled-tags: + - performance + + settings: # settings passed to gocritic + captLocal: # must be valid enabled check name + paramsOnly: true + rangeValCopy: + sizeThreshold: 32 + + misspell: + locale: US + +linters: + enable: + - megacheck + - govet + - gocyclo + - gocritic + - interfacer + - goconst + - goimports + - gofmt # We enable this as well as goimports for its simplify mode. + - prealloc + - golint + - unconvert + - misspell + - nakedret + - dupl + - depguard + + presets: + - bugs + - unused + fast: false + +issues: + # Excluding configuration per-path and per-linter + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test(ing)?\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + - scopelint + - unparam + + # These are performance optimisations rather than style issues per se. + # They warn when function arguments or range values copy a lot of memory + # rather than using a pointer. + - text: "(hugeParam|rangeValCopy):" + linters: + - gocritic + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 diff --git a/Makefile b/Makefile index 1f27d92a..fa11f871 100644 --- a/Makefile +++ b/Makefile @@ -20,3 +20,7 @@ install: .PHONY: changelog changelog: @git-chglog --next-tag $(tag) $(tag) + +.PHONY: lint +lint: + @golangci-lint run diff --git a/README.md b/README.md index 231b038b..9e4fbf00 100644 --- a/README.md +++ b/README.md @@ -522,7 +522,7 @@ The following is a sample pattern: pattern: "^(?:(\\w*)|(?:\\[(.*)\\])?)\\:\\s(.*)$" pattern_maps: - Type - - JiraIssueId + - JiraIssueID - Subject ``` @@ -646,6 +646,8 @@ We alway welcome your contributions :clap: 1. Fork (https://github.com/git-chglog/git-chglog) :tada: 1. Create a feature branch :coffee: 1. Run test suite with the `$ make test` command and confirm that it passes :zap: +1. Run linters with the `$ make lint` command and confirm it passes :broom: + - The project uses [golangci-lint](https://golangci-lint.run/usage/install/#local-installation) 1. Commit your changes :memo: 1. Rebase your local changes against the `master` branch :bulb: 1. Create new Pull Request :love_letter: diff --git a/chglog.go b/chglog.go index 5a3427d3..f71f8095 100644 --- a/chglog.go +++ b/chglog.go @@ -5,13 +5,14 @@ import ( "errors" "fmt" "io" + "log" "os" "path/filepath" "strings" "text/template" "time" - gitcmd "github.com/tsuyoshiwada/go-gitcmd" + "github.com/tsuyoshiwada/go-gitcmd" ) // Options is an option used to process commits @@ -37,7 +38,7 @@ type Options struct { NoteKeywords []string // Keyword list to find `Note`. A semicolon is a separator, like `:` (e.g. `BREAKING CHANGE`) JiraUsername string JiraToken string - JiraUrl string + JiraURL string JiraTypeMaps map[string]string JiraIssueDescriptionPattern string Paths []string // Path filter @@ -138,7 +139,11 @@ func (gen *Generator) Generate(w io.Writer, query string) error { if err != nil { return err } - defer back() + defer func() { + if err = back(); err != nil { + log.Fatal(err) + } + }() tags, first, err := gen.getTags(query) if err != nil { @@ -269,7 +274,7 @@ func (gen *Generator) getTags(query string) ([]*Tag, string, error) { // Assign the date with `readVersions()` tags = append([]*Tag{ - &Tag{ + { Name: next, Subject: next, Previous: previous, @@ -318,30 +323,18 @@ func (gen *Generator) render(w io.Writer, unreleased *Unreleased, versions []*Ve "datetime": func(layout string, input time.Time) string { return input.Format(layout) }, - // check whether substs is withing s - "contains": func(s, substr string) bool { - return strings.Contains(s, substr) - }, + // check whether substr is within s + "contains": strings.Contains, // check whether s begins with prefix - "hasPrefix": func(s, prefix string) bool { - return strings.HasPrefix(s, prefix) - }, + "hasPrefix": strings.HasPrefix, // check whether s ends with suffix - "hasSuffix": func(s, suffix string) bool { - return strings.HasSuffix(s, suffix) - }, + "hasSuffix": strings.HasSuffix, // replace the first n instances of old with new - "replace": func(s, old, new string, n int) string { - return strings.Replace(s, old, new, n) - }, + "replace": strings.Replace, // lower case a string - "lower": func(s string) string { - return strings.ToLower(s) - }, + "lower": strings.ToLower, // upper case a string - "upper": func(s string) string { - return strings.ToUpper(s) - }, + "upper": strings.ToUpper, // upper case the first character of a string "upperFirst": func(s string) string { if len(s) > 0 { diff --git a/chglog_test.go b/chglog_test.go index 8e2a3e9e..47082a36 100644 --- a/chglog_test.go +++ b/chglog_test.go @@ -32,17 +32,17 @@ func TestMain(m *testing.M) { func setup(dir string, setupRepo func(commitFunc, tagFunc, gitcmd.Client)) { testDir := filepath.Join(cwd, testRepoRoot, dir) - os.RemoveAll(testDir) - os.MkdirAll(testDir, os.ModePerm) - os.Chdir(testDir) + _ = os.RemoveAll(testDir) + _ = os.MkdirAll(testDir, os.ModePerm) + _ = os.Chdir(testDir) loc, _ := time.LoadLocation("UTC") time.Local = loc git := gitcmd.New(nil) - git.Exec("init") - git.Exec("config", "user.name", "test_user") - git.Exec("config", "user.email", "test@example.com") + _, _ = git.Exec("init") + _, _ = git.Exec("config", "user.name", "test_user") + _, _ = git.Exec("config", "user.email", "test@example.com") var commit = func(date, subject, body string) { msg := subject @@ -51,21 +51,21 @@ func setup(dir string, setupRepo func(commitFunc, tagFunc, gitcmd.Client)) { } 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) + _, _ = git.Exec("commit", "--allow-empty", "--date", d, "-m", msg) } var tag = func(name string) { - git.Exec("tag", name) + _, _ = git.Exec("tag", name) } setupRepo(commit, tag, git) - os.Chdir(cwd) + _ = os.Chdir(cwd) } func cleanup() { - os.Chdir(cwd) - os.RemoveAll(filepath.Join(cwd, testRepoRoot)) + _ = os.Chdir(cwd) + _ = os.RemoveAll(filepath.Join(cwd, testRepoRoot)) } func TestGeneratorNotFoundTags(t *testing.T) { @@ -193,7 +193,7 @@ func TestGeneratorWithTypeScopeSubject(t *testing.T) { tag("1.1.0") commit("2018-01-03 00:00:00", "feat(context): Online breaking change", "BREAKING CHANGE: Online breaking change message.") - commit("2018-01-03 00:01:00", "feat(router): Muliple breaking change", `This is body, + commit("2018-01-03 00:01:00", "feat(router): Multiple breaking change", `This is body, BREAKING CHANGE: Multiple @@ -216,7 +216,7 @@ change message.`) }, Options: &Options{ CommitFilters: map[string][]string{ - "Type": []string{ + "Type": { "feat", "fix", }, @@ -256,7 +256,7 @@ change message.`) buf := &bytes.Buffer{} err := gen.Generate(buf, "") - output := strings.Replace(strings.TrimSpace(buf.String()), "\r\n", "\n", -1) + output := strings.ReplaceAll(strings.TrimSpace(buf.String()), "\r\n", "\n") assert.Nil(err) assert.Equal(` @@ -270,7 +270,7 @@ change message.`) ## [2.0.0-beta.0] - 2018-01-03 ### Features - **context:** Online breaking change -- **router:** Muliple breaking change +- **router:** Multiple breaking change ### BREAKING CHANGE @@ -331,7 +331,7 @@ func TestGeneratorWithNextTag(t *testing.T) { Options: &Options{ NextTag: "3.0.0", CommitFilters: map[string][]string{ - "Type": []string{ + "Type": { "feat", }, }, @@ -352,7 +352,7 @@ func TestGeneratorWithNextTag(t *testing.T) { buf := &bytes.Buffer{} err := gen.Generate(buf, "") - output := strings.Replace(strings.TrimSpace(buf.String()), "\r\n", "\n", -1) + output := strings.ReplaceAll(strings.TrimSpace(buf.String()), "\r\n", "\n") assert.Nil(err) assert.Equal(` @@ -383,7 +383,7 @@ func TestGeneratorWithNextTag(t *testing.T) { buf = &bytes.Buffer{} err = gen.Generate(buf, "3.0.0") - output = strings.Replace(strings.TrimSpace(buf.String()), "\r\n", "\n", -1) + output = strings.ReplaceAll(strings.TrimSpace(buf.String()), "\r\n", "\n") assert.Nil(err) assert.Equal(` @@ -424,7 +424,7 @@ func TestGeneratorWithTagFiler(t *testing.T) { Options: &Options{ TagFilterPattern: "^v", CommitFilters: map[string][]string{ - "Type": []string{ + "Type": { "feat", }, }, diff --git a/cmd/git-chglog/cli.go b/cmd/git-chglog/cli.go index 2f2b4c1b..4c5cd813 100644 --- a/cmd/git-chglog/cli.go +++ b/cmd/git-chglog/cli.go @@ -7,6 +7,7 @@ import ( "time" "github.com/fatih/color" + chglog "github.com/git-chglog/git-chglog" ) diff --git a/cmd/git-chglog/cli_test.go b/cmd/git-chglog/cli_test.go index ba70c172..e1b4e079 100644 --- a/cmd/git-chglog/cli_test.go +++ b/cmd/git-chglog/cli_test.go @@ -8,8 +8,9 @@ import ( "regexp" "testing" - chglog "github.com/git-chglog/git-chglog" "github.com/stretchr/testify/assert" + + chglog "github.com/git-chglog/git-chglog" ) func TestCLIForStdout(t *testing.T) { @@ -36,7 +37,7 @@ func TestCLIForStdout(t *testing.T) { if config.Bin != "/custom/bin/git" { return errors.New("") } - w.Write([]byte("success!!")) + _, _ = w.Write([]byte("success!!")) return nil }, } @@ -104,7 +105,7 @@ func TestCLIForFile(t *testing.T) { if filepath.ToSlash(config.Bin) != "/custom/bin/git" { return errors.New("") } - w.Write([]byte("success!!")) + _, _ = w.Write([]byte("success!!")) return nil }, } diff --git a/cmd/git-chglog/config.go b/cmd/git-chglog/config.go index 2410b68c..b3992b86 100644 --- a/cmd/git-chglog/config.go +++ b/cmd/git-chglog/config.go @@ -4,8 +4,9 @@ import ( "path/filepath" "strings" - chglog "github.com/git-chglog/git-chglog" "github.com/imdario/mergo" + + chglog "github.com/git-chglog/git-chglog" ) // Info ... @@ -49,6 +50,7 @@ type NoteOptions struct { Keywords []string `yaml:"keywords"` } +// JiraClientInfoOptions ... type JiraClientInfoOptions struct { Username string `yaml:"username"` Token string `yaml:"token"` @@ -286,7 +288,7 @@ func (config *Config) Convert(ctx *CLIContext) *chglog.Config { Template: orValue(ctx.Template, config.Template), Info: &chglog.Info{ Title: info.Title, - RepositoryURL: orValue(ctx.RepositoryUrl, info.RepositoryURL), + RepositoryURL: orValue(ctx.RepositoryURL, info.RepositoryURL), }, Options: &chglog.Options{ NextTag: ctx.NextTag, @@ -309,7 +311,7 @@ func (config *Config) Convert(ctx *CLIContext) *chglog.Config { NoteKeywords: opts.Notes.Keywords, JiraUsername: orValue(ctx.JiraUsername, opts.Jira.ClintInfo.Username), JiraToken: orValue(ctx.JiraToken, opts.Jira.ClintInfo.Token), - JiraUrl: orValue(ctx.JiraUrl, opts.Jira.ClintInfo.URL), + JiraURL: orValue(ctx.JiraURL, opts.Jira.ClintInfo.URL), JiraTypeMaps: opts.Jira.Issue.TypeMaps, JiraIssueDescriptionPattern: opts.Jira.Issue.DescriptionPattern, }, diff --git a/cmd/git-chglog/config_loader.go b/cmd/git-chglog/config_loader.go index c20d2950..d49eb779 100644 --- a/cmd/git-chglog/config_loader.go +++ b/cmd/git-chglog/config_loader.go @@ -2,8 +2,9 @@ package main import ( "io/ioutil" + "path/filepath" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" ) // ConfigLoader ... @@ -20,7 +21,8 @@ func NewConfigLoader() ConfigLoader { } func (loader *configLoaderImpl) Load(path string) (*Config, error) { - bytes, err := ioutil.ReadFile(path) + fp := filepath.Clean(path) + bytes, err := ioutil.ReadFile(fp) if err != nil { return nil, err } diff --git a/cmd/git-chglog/context.go b/cmd/git-chglog/context.go index a086cf15..65dea9db 100644 --- a/cmd/git-chglog/context.go +++ b/cmd/git-chglog/context.go @@ -11,7 +11,7 @@ type CLIContext struct { Stderr io.Writer ConfigPath string Template string - RepositoryUrl string + RepositoryURL string OutputPath string Silent bool NoColor bool @@ -22,7 +22,7 @@ type CLIContext struct { TagFilterPattern string JiraUsername string JiraToken string - JiraUrl string + JiraURL string Paths []string } diff --git a/cmd/git-chglog/custom_template_builder.go b/cmd/git-chglog/custom_template_builder.go index b41ec392..84606c82 100644 --- a/cmd/git-chglog/custom_template_builder.go +++ b/cmd/git-chglog/custom_template_builder.go @@ -18,7 +18,7 @@ func (t *customTemplateBuilderImpl) Build(ans *Answer) (string, error) { tpl += t.versionHeader(ans.Style, ans.Template) // commits - tpl += t.commits(ans.Template, ans.CommitMessageFormat) + tpl += t.commits(ans.CommitMessageFormat) // revert if ans.IncludeReverts { @@ -75,7 +75,7 @@ func (*customTemplateBuilderImpl) versionHeader(style, template string) string { return tpl } -func (*customTemplateBuilderImpl) commits(template, format string) string { +func (*customTemplateBuilderImpl) commits(format string) string { var ( header string body string diff --git a/cmd/git-chglog/initializer.go b/cmd/git-chglog/initializer.go index c0a8489a..7b404b74 100644 --- a/cmd/git-chglog/initializer.go +++ b/cmd/git-chglog/initializer.go @@ -2,17 +2,16 @@ package main import ( "fmt" - "github.com/git-chglog/git-chglog" "path/filepath" "github.com/fatih/color" - gitcmd "github.com/tsuyoshiwada/go-gitcmd" + + chglog "github.com/git-chglog/git-chglog" ) // Initializer ... type Initializer struct { ctx *InitContext - client gitcmd.Client fs FileSystem logger *chglog.Logger questioner Questioner diff --git a/cmd/git-chglog/main.go b/cmd/git-chglog/main.go index e3f862f6..562a0a78 100644 --- a/cmd/git-chglog/main.go +++ b/cmd/git-chglog/main.go @@ -3,14 +3,17 @@ package main import ( "fmt" "io" + "log" "os" "github.com/fatih/color" "github.com/mattn/go-colorable" - gitcmd "github.com/tsuyoshiwada/go-gitcmd" + "github.com/tsuyoshiwada/go-gitcmd" "github.com/urfave/cli/v2" ) +// CreateApp creates and initializes CLI application +// with description, flags, version, etc. func CreateApp(actionFunc cli.ActionFunc) *cli.App { ttl := color.New(color.FgYellow).SprintFunc() @@ -182,6 +185,8 @@ func CreateApp(actionFunc cli.ActionFunc) *cli.App { return app } +// AppAction is a callback function to create initializer +// and CLIContext and ultimately run the application. func AppAction(c *cli.Context) error { wd, err := os.Getwd() if err != nil { @@ -219,7 +224,7 @@ func AppAction(c *cli.Context) error { Stderr: colorable.NewColorableStderr(), ConfigPath: c.String("config"), Template: c.String("template"), - RepositoryUrl: c.String("repository-url"), + RepositoryURL: c.String("repository-url"), OutputPath: c.String("output"), Silent: c.Bool("silent"), NoColor: c.Bool("no-color"), @@ -230,7 +235,7 @@ func AppAction(c *cli.Context) error { TagFilterPattern: c.String("tag-filter-pattern"), JiraUsername: c.String("jira-username"), JiraToken: c.String("jira-token"), - JiraUrl: c.String("jira-url"), + JiraURL: c.String("jira-url"), Paths: c.StringSlice("path"), }, fs, @@ -245,5 +250,8 @@ func AppAction(c *cli.Context) error { func main() { app := CreateApp(AppAction) - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } diff --git a/cmd/git-chglog/main_test.go b/cmd/git-chglog/main_test.go index b7f4e396..46f762c3 100644 --- a/cmd/git-chglog/main_test.go +++ b/cmd/git-chglog/main_test.go @@ -1,14 +1,16 @@ package main import ( + "log" + "testing" + "github.com/stretchr/testify/assert" "github.com/urfave/cli/v2" - "testing" ) var gAssert *assert.Assertions -func mock_app_action(c *cli.Context) error { +func mockAppAction(c *cli.Context) error { assert := gAssert assert.Equal("c.yml", c.String("config")) assert.Equal("^v", c.String("tag-filter-pattern")) @@ -25,7 +27,7 @@ func TestCreateApp(t *testing.T) { assert.True(true) gAssert = assert - app := CreateApp(mock_app_action) + app := CreateApp(mockAppAction) args := []string{ "git-chglog", "--silent", @@ -36,5 +38,8 @@ func TestCreateApp(t *testing.T) { "--next-tag", "v5", "--tag-filter-pattern", "^v", } - app.Run(args) + err := app.Run(args) + if err != nil { + log.Fatal(err) + } } diff --git a/cmd/git-chglog/processor_factory_test.go b/cmd/git-chglog/processor_factory_test.go index f9d5c5bc..b9fca58f 100644 --- a/cmd/git-chglog/processor_factory_test.go +++ b/cmd/git-chglog/processor_factory_test.go @@ -3,8 +3,9 @@ package main import ( "testing" - chglog "github.com/git-chglog/git-chglog" "github.com/stretchr/testify/assert" + + chglog "github.com/git-chglog/git-chglog" ) func TestProcessorFactory(t *testing.T) { diff --git a/cmd/git-chglog/questioner.go b/cmd/git-chglog/questioner.go index 99ba5b23..5b801487 100644 --- a/cmd/git-chglog/questioner.go +++ b/cmd/git-chglog/questioner.go @@ -6,8 +6,8 @@ import ( "path/filepath" "strings" - gitcmd "github.com/tsuyoshiwada/go-gitcmd" - survey "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2" + "github.com/tsuyoshiwada/go-gitcmd" ) // Answer ... @@ -52,11 +52,12 @@ func (q *questionerImpl) Ask() (*Answer, error) { t := q.fs.Exists(tpl) msg := "" - if c && t { + switch { + case c && t: msg = fmt.Sprintf("\"%s\" and \"%s\" already exists. Do you want to overwrite?", config, tpl) - } else if c { + case c: msg = fmt.Sprintf("\"%s\" already exists. Do you want to overwrite?", config) - } else if t { + case t: msg = fmt.Sprintf("\"%s\" already exists. Do you want to overwrite?", tpl) } diff --git a/cmd/git-chglog/utils.go b/cmd/git-chglog/utils.go index 7ff14b0a..1d961499 100644 --- a/cmd/git-chglog/utils.go +++ b/cmd/git-chglog/utils.go @@ -7,7 +7,7 @@ import ( "strings" ) -var reSSH = regexp.MustCompile("^\\w+@([\\w\\.\\-]+):([\\w\\.\\-]+)\\/([\\w\\.\\-]+)$") +var reSSH = regexp.MustCompile(`^\w+@([\w\.\-]+):([\w\.\-]+)\/([\w\.\-]+)$`) func remoteOriginURLToHTTP(rawurl string) string { if rawurl == "" { diff --git a/cmd/git-chglog/variables.go b/cmd/git-chglog/variables.go index 60a7cbc9..59eeeb84 100644 --- a/cmd/git-chglog/variables.go +++ b/cmd/git-chglog/variables.go @@ -74,7 +74,7 @@ func (f *CommitMessageFormat) PatternMapString() string { return fmt.Sprintf("\n%s", strings.Join(arr, "\n")) } -// FilterTypeString ... +// FilterTypesString ... func (f *CommitMessageFormat) FilterTypesString() string { if len(f.typeSamples) == 0 { return " []" diff --git a/commit_extractor.go b/commit_extractor.go index ea1b2996..c5921ca7 100644 --- a/commit_extractor.go +++ b/commit_extractor.go @@ -127,7 +127,11 @@ func (e *commitExtractor) commitGroupTitle(commit *Commit) (string, string) { return raw, ttl } -func (e *commitExtractor) sortCommitGroups(groups []*CommitGroup) { +func (e *commitExtractor) sortCommitGroups(groups []*CommitGroup) { //nolint:gocyclo + // NOTE(khos2ow): this function is over our cyclomatic complexity goal. + // Be wary when adding branches, and look for functionality that could + // be reasonably moved into an injected dependency. + order := make(map[string]int) if e.opts.CommitGroupSortBy == "Custom" { for i, t := range e.opts.CommitGroupTitleOrder { @@ -136,6 +140,9 @@ func (e *commitExtractor) sortCommitGroups(groups []*CommitGroup) { } // groups + // TODO(khos2ow): move the inline sort function to + // conceret implementation of sort.Interface in order + // to reduce cyclomatic complaxity. sort.Slice(groups, func(i, j int) bool { if e.opts.CommitGroupSortBy == "Custom" { return order[groups[i].RawTitle] < order[groups[j].RawTitle] @@ -165,6 +172,11 @@ func (e *commitExtractor) sortCommitGroups(groups []*CommitGroup) { // commits for _, group := range groups { + group := group // pin group to avoid potential bugs with passing group to lower functions + + // TODO(khos2ow): move the inline sort function to + // conceret implementation of sort.Interface in order + // to reduce cyclomatic complaxity. sort.Slice(group.Commits, func(i, j int) bool { var ( a, b interface{} @@ -198,6 +210,7 @@ func (e *commitExtractor) sortNoteGroups(groups []*NoteGroup) { // notes for _, group := range groups { + group := group // pin group to avoid potential bugs with passing group to lower functions sort.Slice(group.Notes, func(i, j int) bool { return strings.ToLower(group.Notes[i].Title) < strings.ToLower(group.Notes[j].Title) }) diff --git a/commit_extractor_test.go b/commit_extractor_test.go index 5e251b3c..bfde4bff 100644 --- a/commit_extractor_test.go +++ b/commit_extractor_test.go @@ -20,14 +20,14 @@ func TestCommitExtractor(t *testing.T) { fixtures := []*Commit{ // [0] - &Commit{ + { Type: "foo", Scope: "c", Header: "1", Notes: []*Note{}, }, // [1] - &Commit{ + { Type: "foo", Scope: "b", Header: "2", @@ -37,7 +37,7 @@ func TestCommitExtractor(t *testing.T) { }, }, // [2] - &Commit{ + { Type: "bar", Scope: "d", Header: "3", @@ -47,7 +47,7 @@ func TestCommitExtractor(t *testing.T) { }, }, // [3] - &Commit{ + { Type: "foo", Scope: "a", Header: "4", @@ -56,7 +56,7 @@ func TestCommitExtractor(t *testing.T) { }, }, // [4] - &Commit{ + { Type: "", Scope: "", Header: "Merge1", @@ -67,7 +67,7 @@ func TestCommitExtractor(t *testing.T) { }, }, // [5] - &Commit{ + { Type: "", Scope: "", Header: "Revert1", @@ -81,14 +81,14 @@ func TestCommitExtractor(t *testing.T) { commitGroups, mergeCommits, revertCommits, noteGroups := extractor.Extract(fixtures) assert.Equal([]*CommitGroup{ - &CommitGroup{ + { RawTitle: "bar", Title: "BAR", Commits: []*Commit{ fixtures[2], }, }, - &CommitGroup{ + { RawTitle: "foo", Title: "Foo", Commits: []*Commit{ @@ -108,26 +108,26 @@ func TestCommitExtractor(t *testing.T) { }, revertCommits) assert.Equal([]*NoteGroup{ - &NoteGroup{ + { Title: "note1-title", Notes: []*Note{ fixtures[1].Notes[0], fixtures[2].Notes[0], }, }, - &NoteGroup{ + { Title: "note2-title", Notes: []*Note{ fixtures[1].Notes[1], }, }, - &NoteGroup{ + { Title: "note3-title", Notes: []*Note{ fixtures[2].Notes[1], }, }, - &NoteGroup{ + { Title: "note4-title", Notes: []*Note{ fixtures[3].Notes[0], diff --git a/commit_filter.go b/commit_filter.go index bd7ee797..763d88ee 100644 --- a/commit_filter.go +++ b/commit_filter.go @@ -4,7 +4,11 @@ import ( "strings" ) -func commitFilter(commits []*Commit, filters map[string][]string, noCaseSensitive bool) []*Commit { +func commitFilter(commits []*Commit, filters map[string][]string, noCaseSensitive bool) []*Commit { //nolint:gocyclo + // NOTE(khos2ow): this function is over our cyclomatic complexity goal. + // Be wary when adding branches, and look for functionality that could + // be reasonably moved into an injected dependency. + res := []*Commit{} for _, commit := range commits { diff --git a/commit_filter_test.go b/commit_filter_test.go index ec84ef10..59123bd9 100644 --- a/commit_filter_test.go +++ b/commit_filter_test.go @@ -18,27 +18,27 @@ func TestCommitFilter(t *testing.T) { } fixtures := []*Commit{ - &Commit{ + { Type: "foo", Scope: "hoge", Subject: "1", }, - &Commit{ + { Type: "foo", Scope: "fuga", Subject: "2", }, - &Commit{ + { Type: "bar", Scope: "hoge", Subject: "3", }, - &Commit{ + { Type: "bar", Scope: "fuga", Subject: "4", }, - &Commit{ + { Type: "Bar", Scope: "hogera", Subject: "5", diff --git a/commit_parser.go b/commit_parser.go index f3cc7a8b..8c62c981 100644 --- a/commit_parser.go +++ b/commit_parser.go @@ -80,7 +80,7 @@ func newCommitParser(logger *Logger, client gitcmd.Client, jiraClient JiraClient reRef: regexp.MustCompile("(?i)(" + joinedRefActions + ")\\s?([\\w/\\.\\-]+)?(?:" + joinedIssuePrefix + ")(\\d+)"), reIssue: regexp.MustCompile("(?:" + joinedIssuePrefix + ")(\\d+)"), reNotes: regexp.MustCompile("^(?i)\\s*(" + joinedNoteKeywords + ")[:\\s]+(.*)"), - reMention: regexp.MustCompile("@([\\w-]+)"), + reMention: regexp.MustCompile(`@([\w-]+)`), reJiraIssueDescription: regexp.MustCompile(opts.JiraIssueDescriptionPattern), } } @@ -223,8 +223,8 @@ func (p *commitParser) processHeader(commit *Commit, input string) { commit.Mentions = p.parseMentions(input) // Jira - if commit.JiraIssueId != "" { - p.processJiraIssue(commit, commit.JiraIssueId) + if commit.JiraIssueID != "" { + p.processJiraIssue(commit, commit.JiraIssueID) } } @@ -364,10 +364,10 @@ func (p *commitParser) uniqMentions(mentions []string) []string { return arr } -func (p *commitParser) processJiraIssue(commit *Commit, issueId string) { - issue, err := p.jiraClient.GetJiraIssue(commit.JiraIssueId) +func (p *commitParser) processJiraIssue(commit *Commit, issueID string) { + issue, err := p.jiraClient.GetJiraIssue(commit.JiraIssueID) if err != nil { - p.logger.Error(fmt.Sprintf("Failed to parse Jira story %s: %s\n", issueId, err)) + p.logger.Error(fmt.Sprintf("Failed to parse Jira story %s: %s\n", issueID, err)) return } commit.Type = p.config.Options.JiraTypeMaps[issue.Fields.Type.Name] diff --git a/commit_parser_test.go b/commit_parser_test.go index defc7357..b81aca71 100644 --- a/commit_parser_test.go +++ b/commit_parser_test.go @@ -33,7 +33,7 @@ func TestCommitParserParse(t *testing.T) { mock, nil, &Config{ Options: &Options{ CommitFilters: map[string][]string{ - "Type": []string{ + "Type": { "feat", "fix", "perf", @@ -79,7 +79,7 @@ func TestCommitParserParse(t *testing.T) { commits, err := parser.Parse("HEAD") assert.Nil(err) assert.Equal([]*Commit{ - &Commit{ + { Hash: &Hash{ Long: "65cf1add9735dcc4810dda3312b0792236c97c4e", Short: "65cf1add", @@ -97,7 +97,7 @@ func TestCommitParserParse(t *testing.T) { Merge: nil, Revert: nil, Refs: []*Ref{ - &Ref{ + { Action: "", Ref: "123", Source: "", @@ -111,7 +111,7 @@ func TestCommitParserParse(t *testing.T) { Subject: "Add new feature #123", Body: "", }, - &Commit{ + { Hash: &Hash{ Long: "14ef0b6d386c5432af9292eab3c8314fa3001bc7", Short: "14ef0b6d", @@ -132,24 +132,24 @@ func TestCommitParserParse(t *testing.T) { }, Revert: nil, Refs: []*Ref{ - &Ref{ + { Action: "", Ref: "3", Source: "", }, - &Ref{ + { Action: "Fixes", Ref: "3", Source: "", }, - &Ref{ + { Action: "Closes", Ref: "1", Source: "", }, }, Notes: []*Note{ - &Note{ + { Title: "BREAKING CHANGE", Body: "This is breaking point message.", }, @@ -167,7 +167,7 @@ Closes #1 BREAKING CHANGE: This is breaking point message.`, }, - &Commit{ + { Hash: &Hash{ Long: "809a8280ffd0dadb0f4e7ba9fc835e63c37d6af6", Short: "809a8280", @@ -201,7 +201,7 @@ BREAKING CHANGE: This is breaking point message.`, @hogefuga @FooBarBaz`, }, - &Commit{ + { Hash: &Hash{ Long: "74824d6bd1470b901ec7123d13a76a1b8938d8d0", Short: "74824d6b", @@ -219,19 +219,19 @@ BREAKING CHANGE: This is breaking point message.`, Merge: nil, Revert: nil, Refs: []*Ref{ - &Ref{ + { Action: "Fixes", Ref: "123", Source: "", }, - &Ref{ + { Action: "Closes", Ref: "456", Source: "username/repository", }, }, Notes: []*Note{ - &Note{ + { Title: "BREAKING CHANGE", Body: fmt.Sprintf(`This is multiline breaking change note. It is treated as the body of the Note until a mention or reference appears. @@ -281,7 +281,7 @@ class MyController extends Controller { Fixes #123 Closes username/repository#456`, "```", "```"), }, - &Commit{ + { Hash: &Hash{ Long: "123456789735dcc4810dda3312b0792236c97c4e", Short: "12345678", @@ -381,7 +381,7 @@ func TestCommitParserParseWithJira(t *testing.T) { mock, mockJiraClient{}, &Config{ Options: &Options{ CommitFilters: map[string][]string{ - "Type": []string{ + "Type": { "feat", "fix", "perf", @@ -391,7 +391,7 @@ func TestCommitParserParseWithJira(t *testing.T) { HeaderPattern: "^(?:(\\w*)|(?:\\[(.*)\\])?)\\:\\s(.*)$", HeaderPatternMaps: []string{ "Type", - "JiraIssueId", + "JiraIssueID", "Subject", }, JiraTypeMaps: map[string]string{ @@ -403,7 +403,7 @@ func TestCommitParserParseWithJira(t *testing.T) { commits, err := parser.Parse("HEAD") assert.Nil(err) commit := commits[0] - assert.Equal(commit.JiraIssueId, "JIRA-1111") + assert.Equal(commit.JiraIssueID, "JIRA-1111") assert.Equal(commit.JiraIssue.Type, "Story") assert.Equal(commit.JiraIssue.Summary, "summary of JIRA-1111") assert.Equal(commit.JiraIssue.Description, "description of JIRA-1111") diff --git a/example_test.go b/example_test.go index 931b3bb1..5d9ea93f 100644 --- a/example_test.go +++ b/example_test.go @@ -19,7 +19,7 @@ func Example() { }, Options: &Options{ CommitFilters: map[string][]string{ - "Type": []string{ + "Type": { "feat", "fix", }, diff --git a/fields.go b/fields.go index 123da6c7..213bc719 100644 --- a/fields.go +++ b/fields.go @@ -52,7 +52,7 @@ type NoteGroup struct { Notes []*Note } -// JiraIssue +// JiraIssue is information about a jira ticket (type, summary, description, and labels) type JiraIssue struct { Type string Summary string @@ -69,13 +69,13 @@ type Commit struct { Revert *Revert // If it is not a revert commit, `nil` is assigned Refs []*Ref Notes []*Note - Mentions []string // Name of the user included in the commit header or body + Mentions []string // Name of the user included in the commit header or body JiraIssue *JiraIssue // If no issue id found in header, `nil` is assigned - Header string // (e.g. `feat(core)[RNWY-310]: Add new feature`) - Type string // (e.g. `feat`) - Scope string // (e.g. `core`) - Subject string // (e.g. `Add new feature`) - JiraIssueId string // (e.g. `RNWY-310`) + Header string // (e.g. `feat(core)[RNWY-310]: Add new feature`) + Type string // (e.g. `feat`) + Scope string // (e.g. `core`) + Subject string // (e.g. `Add new feature`) + JiraIssueID string // (e.g. `RNWY-310`) Body string } diff --git a/jira.go b/jira.go index 78afbdd7..c1b1da31 100644 --- a/jira.go +++ b/jira.go @@ -4,6 +4,7 @@ import ( agjira "github.com/andygrunwald/go-jira" ) +// JiraClient is an HTTP client for Jira type JiraClient interface { GetJiraIssue(id string) (*agjira.Issue, error) } @@ -14,11 +15,12 @@ type jiraClient struct { url string } +// NewJiraClient returns an instance of JiraClient func NewJiraClient(config *Config) JiraClient { return jiraClient{ username: config.Options.JiraUsername, - token: config.Options.JiraToken, - url: config.Options.JiraUrl, + token: config.Options.JiraToken, + url: config.Options.JiraURL, } } diff --git a/jira_test.go b/jira_test.go index 30936e29..95290a17 100644 --- a/jira_test.go +++ b/jira_test.go @@ -1,14 +1,15 @@ package chglog import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestJira(t *testing.T) { assert := assert.New(t) - config := &Config { + config := &Config{ Options: &Options{ Processor: nil, NextTag: "", @@ -29,7 +30,7 @@ func TestJira(t *testing.T) { NoteKeywords: nil, JiraUsername: "uuu", JiraToken: "ppp", - JiraUrl: "http://jira.com", + JiraURL: "http://jira.com", JiraTypeMaps: nil, JiraIssueDescriptionPattern: "", }, diff --git a/logger.go b/logger.go index 52003ecb..280543e4 100644 --- a/logger.go +++ b/logger.go @@ -7,7 +7,7 @@ import ( "regexp" "github.com/fatih/color" - emoji "github.com/kyokomi/emoji/v2" + "github.com/kyokomi/emoji/v2" ) // Logger ... @@ -26,7 +26,7 @@ func NewLogger(stdout, stderr io.Writer, silent, noEmoji bool) *Logger { stderr: stderr, silent: silent, noEmoji: noEmoji, - reEmoji: regexp.MustCompile(":[\\w\\+_\\-]+:\\s?"), + reEmoji: regexp.MustCompile(`:[\w\+_\-]+:\s?`), } } diff --git a/logger_test.go b/logger_test.go index 77f5ff4d..5118bfe9 100644 --- a/logger_test.go +++ b/logger_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/fatih/color" - "github.com/stretchr/testify/assert" emoji "github.com/kyokomi/emoji/v2" + "github.com/stretchr/testify/assert" ) func TestLoggerLogSilent(t *testing.T) { @@ -40,7 +40,7 @@ func TestLoggerLogNoEmoji(t *testing.T) { stderr := &bytes.Buffer{} logger := NewLogger(stdout, stderr, false, true) logger.Log(":+1:Hello, World! :)") - assert.Equal(fmt.Sprint("Hello, World! :)\n"), stdout.String()) + assert.Equal(fmt.Sprint("Hello, World! :)\n"), stdout.String()) //nolint:gosimple } func TestLoggerError(t *testing.T) { diff --git a/processor.go b/processor.go index 9b46d387..505fec6c 100644 --- a/processor.go +++ b/processor.go @@ -33,8 +33,8 @@ func (p *GitHubProcessor) Bootstrap(config *Config) { p.Host = strings.TrimRight(p.Host, "/") } - p.reMention = regexp.MustCompile("@(\\w+)") - p.reIssue = regexp.MustCompile("(?i)(#|gh-)(\\d+)") + p.reMention = regexp.MustCompile(`@(\w+)`) + p.reIssue = regexp.MustCompile(`(?i)(#|gh-)(\d+)`) } // ProcessCommit ... @@ -88,8 +88,8 @@ func (p *GitLabProcessor) Bootstrap(config *Config) { p.Host = strings.TrimRight(p.Host, "/") } - p.reMention = regexp.MustCompile("@(\\w+)") - p.reIssue = regexp.MustCompile("(?i)#(\\d+)") + p.reMention = regexp.MustCompile(`@(\w+)`) + p.reIssue = regexp.MustCompile(`(?i)#(\d+)`) } // ProcessCommit ... @@ -143,8 +143,8 @@ func (p *BitbucketProcessor) Bootstrap(config *Config) { p.Host = strings.TrimRight(p.Host, "/") } - p.reMention = regexp.MustCompile("@(\\w+)") - p.reIssue = regexp.MustCompile("(?i)#(\\d+)") + p.reMention = regexp.MustCompile(`@(\w+)`) + p.reIssue = regexp.MustCompile(`(?i)#(\d+)`) } // ProcessCommit ... diff --git a/processor_test.go b/processor_test.go index 177f1506..88e8502e 100644 --- a/processor_test.go +++ b/processor_test.go @@ -27,7 +27,7 @@ func TestGitHubProcessor(t *testing.T) { multiline [#789](https://example.com/issues/789) [@foo](https://github.com/foo), [@bar](https://github.com/bar)`, Notes: []*Note{ - &Note{ + { Body: `issue1 [#11](https://example.com/issues/11) issue2 [#22](https://example.com/issues/22) [gh-56](https://example.com/issues/56) hoge fuga`, @@ -42,7 +42,7 @@ issue2 [#22](https://example.com/issues/22) multiline #789 @foo, @bar`, Notes: []*Note{ - &Note{ + { Body: `issue1 #11 issue2 #22 gh-56 hoge fuga`, @@ -89,7 +89,7 @@ func TestGitLabProcessor(t *testing.T) { multiline [#789](https://example.com/issues/789) [@foo](https://gitlab.com/foo), [@bar](https://gitlab.com/bar)`, Notes: []*Note{ - &Note{ + { Body: `issue1 [#11](https://example.com/issues/11) issue2 [#22](https://example.com/issues/22) gh-56 hoge fuga`, @@ -104,7 +104,7 @@ gh-56 hoge fuga`, multiline #789 @foo, @bar`, Notes: []*Note{ - &Note{ + { Body: `issue1 #11 issue2 #22 gh-56 hoge fuga`, @@ -151,7 +151,7 @@ func TestBitbucketProcessor(t *testing.T) { multiline [#789](https://example.com/issues/789/) [@foo](https://bitbucket.org/foo/), [@bar](https://bitbucket.org/bar/)`, Notes: []*Note{ - &Note{ + { Body: `issue1 [#11](https://example.com/issues/11/) issue2 [#22](https://example.com/issues/22/) gh-56 hoge fuga`, @@ -166,7 +166,7 @@ gh-56 hoge fuga`, multiline #789 @foo, @bar`, Notes: []*Note{ - &Note{ + { Body: `issue1 #11 issue2 #22 gh-56 hoge fuga`, diff --git a/tag_reader.go b/tag_reader.go index a1d00d8a..39486073 100644 --- a/tag_reader.go +++ b/tag_reader.go @@ -12,7 +12,6 @@ import ( type tagReader struct { client gitcmd.Client - format string separator string reFilter *regexp.Regexp } @@ -36,7 +35,7 @@ func (r *tagReader) ReadAll() ([]*Tag, error) { tags := []*Tag{} if err != nil { - return tags, fmt.Errorf("failed to get git-tag: %s", err.Error()) + return tags, fmt.Errorf("failed to get git-tag: %w", err) } lines := strings.Split(out, "\n") diff --git a/tag_reader_test.go b/tag_reader_test.go index 15e3b1e1..786951a5 100644 --- a/tag_reader_test.go +++ b/tag_reader_test.go @@ -33,7 +33,7 @@ func TestTagReader(t *testing.T) { assert.Equal( []*Tag{ - &Tag{ + { Name: "hoge_fuga", Subject: "Invalid semver tag name", Date: time.Date(2018, 3, 12, 12, 30, 10, 0, time.UTC), @@ -44,7 +44,7 @@ func TestTagReader(t *testing.T) { Date: time.Date(2018, 2, 3, 12, 30, 10, 0, time.UTC), }, }, - &Tag{ + { Name: "5.0.0-rc.0", Subject: "Release 5.0.0-rc.0", Date: time.Date(2018, 2, 3, 12, 30, 10, 0, time.UTC), @@ -59,7 +59,7 @@ func TestTagReader(t *testing.T) { Date: time.Date(2018, 2, 2, 10, 0, 40, 0, time.UTC), }, }, - &Tag{ + { Name: "4.4.4", Subject: "Release 4.4.4", Date: time.Date(2018, 2, 2, 10, 0, 40, 0, time.UTC), @@ -74,7 +74,7 @@ func TestTagReader(t *testing.T) { Date: time.Date(2018, 2, 2, 0, 0, 0, 0, time.UTC), }, }, - &Tag{ + { Name: "4.4.3", Subject: "This is tag subject", Date: time.Date(2018, 2, 2, 0, 0, 0, 0, time.UTC), @@ -89,7 +89,7 @@ func TestTagReader(t *testing.T) { Date: time.Date(2018, 2, 1, 0, 0, 0, 0, time.UTC), }, }, - &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), @@ -104,11 +104,11 @@ func TestTagReader(t *testing.T) { actual, ) - actual_filtered, err_filtered := newTagReader(client, "^v").ReadAll() - assert.Nil(err_filtered) + actualFiltered, errFiltered := newTagReader(client, "^v").ReadAll() + assert.Nil(errFiltered) 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), @@ -116,6 +116,6 @@ func TestTagReader(t *testing.T) { Previous: nil, }, }, - actual_filtered, + actualFiltered, ) } diff --git a/tag_selector.go b/tag_selector.go index fe17a505..7e292d9e 100644 --- a/tag_selector.go +++ b/tag_selector.go @@ -19,14 +19,16 @@ func (s *tagSelector) Select(tags []*Tag, query string) ([]*Tag, string, error) case 2: old := tokens[0] new := tokens[1] - if old == "" && new == "" { + switch { + case old == "" && new == "": return nil, "", nil - } else if old == "" { + case old == "": return s.selectBeforeTags(tags, new) - } else if new == "" { + case new == "": return s.selectAfterTags(tags, old) + default: + return s.selectRangeTags(tags, tokens[0], tokens[1]) } - return s.selectRangeTags(tags, tokens[0], tokens[1]) } return nil, "", errFailedQueryParse @@ -76,7 +78,8 @@ func (*tagSelector) selectBeforeTags(tags []*Tag, token string) ([]*Tag, string, } func (*tagSelector) selectAfterTags(tags []*Tag, token string) ([]*Tag, string, error) { - var ( + // NOTE(clok): the res slice can range in size based on the token passed in. + var ( //nolint:prealloc res []*Tag from string ) diff --git a/tag_selector_test.go b/tag_selector_test.go index ee8e55b4..a776087b 100644 --- a/tag_selector_test.go +++ b/tag_selector_test.go @@ -12,51 +12,51 @@ func TestTagSelector(t *testing.T) { selector := newTagSelector() fixtures := []*Tag{ - &Tag{Name: "2.2.12-rc.12"}, - &Tag{Name: "2.1.0"}, - &Tag{Name: "v2.0.0-beta.1"}, - &Tag{Name: "v1.2.9"}, - &Tag{Name: "v1.0.0"}, + {Name: "2.2.12-rc.12"}, + {Name: "2.1.0"}, + {Name: "v2.0.0-beta.1"}, + {Name: "v1.2.9"}, + {Name: "v1.0.0"}, } table := map[string][]string{ // Single - "2.2.12-rc.12": []string{ + "2.2.12-rc.12": { "2.2.12-rc.12", "2.1.0", }, - "v2.0.0-beta.1": []string{ + "v2.0.0-beta.1": { "v2.0.0-beta.1", "v1.2.9", }, - "v1.0.0": []string{ + "v1.0.0": { "v1.0.0", "", }, // ~ - "..2.1.0": []string{ + "..2.1.0": { "2.1.0", "v2.0.0-beta.1", "v1.2.9", "v1.0.0", "", }, - "..v1.0.0": []string{ + "..v1.0.0": { "v1.0.0", "", }, // ~ - "v2.0.0-beta.1..": []string{ + "v2.0.0-beta.1..": { "2.2.12-rc.12", "2.1.0", "v2.0.0-beta.1", "v1.2.9", }, - "2.2.12-rc.12..": []string{ + "2.2.12-rc.12..": { "2.2.12-rc.12", "2.1.0", }, - "v1.0.0..": []string{ + "v1.0.0..": { "2.2.12-rc.12", "2.1.0", "v2.0.0-beta.1", @@ -65,7 +65,7 @@ func TestTagSelector(t *testing.T) { "", }, // ~ - "v1.0.0..2.2.12-rc.12": []string{ + "v1.0.0..2.2.12-rc.12": { "2.2.12-rc.12", "2.1.0", "v2.0.0-beta.1", @@ -73,13 +73,13 @@ func TestTagSelector(t *testing.T) { "v1.0.0", "", }, - "v1.0.0..v2.0.0-beta.1": []string{ + "v1.0.0..v2.0.0-beta.1": { "v2.0.0-beta.1", "v1.2.9", "v1.0.0", "", }, - "v1.2.9..2.1.0": []string{ + "v1.2.9..2.1.0": { "2.1.0", "v2.0.0-beta.1", "v1.2.9",