diff --git a/internal/analyzer/analyzer.go b/internal/analyzer/analyzer.go index 8607f91..17fc4a5 100644 --- a/internal/analyzer/analyzer.go +++ b/internal/analyzer/analyzer.go @@ -9,13 +9,13 @@ import ( log "github.com/sirupsen/logrus" ) -//Analyzer struct +// Analyzer struct type Analyzer struct { analyzeCommits analyzeCommits Config config.ChangelogConfig } -//Rule for commits +// Rule for commits type Rule struct { Tag string TagString string @@ -24,11 +24,11 @@ type Rule struct { } type analyzeCommits interface { - analyze(commit shared.Commit, tag Rule) (shared.AnalyzedCommit, bool, error) + analyze(commit shared.Commit, tag Rule) (*shared.AnalyzedCommit, bool) getRules() []Rule } -//New Analyzer struct for given commit format +// New Analyzer struct for given commit format func New(format string, config config.ChangelogConfig) (*Analyzer, error) { analyzer := &Analyzer{ Config: config, @@ -45,7 +45,6 @@ func New(format string, config config.ChangelogConfig) (*Analyzer, error) { return nil, fmt.Errorf("invalid commit format: %s", format) } return analyzer, nil - } // GetRules from current mode @@ -53,9 +52,8 @@ func (a *Analyzer) GetRules() []Rule { return a.analyzeCommits.getRules() } -// Analyze commits and return commits splitted by major,minor,patch +// Analyze commits and return commits split by major,minor,patch func (a *Analyzer) Analyze(commits []shared.Commit) map[shared.Release][]shared.AnalyzedCommit { - analyzedCommits := make(map[shared.Release][]shared.AnalyzedCommit) analyzedCommits["major"] = make([]shared.AnalyzedCommit, 0) analyzedCommits["minor"] = make([]shared.AnalyzedCommit, 0) @@ -64,25 +62,21 @@ func (a *Analyzer) Analyze(commits []shared.Commit) map[shared.Release][]shared. for _, commit := range commits { for _, rule := range a.analyzeCommits.getRules() { - analyzedCommit, hasBreakingChange, err := a.analyzeCommits.analyze(commit, rule) - if err == nil { - if a.Config.PrintAll { - analyzedCommit.Print = true - } else { - analyzedCommit.Print = rule.Changelog - } - if hasBreakingChange { - analyzedCommits["major"] = append(analyzedCommits["major"], analyzedCommit) - } else { - analyzedCommits[rule.Release] = append(analyzedCommits[rule.Release], analyzedCommit) - } + analyzedCommit, hasBreakingChange := a.analyzeCommits.analyze(commit, rule) + if analyzedCommit == nil { + continue + } + if a.Config.PrintAll || rule.Changelog { + analyzedCommit.Print = true + } + if hasBreakingChange { + analyzedCommits["major"] = append(analyzedCommits["major"], *analyzedCommit) break } - + analyzedCommits[rule.Release] = append(analyzedCommits[rule.Release], *analyzedCommit) + break } } - log.Debugf("Analyzed commits: major=%d minor=%d patch=%d none=%d", len(analyzedCommits["major"]), len(analyzedCommits["minor"]), len(analyzedCommits["patch"]), len(analyzedCommits["none"])) - return analyzedCommits } diff --git a/internal/analyzer/angular.go b/internal/analyzer/angular.go index bd0ef50..b7c3784 100644 --- a/internal/analyzer/angular.go +++ b/internal/analyzer/angular.go @@ -2,7 +2,6 @@ package analyzer import ( - "fmt" "regexp" "strings" @@ -36,12 +35,14 @@ func newAngular() *angular { TagString: "Bug fixes", Release: "patch", Changelog: true, - }, { + }, + { Tag: "perf", - TagString: "Performance improvments", + TagString: "Performance improvements", Release: "patch", Changelog: true, - }, { + }, + { Tag: "docs", TagString: "Documentation changes", Release: "none", @@ -52,22 +53,26 @@ func newAngular() *angular { TagString: "Style", Release: "none", Changelog: false, - }, { + }, + { Tag: "refactor", TagString: "Code refactor", Release: "none", Changelog: false, - }, { + }, + { Tag: "test", TagString: "Testing", Release: "none", Changelog: false, - }, { + }, + { Tag: "chore", TagString: "Changes to the build process or auxiliary tools and libraries such as documentation generation", Release: "none", Changelog: false, - }, { + }, + { Tag: "build", TagString: "Changes to CI/CD", Release: "none", @@ -81,38 +86,37 @@ func (a *angular) getRules() []Rule { return a.rules } -func (a *angular) analyze(commit shared.Commit, rule Rule) (shared.AnalyzedCommit, bool, error) { +func (a *angular) analyze(commit shared.Commit, rule Rule) (*shared.AnalyzedCommit, bool) { + re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1)) + matches := re.FindStringSubmatch(commit.Message) + if matches == nil { + a.log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag) + return nil, false + } - analyzed := shared.AnalyzedCommit{ + analyzed := &shared.AnalyzedCommit{ Commit: commit, Tag: rule.Tag, TagString: rule.TagString, + Scope: shared.Scope(matches[2]), } - re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1)) - matches := re.FindAllStringSubmatch(commit.Message, -1) - if len(matches) >= 1 { - if len(matches[0]) >= 3 { - - analyzed.Scope = shared.Scope(matches[0][2]) - - message := strings.Join(matches[0][3:], "") - if !strings.Contains(message, "BREAKING CHANGE:") { - analyzed.ParsedMessage = strings.Trim(message, " ") - - a.log.Tracef("%s: found %s", commit.Message, rule.Tag) - return analyzed, false, nil - } - breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2) - - analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0]) - analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1]) - - a.log.Tracef(" %s, BREAKING CHANGE found", commit.Message) - return analyzed, true, nil - } + message := strings.Join(matches[3:], "") + if !strings.Contains(message, "BREAKING CHANGE:") { + analyzed.ParsedMessage = strings.Trim(message, " ") + a.log.Tracef("%s: found %s", commit.Message, rule.Tag) + return analyzed, false } - a.log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag) - return analyzed, false, fmt.Errorf("not found") + a.log.Tracef(" %s, BREAKING CHANGE found", commit.Message) + breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2) + + if len(breakingChange) > 1 { + analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0]) + analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1]) + return analyzed, true + } + + analyzed.ParsedBreakingChangeMessage = breakingChange[0] + return analyzed, true } diff --git a/internal/analyzer/angular_test.go b/internal/analyzer/angular_test.go index 6f85479..250d5aa 100644 --- a/internal/analyzer/angular_test.go +++ b/internal/analyzer/angular_test.go @@ -10,7 +10,7 @@ import ( ) func TestAngular(t *testing.T) { - + t.Parallel() testConfigs := []struct { testCase string commits []shared.Commit @@ -93,7 +93,8 @@ func TestAngular(t *testing.T) { }, }, }, - {testCase: "feat breaking change footer", + { + testCase: "feat breaking change footer", commits: []shared.Commit{ { Message: "feat(internal/changelog): my first commit", @@ -221,5 +222,4 @@ func TestAngular(t *testing.T) { assert.Equalf(t, test.analyzedCommits["patch"], analyzedCommits["patch"], "Testcase %s should have patch commits", test.testCase) assert.Equalf(t, test.analyzedCommits["none"], analyzedCommits["none"], "Testcase %s should have none commits", test.testCase) } - } diff --git a/internal/analyzer/conventional.go b/internal/analyzer/conventional.go index 1b6fe47..4486a48 100644 --- a/internal/analyzer/conventional.go +++ b/internal/analyzer/conventional.go @@ -2,7 +2,6 @@ package analyzer import ( - "fmt" "regexp" "strings" @@ -36,12 +35,14 @@ func newConventional() *conventional { TagString: "Bug fixes", Release: "patch", Changelog: true, - }, { + }, + { Tag: "perf", - TagString: "Performance improvments", + TagString: "Performance improvements", Release: "patch", Changelog: true, - }, { + }, + { Tag: "docs", TagString: "Documentation changes", Release: "none", @@ -52,22 +53,26 @@ func newConventional() *conventional { TagString: "Style", Release: "none", Changelog: false, - }, { + }, + { Tag: "refactor", TagString: "Code refactor", Release: "none", Changelog: false, - }, { + }, + { Tag: "test", TagString: "Testing", Release: "none", Changelog: false, - }, { + }, + { Tag: "chore", TagString: "Changes to the build process or auxiliary tools and libraries such as documentation generation", Release: "none", Changelog: false, - }, { + }, + { Tag: "build", TagString: "Changes to CI/CD", Release: "none", @@ -81,50 +86,37 @@ func (a *conventional) getRules() []Rule { return a.rules } -func (a *conventional) analyze(commit shared.Commit, rule Rule) (shared.AnalyzedCommit, bool, error) { +func (a *conventional) analyze(commit shared.Commit, rule Rule) (*shared.AnalyzedCommit, bool) { + re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1)) + matches := re.FindStringSubmatch(commit.Message) + if matches == nil { + a.log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag) + return nil, false + } - analyzed := shared.AnalyzedCommit{ + analyzed := &shared.AnalyzedCommit{ Commit: commit, Tag: rule.Tag, TagString: rule.TagString, + Scope: shared.Scope(matches[2]), } - re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1)) - matches := re.FindAllStringSubmatch(commit.Message, -1) - if len(matches) >= 1 { - if len(matches[0]) >= 4 { - - analyzed.Scope = shared.Scope(matches[0][2]) - - message := strings.Join(matches[0][4:], "") - if matches[0][3] == "" && !strings.Contains(message, "BREAKING CHANGE:") { - analyzed.ParsedMessage = strings.Trim(message, " ") - - a.log.Tracef("%s: found %s", commit.Message, rule.Tag) - return analyzed, false, nil - } - - if matches[0][3] == "" { - breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2) - analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0]) - analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1]) - a.log.Tracef(" %s, BREAKING CHANGE found", commit.Message) - return analyzed, true, nil - } - - breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2) - - if len(breakingChange) > 1 { - analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0]) - analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1]) - } else { - analyzed.ParsedBreakingChangeMessage = breakingChange[0] - } - a.log.Infof(" %s, BREAKING CHANGE found", commit.Message) - return analyzed, true, nil - } + message := strings.Join(matches[4:], "") + if matches[3] == "" && !strings.Contains(message, "BREAKING CHANGE:") { + analyzed.ParsedMessage = strings.Trim(message, " ") + a.log.Tracef("%s: found %s", commit.Message, rule.Tag) + return analyzed, false } - a.log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag) - return analyzed, false, fmt.Errorf("not found") + a.log.Infof(" %s, BREAKING CHANGE found", commit.Message) + breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2) + + if len(breakingChange) > 1 { + analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0]) + analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1]) + return analyzed, true + } + + analyzed.ParsedBreakingChangeMessage = breakingChange[0] + return analyzed, true } diff --git a/internal/analyzer/conventional_test.go b/internal/analyzer/conventional_test.go index 20833a9..ca780f0 100644 --- a/internal/analyzer/conventional_test.go +++ b/internal/analyzer/conventional_test.go @@ -10,15 +10,15 @@ import ( ) func TestConventional(t *testing.T) { - + t.Parallel() testConfigs := []struct { - testCase string - commits []shared.Commit - analyzedCommits map[shared.Release][]shared.AnalyzedCommit + testCase string + commits []shared.Commit + wantAnalyzedCommits map[shared.Release][]shared.AnalyzedCommit }{ { testCase: "feat", - analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ + wantAnalyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ "minor": { { Commit: shared.Commit{ @@ -64,7 +64,7 @@ func TestConventional(t *testing.T) { }, { testCase: "feat breaking change", - analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ + wantAnalyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ "minor": { { Commit: shared.Commit{ @@ -112,24 +112,7 @@ func TestConventional(t *testing.T) { }, { testCase: "feat breaking change footer", - commits: []shared.Commit{ - { - Message: "feat: my first commit", - Author: "me", - Hash: "12345667", - }, - { - Message: "feat: my first break \n\nBREAKING CHANGE: change api to v2\n", - Author: "me", - Hash: "12345668", - }, - { - Message: "feat!: my first break \n\nBREAKING CHANGE: hey from the change", - Author: "me", - Hash: "12345669", - }, - }, - analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ + wantAnalyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ "minor": { { Commit: shared.Commit{ @@ -175,10 +158,27 @@ func TestConventional(t *testing.T) { "patch": {}, "none": {}, }, + commits: []shared.Commit{ + { + Message: "feat: my first commit", + Author: "me", + Hash: "12345667", + }, + { + Message: "feat: my first break \n\nBREAKING CHANGE: change api to v2\n", + Author: "me", + Hash: "12345668", + }, + { + Message: "feat!: my first break \n\nBREAKING CHANGE: hey from the change", + Author: "me", + Hash: "12345669", + }, + }, }, { testCase: "invalid", - analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ + wantAnalyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ "minor": {}, "major": {}, "patch": {}, @@ -199,7 +199,7 @@ func TestConventional(t *testing.T) { }, { testCase: "feat and build", - analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ + wantAnalyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ "minor": { { Commit: shared.Commit{ @@ -245,6 +245,52 @@ func TestConventional(t *testing.T) { }, }, }, + { + testCase: "fix and build", + wantAnalyzedCommits: map[shared.Release][]shared.AnalyzedCommit{ + "minor": {}, + "none": { + { + Commit: shared.Commit{ + Message: "build: my first build", + Author: "me", + Hash: "12345668", + }, + Scope: "", + ParsedMessage: "my first build", + Tag: "build", + TagString: "Changes to CI/CD", + Print: false, + ParsedBreakingChangeMessage: "", + }, + }, + "patch": {{ + Commit: shared.Commit{ + Message: "fix: my first commit", + Author: "me", + Hash: "12345667", + }, + Scope: "", + ParsedMessage: "my first commit", + Tag: "fix", + TagString: "Bug fixes", + Print: true, + }}, + "major": {}, + }, + commits: []shared.Commit{ + { + Message: "fix: my first commit", + Author: "me", + Hash: "12345667", + }, + { + Message: "build: my first build", + Author: "me", + Hash: "12345668", + }, + }, + }, } conventional, err := analyzer.New("conventional", config.ChangelogConfig{}) @@ -252,10 +298,9 @@ func TestConventional(t *testing.T) { for _, test := range testConfigs { analyzedCommits := conventional.Analyze(test.commits) - assert.Equalf(t, test.analyzedCommits["major"], analyzedCommits["major"], "Testcase %s should have major commits", test.testCase) - assert.Equalf(t, test.analyzedCommits["minor"], analyzedCommits["minor"], "Testcase %s should have minor commits", test.testCase) - assert.Equalf(t, test.analyzedCommits["patch"], analyzedCommits["patch"], "Testcase %s should have patch commits", test.testCase) - assert.Equalf(t, test.analyzedCommits["none"], analyzedCommits["none"], "Testcase %s should have none commits", test.testCase) + assert.Equalf(t, test.wantAnalyzedCommits["major"], analyzedCommits["major"], "Testcase %s should have major commits", test.testCase) + assert.Equalf(t, test.wantAnalyzedCommits["minor"], analyzedCommits["minor"], "Testcase %s should have minor commits", test.testCase) + assert.Equalf(t, test.wantAnalyzedCommits["patch"], analyzedCommits["patch"], "Testcase %s should have patch commits", test.testCase) + assert.Equalf(t, test.wantAnalyzedCommits["none"], analyzedCommits["none"], "Testcase %s should have none commits", test.testCase) } - } diff --git a/pkg/semanticrelease/semantic-release.go b/pkg/semanticrelease/semantic-release.go index ce6f693..1cd5a9e 100644 --- a/pkg/semanticrelease/semantic-release.go +++ b/pkg/semanticrelease/semantic-release.go @@ -23,7 +23,7 @@ import ( // SemanticRelease struct type SemanticRelease struct { config *config.ReleaseConfig - gitutil *gitutil.GitUtil + gitUtil *gitutil.GitUtil analyzer *analyzer.Analyzer calculator *calculator.Calculator releaser releaser.Releaser @@ -57,7 +57,7 @@ func New(c *config.ReleaseConfig, repository string, checkConfig bool) (*Semanti return &SemanticRelease{ config: c, - gitutil: util, + gitUtil: util, releaser: releaser, analyzer: analyzer, repository: repository, @@ -67,9 +67,9 @@ func New(c *config.ReleaseConfig, repository string, checkConfig bool) (*Semanti }, nil } -//GetCIProvider result with ci config +// GetCIProvider result with ci config func (s *SemanticRelease) GetCIProvider() (*ci.ProviderConfig, error) { - return ci.GetCIProvider(s.gitutil, s.checkConfig, ci.ReadAllEnvs()) + return ci.GetCIProvider(s.gitUtil, s.checkConfig, ci.ReadAllEnvs()) } // GetNextVersion from .version or calculate new from commits @@ -86,7 +86,7 @@ func (s *SemanticRelease) GetNextVersion(provider *ci.ProviderConfig, force bool } } - lastVersion, lastVersionHash, err := s.gitutil.GetLastVersion() + lastVersion, lastVersionHash, err := s.gitUtil.GetLastVersion() if err != nil { return nil, err } @@ -99,7 +99,7 @@ func (s *SemanticRelease) GetNextVersion(provider *ci.ProviderConfig, force bool firstRelease = true } - commits, err := s.gitutil.GetCommits(lastVersionHash) + commits, err := s.gitUtil.GetCommits(lastVersionHash) if err != nil { return nil, err } @@ -149,15 +149,14 @@ func (s *SemanticRelease) GetNextVersion(provider *ci.ProviderConfig, force bool return &releaseVersion, err } -//SetVersion for git repository +// SetVersion for git repository func (s *SemanticRelease) SetVersion(provider *ci.ProviderConfig, version string) error { - newVersion, err := semver.NewVersion(version) if err != nil { return err } - lastVersion, lastVersionHash, err := s.gitutil.GetLastVersion() + lastVersion, lastVersionHash, err := s.gitUtil.GetLastVersion() if err != nil { return err } @@ -187,17 +186,15 @@ func (s *SemanticRelease) GetChangelog(releaseVersion *shared.ReleaseVersion) (* CommitURL: s.releaser.GetCommitURL(), CompareURL: s.releaser.GetCompareURL(releaseVersion.Last.Version.String(), releaseVersion.Next.Version.String()), }, releaseVersion.Commits) - } -// WriteChangeLog wirtes changelog content to the given file +// WriteChangeLog writes changelog content to the given file func (s *SemanticRelease) WriteChangeLog(changelogContent, file string) error { - return ioutil.WriteFile(file, []byte(changelogContent), 0644) + return ioutil.WriteFile(file, []byte(changelogContent), 644) } // Release publish release to provider func (s *SemanticRelease) Release(provider *ci.ProviderConfig, force bool) error { - if provider.IsPR { log.Infof("Will not perform a new release. This is a pull request") return nil