You've already forked go-semantic-release
feat(analyzer): update AnalyzedCommit to add flexibility in parsing a message
This provides flexibility of parsing and rendering structured messages with more detail in the changelog and helps extract metadata from the message. The new structure can be used to split a message in multiple blocks (e.g. footer)
This commit is contained in:
@@ -3,6 +3,8 @@ package analyzer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Nightapes/go-semantic-release/internal/shared"
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
"github.com/Nightapes/go-semantic-release/pkg/config"
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
@@ -12,7 +14,8 @@ import (
|
|||||||
// Analyzer struct
|
// Analyzer struct
|
||||||
type Analyzer struct {
|
type Analyzer struct {
|
||||||
analyzeCommits analyzeCommits
|
analyzeCommits analyzeCommits
|
||||||
Config config.ChangelogConfig
|
ChangelogConfig config.ChangelogConfig
|
||||||
|
AnalyzerConfig config.AnalyzerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rule for commits
|
// Rule for commits
|
||||||
@@ -24,14 +27,15 @@ type Rule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type analyzeCommits interface {
|
type analyzeCommits interface {
|
||||||
analyze(commit shared.Commit, tag Rule) (*shared.AnalyzedCommit, bool)
|
analyze(commit shared.Commit, tag Rule) *shared.AnalyzedCommit
|
||||||
getRules() []Rule
|
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) {
|
func New(format string, analyzerConfig config.AnalyzerConfig, chglogConfig config.ChangelogConfig) (*Analyzer, error) {
|
||||||
analyzer := &Analyzer{
|
analyzer := &Analyzer{
|
||||||
Config: config,
|
AnalyzerConfig: analyzerConfig,
|
||||||
|
ChangelogConfig: chglogConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch format {
|
switch format {
|
||||||
@@ -39,7 +43,7 @@ func New(format string, config config.ChangelogConfig) (*Analyzer, error) {
|
|||||||
analyzer.analyzeCommits = newAngular()
|
analyzer.analyzeCommits = newAngular()
|
||||||
log.Debugf("Commit format set to %s", ANGULAR)
|
log.Debugf("Commit format set to %s", ANGULAR)
|
||||||
case CONVENTIONAL:
|
case CONVENTIONAL:
|
||||||
analyzer.analyzeCommits = newConventional()
|
analyzer.analyzeCommits = newConventional(analyzerConfig)
|
||||||
log.Debugf("Commit format set to %s", CONVENTIONAL)
|
log.Debugf("Commit format set to %s", CONVENTIONAL)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid commit format: %s", format)
|
return nil, fmt.Errorf("invalid commit format: %s", format)
|
||||||
@@ -62,14 +66,14 @@ func (a *Analyzer) Analyze(commits []shared.Commit) map[shared.Release][]shared.
|
|||||||
|
|
||||||
for _, commit := range commits {
|
for _, commit := range commits {
|
||||||
for _, rule := range a.analyzeCommits.getRules() {
|
for _, rule := range a.analyzeCommits.getRules() {
|
||||||
analyzedCommit, hasBreakingChange := a.analyzeCommits.analyze(commit, rule)
|
analyzedCommit := a.analyzeCommits.analyze(commit, rule)
|
||||||
if analyzedCommit == nil {
|
if analyzedCommit == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if a.Config.PrintAll || rule.Changelog {
|
if a.ChangelogConfig.PrintAll || rule.Changelog {
|
||||||
analyzedCommit.Print = true
|
analyzedCommit.Print = true
|
||||||
}
|
}
|
||||||
if hasBreakingChange {
|
if analyzedCommit.IsBreaking {
|
||||||
analyzedCommits["major"] = append(analyzedCommits["major"], *analyzedCommit)
|
analyzedCommits["major"] = append(analyzedCommits["major"], *analyzedCommit)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -80,3 +84,52 @@ func (a *Analyzer) Analyze(commits []shared.Commit) map[shared.Release][]shared.
|
|||||||
log.Debugf("Analyzed commits: major=%d minor=%d patch=%d none=%d", len(analyzedCommits["major"]), len(analyzedCommits["minor"]), len(analyzedCommits["patch"]), len(analyzedCommits["none"]))
|
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
|
return analyzedCommits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMessageParts(msg string) (header string, bodyBlocks []string){
|
||||||
|
firstSplit := strings.SplitN(msg, "\n", 2)
|
||||||
|
header = firstSplit[0]
|
||||||
|
bodyBlocks = make([]string, 0)
|
||||||
|
|
||||||
|
if len(firstSplit) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Trim and then split by a blank line
|
||||||
|
remaining := strings.Trim(firstSplit[1], "\n")
|
||||||
|
bodyBlocks = strings.Split(remaining, "\n\n")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMessageBlock(msg string, prefixes []string) shared.MessageBlock {
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if !strings.HasPrefix(msg, prefix + ":") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
content := strings.Replace(msg, prefix+":", "", 1)
|
||||||
|
return shared.MessageBlock{
|
||||||
|
Label: prefix,
|
||||||
|
Content: strings.TrimSpace(content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shared.MessageBlock{
|
||||||
|
Label: "",
|
||||||
|
Content: msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// getRegexMatchedMap will match a regex with named groups and map the matching
|
||||||
|
// results to corresponding group names
|
||||||
|
//
|
||||||
|
func getRegexMatchedMap(regEx, url string) (paramsMap map[string]string) {
|
||||||
|
var compRegEx = regexp.MustCompile(regEx)
|
||||||
|
match := compRegEx.FindStringSubmatch(url)
|
||||||
|
|
||||||
|
paramsMap = make(map[string]string)
|
||||||
|
for i, name := range compRegEx.SubexpNames() {
|
||||||
|
if i > 0 && i <= len(match) {
|
||||||
|
paramsMap[name] = match[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paramsMap
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func TestAnalyzer(t *testing.T) {
|
func TestAnalyzer(t *testing.T) {
|
||||||
|
|
||||||
_, err := analyzer.New("unknown", config.ChangelogConfig{})
|
_, err := analyzer.New("unknown", config.AnalyzerConfig{}, config.ChangelogConfig{})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,12 +86,12 @@ func (a *angular) getRules() []Rule {
|
|||||||
return a.rules
|
return a.rules
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *angular) analyze(commit shared.Commit, rule Rule) (*shared.AnalyzedCommit, bool) {
|
func (a *angular) analyze(commit shared.Commit, rule Rule) *shared.AnalyzedCommit {
|
||||||
re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1))
|
re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1))
|
||||||
matches := re.FindStringSubmatch(commit.Message)
|
matches := re.FindStringSubmatch(commit.Message)
|
||||||
if matches == nil {
|
if matches == nil {
|
||||||
a.log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag)
|
a.log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag)
|
||||||
return nil, false
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzed := &shared.AnalyzedCommit{
|
analyzed := &shared.AnalyzedCommit{
|
||||||
@@ -105,18 +105,21 @@ func (a *angular) analyze(commit shared.Commit, rule Rule) (*shared.AnalyzedComm
|
|||||||
if !strings.Contains(message, "BREAKING CHANGE:") {
|
if !strings.Contains(message, "BREAKING CHANGE:") {
|
||||||
analyzed.ParsedMessage = strings.Trim(message, " ")
|
analyzed.ParsedMessage = strings.Trim(message, " ")
|
||||||
a.log.Tracef("%s: found %s", commit.Message, rule.Tag)
|
a.log.Tracef("%s: found %s", commit.Message, rule.Tag)
|
||||||
return analyzed, false
|
return analyzed
|
||||||
}
|
}
|
||||||
|
|
||||||
a.log.Tracef(" %s, BREAKING CHANGE found", commit.Message)
|
a.log.Tracef(" %s, BREAKING CHANGE found", commit.Message)
|
||||||
breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2)
|
breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2)
|
||||||
|
|
||||||
|
analyzed.IsBreaking = true
|
||||||
|
|
||||||
if len(breakingChange) > 1 {
|
if len(breakingChange) > 1 {
|
||||||
analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0])
|
analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0])
|
||||||
analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1])
|
analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1])
|
||||||
return analyzed, true
|
|
||||||
|
return analyzed
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzed.ParsedBreakingChangeMessage = breakingChange[0]
|
analyzed.ParsedBreakingChangeMessage = breakingChange[0]
|
||||||
return analyzed, true
|
return analyzed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ func TestAngular(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "change api to v2",
|
ParsedBreakingChangeMessage: "change api to v2",
|
||||||
|
IsBreaking: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"patch": {},
|
"patch": {},
|
||||||
@@ -135,6 +136,7 @@ func TestAngular(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "change api to v2",
|
ParsedBreakingChangeMessage: "change api to v2",
|
||||||
|
IsBreaking: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"patch": {},
|
"patch": {},
|
||||||
@@ -212,7 +214,7 @@ func TestAngular(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
angular, err := analyzer.New("angular", config.ChangelogConfig{})
|
angular, err := analyzer.New("angular", config.AnalyzerConfig{}, config.ChangelogConfig{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
for _, test := range testConfigs {
|
for _, test := range testConfigs {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -14,14 +14,18 @@ type conventional struct {
|
|||||||
rules []Rule
|
rules []Rule
|
||||||
regex string
|
regex string
|
||||||
log *log.Entry
|
log *log.Entry
|
||||||
|
config config.AnalyzerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONVENTIONAL identifier
|
// CONVENTIONAL identifier
|
||||||
const CONVENTIONAL = "conventional"
|
const CONVENTIONAL = "conventional"
|
||||||
|
const breakingChangeKeywords = "BREAKING CHANGE"
|
||||||
|
const breakingChangePrefix = breakingChangeKeywords + ":"
|
||||||
|
|
||||||
func newConventional() *conventional {
|
func newConventional(config config.AnalyzerConfig) *conventional {
|
||||||
return &conventional{
|
return &conventional{
|
||||||
regex: `^(TAG)(?:\((.*)\))?(\!)?: (?s)(.*)`,
|
config: config,
|
||||||
|
regex: `^(?P<type>\w*)(?:\((?P<scope>.*)\))?(?P<breaking>\!)?: (?P<subject>.*)`,
|
||||||
log: log.WithField("analyzer", CONVENTIONAL),
|
log: log.WithField("analyzer", CONVENTIONAL),
|
||||||
rules: []Rule{
|
rules: []Rule{
|
||||||
{
|
{
|
||||||
@@ -86,37 +90,79 @@ func (a *conventional) getRules() []Rule {
|
|||||||
return a.rules
|
return a.rules
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *conventional) analyze(commit shared.Commit, rule Rule) (*shared.AnalyzedCommit, bool) {
|
func (a *conventional) analyze(commit shared.Commit, rule Rule) *shared.AnalyzedCommit {
|
||||||
re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1))
|
prefixes := append(a.config.BlockPrefixes, breakingChangeKeywords)
|
||||||
matches := re.FindStringSubmatch(commit.Message)
|
|
||||||
if matches == nil {
|
header, txtBlocks := getMessageParts(commit.Message)
|
||||||
|
matches := getRegexMatchedMap(a.regex, header)
|
||||||
|
|
||||||
|
if len(matches) == 0 || matches["type"] != rule.Tag{
|
||||||
a.log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag)
|
a.log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag)
|
||||||
return nil, false
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msgBlockMap := make(map[string][]shared.MessageBlock)
|
||||||
|
footer := ""
|
||||||
|
if len(txtBlocks) > 0 {
|
||||||
|
bodyCount := len(txtBlocks)-1
|
||||||
|
if len(txtBlocks) == 1 {
|
||||||
|
bodyCount = 1
|
||||||
|
}
|
||||||
|
bodyTxtBlocks := txtBlocks[0:bodyCount]
|
||||||
|
if len(txtBlocks) > 1{
|
||||||
|
footer = txtBlocks[len(txtBlocks)-1]
|
||||||
|
}
|
||||||
|
msgBlockMap["body"] = getMessageBlocks(bodyTxtBlocks, prefixes)
|
||||||
|
|
||||||
|
if len(footer) > 0{
|
||||||
|
footerLines := strings.Split(footer, "\n")
|
||||||
|
msgBlockMap["footer"] = getMessageBlocks(footerLines, prefixes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzed := &shared.AnalyzedCommit{
|
analyzed := &shared.AnalyzedCommit{
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
Tag: rule.Tag,
|
Tag: rule.Tag,
|
||||||
TagString: rule.TagString,
|
TagString: rule.TagString,
|
||||||
Scope: shared.Scope(matches[2]),
|
Scope: shared.Scope(matches["scope"]),
|
||||||
|
Subject: strings.TrimSpace(matches["subject"]),
|
||||||
|
MessageBlocks: msgBlockMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
message := strings.Join(matches[4:], "")
|
isBreaking := matches["breaking"] == "!" || strings.Contains(commit.Message, breakingChangePrefix)
|
||||||
if matches[3] == "" && !strings.Contains(message, "BREAKING CHANGE:") {
|
analyzed.IsBreaking = isBreaking
|
||||||
analyzed.ParsedMessage = strings.Trim(message, " ")
|
|
||||||
|
oldMsgSplit := strings.SplitN(commit.Message, "\n", 2)
|
||||||
|
originalBodyBlock := ""
|
||||||
|
if len(oldMsgSplit) > 1 {
|
||||||
|
originalBodyBlock = oldMsgSplit[1]
|
||||||
|
}
|
||||||
|
oldFormatMessage := strings.TrimSpace(matches["subject"] + "\n" + originalBodyBlock)
|
||||||
|
if !isBreaking {
|
||||||
|
analyzed.ParsedMessage = strings.Trim(oldFormatMessage, " ")
|
||||||
a.log.Tracef("%s: found %s", commit.Message, rule.Tag)
|
a.log.Tracef("%s: found %s", commit.Message, rule.Tag)
|
||||||
return analyzed, false
|
return analyzed
|
||||||
}
|
}
|
||||||
|
|
||||||
a.log.Infof(" %s, BREAKING CHANGE found", commit.Message)
|
a.log.Infof(" %s, BREAKING CHANGE found", commit.Message)
|
||||||
breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2)
|
breakingChange := strings.SplitN(oldFormatMessage, breakingChangePrefix, 2)
|
||||||
|
|
||||||
if len(breakingChange) > 1 {
|
if len(breakingChange) > 1 {
|
||||||
analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0])
|
analyzed.ParsedMessage = strings.TrimSpace(breakingChange[0])
|
||||||
analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1])
|
analyzed.ParsedBreakingChangeMessage = strings.TrimSpace(breakingChange[1])
|
||||||
return analyzed, true
|
} else {
|
||||||
|
analyzed.ParsedBreakingChangeMessage = breakingChange[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzed.ParsedBreakingChangeMessage = breakingChange[0]
|
return analyzed
|
||||||
return analyzed, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMessageBlocks(txtArray, prefixes []string) []shared.MessageBlock {
|
||||||
|
blocks := make([]shared.MessageBlock, len(txtArray))
|
||||||
|
for i, line := range txtArray{
|
||||||
|
blocks[i] = parseMessageBlock(line, prefixes)
|
||||||
|
}
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ func TestConventional(t *testing.T) {
|
|||||||
ParsedMessage: "my first commit",
|
ParsedMessage: "my first commit",
|
||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
Print: true,
|
Print: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -42,6 +44,8 @@ func TestConventional(t *testing.T) {
|
|||||||
ParsedMessage: "no scope",
|
ParsedMessage: "no scope",
|
||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
|
Subject: "no scope",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
Print: true,
|
Print: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -77,6 +81,8 @@ func TestConventional(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"major": {
|
"major": {
|
||||||
@@ -92,6 +98,9 @@ func TestConventional(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "my first break",
|
ParsedBreakingChangeMessage: "my first break",
|
||||||
|
IsBreaking: true,
|
||||||
|
Subject: "my first break",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"patch": {},
|
"patch": {},
|
||||||
@@ -125,6 +134,8 @@ func TestConventional(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"major": {
|
"major": {
|
||||||
@@ -140,6 +151,15 @@ func TestConventional(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "change api to v2",
|
ParsedBreakingChangeMessage: "change api to v2",
|
||||||
|
IsBreaking: true,
|
||||||
|
Subject: "my first break",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{
|
||||||
|
"body" : { shared.MessageBlock{
|
||||||
|
Label: "BREAKING CHANGE",
|
||||||
|
Content: "change api to v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Commit: shared.Commit{
|
Commit: shared.Commit{
|
||||||
@@ -153,6 +173,15 @@ func TestConventional(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "hey from the change",
|
ParsedBreakingChangeMessage: "hey from the change",
|
||||||
|
IsBreaking: true,
|
||||||
|
Subject: "my first break",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{
|
||||||
|
"body" : {shared.MessageBlock{
|
||||||
|
Label: "BREAKING CHANGE",
|
||||||
|
Content: "hey from the change",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"patch": {},
|
"patch": {},
|
||||||
@@ -212,6 +241,8 @@ func TestConventional(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"none": {
|
"none": {
|
||||||
@@ -227,6 +258,8 @@ func TestConventional(t *testing.T) {
|
|||||||
TagString: "Changes to CI/CD",
|
TagString: "Changes to CI/CD",
|
||||||
Print: false,
|
Print: false,
|
||||||
ParsedBreakingChangeMessage: "",
|
ParsedBreakingChangeMessage: "",
|
||||||
|
Subject: "my first build",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"patch": {},
|
"patch": {},
|
||||||
@@ -262,6 +295,8 @@ func TestConventional(t *testing.T) {
|
|||||||
TagString: "Changes to CI/CD",
|
TagString: "Changes to CI/CD",
|
||||||
Print: false,
|
Print: false,
|
||||||
ParsedBreakingChangeMessage: "",
|
ParsedBreakingChangeMessage: "",
|
||||||
|
Subject: "my first build",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"patch": {{
|
"patch": {{
|
||||||
@@ -275,6 +310,8 @@ func TestConventional(t *testing.T) {
|
|||||||
Tag: "fix",
|
Tag: "fix",
|
||||||
TagString: "Bug fixes",
|
TagString: "Bug fixes",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
}},
|
}},
|
||||||
"major": {},
|
"major": {},
|
||||||
},
|
},
|
||||||
@@ -291,9 +328,53 @@ func TestConventional(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
testCase: "fix issue with footers",
|
||||||
|
wantAnalyzedCommits: map[shared.Release][]shared.AnalyzedCommit{
|
||||||
|
"patch": {{
|
||||||
|
Commit: shared.Commit{
|
||||||
|
Message: "fix: squash bug for logging\n\nNote: now the logs will not print lines twice.\n\nIssue: #123\nSeverity: medium",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
Scope: "",
|
||||||
|
ParsedMessage: "squash bug for logging\n\nNote: now the logs will not print lines twice.\n\nIssue: #123\nSeverity: medium",
|
||||||
|
Tag: "fix",
|
||||||
|
TagString: "Bug fixes",
|
||||||
|
Print: true,
|
||||||
|
Subject: "squash bug for logging",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{
|
||||||
|
"body": { shared.MessageBlock{
|
||||||
|
Label: "Note",
|
||||||
|
Content: "now the logs will not print lines twice.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"footer": { shared.MessageBlock{
|
||||||
|
Label: "Issue",
|
||||||
|
Content: "#123",
|
||||||
|
},
|
||||||
|
shared.MessageBlock{
|
||||||
|
Label: "Severity",
|
||||||
|
Content: "medium",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
"minor": {},
|
||||||
|
"major": {},
|
||||||
|
"none": {},
|
||||||
|
},
|
||||||
|
commits: []shared.Commit{
|
||||||
|
{
|
||||||
|
Message: "fix: squash bug for logging\n\nNote: now the logs will not print lines twice.\n\nIssue: #123\nSeverity: medium",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
conventional, err := analyzer.New("conventional", config.ChangelogConfig{})
|
conventional, err := analyzer.New("conventional", config.AnalyzerConfig{BlockPrefixes: []string{"Note", "Issue", "Severity"}}, config.ChangelogConfig{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
for _, test := range testConfigs {
|
for _, test := range testConfigs {
|
||||||
|
|||||||
2
internal/cache/cache_test.go
vendored
2
internal/cache/cache_test.go
vendored
@@ -69,6 +69,8 @@ func TestWriteAndReadCache(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "add gitlab as release option",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ func (c *Changelog) GenerateChanglog(templateConfig shared.ChangelogTemplateConf
|
|||||||
for _, commits := range analyzedCommits {
|
for _, commits := range analyzedCommits {
|
||||||
for _, commit := range commits {
|
for _, commit := range commits {
|
||||||
if commit.Print {
|
if commit.Print {
|
||||||
if commit.ParsedBreakingChangeMessage != "" {
|
if commit.IsBreaking {
|
||||||
commitsBreakingChange = append(commitsBreakingChange, commit)
|
commitsBreakingChange = append(commitsBreakingChange, commit)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -142,6 +142,7 @@ func (c *Changelog) GenerateChanglog(templateConfig shared.ChangelogTemplateConf
|
|||||||
HasDockerLatest: c.config.Changelog.Docker.Latest,
|
HasDockerLatest: c.config.Changelog.Docker.Latest,
|
||||||
DockerRepository: c.config.Changelog.Docker.Repository,
|
DockerRepository: c.config.Changelog.Docker.Repository,
|
||||||
}
|
}
|
||||||
|
|
||||||
template := defaultCommitListSubTemplate + defaultChangelog
|
template := defaultCommitListSubTemplate + defaultChangelog
|
||||||
if c.config.Changelog.TemplatePath != "" {
|
if c.config.Changelog.TemplatePath != "" {
|
||||||
content, err := ioutil.ReadFile(c.config.Changelog.TemplatePath)
|
content, err := ioutil.ReadFile(c.config.Changelog.TemplatePath)
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ func TestChangelog(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -64,6 +66,8 @@ func TestChangelog(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -88,6 +92,8 @@ func TestChangelog(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Commit: shared.Commit{
|
Commit: shared.Commit{
|
||||||
@@ -101,6 +107,15 @@ func TestChangelog(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "change api to v2",
|
ParsedBreakingChangeMessage: "change api to v2",
|
||||||
|
IsBreaking: true,
|
||||||
|
Subject: "my first break",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{
|
||||||
|
"body" : { shared.MessageBlock{
|
||||||
|
Label: "BREAKING CHANGE",
|
||||||
|
Content: "change api to v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -126,6 +141,15 @@ func TestChangelog(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "hey from the change",
|
ParsedBreakingChangeMessage: "hey from the change",
|
||||||
|
IsBreaking: true,
|
||||||
|
Subject: "my first break",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{
|
||||||
|
"body" : { shared.MessageBlock{
|
||||||
|
Label: "BREAKING CHANGE",
|
||||||
|
Content: "hey from the change",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Commit: shared.Commit{
|
Commit: shared.Commit{
|
||||||
@@ -138,6 +162,8 @@ func TestChangelog(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my first commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Commit: shared.Commit{
|
Commit: shared.Commit{
|
||||||
@@ -150,10 +176,12 @@ func TestChangelog(t *testing.T) {
|
|||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
|
Subject: "my second commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Commit: shared.Commit{
|
Commit: shared.Commit{
|
||||||
Message: "feat: my new commit \n\nmy first break: BREAKING CHANGE: change api to v2",
|
Message: "feat: my new commit \n\nBREAKING CHANGE: change api to v2",
|
||||||
Author: "me",
|
Author: "me",
|
||||||
Hash: "12345668",
|
Hash: "12345668",
|
||||||
},
|
},
|
||||||
@@ -163,6 +191,14 @@ func TestChangelog(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "change api to v2",
|
ParsedBreakingChangeMessage: "change api to v2",
|
||||||
|
IsBreaking: true,
|
||||||
|
Subject: "my new commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{
|
||||||
|
"body": { shared.MessageBlock{
|
||||||
|
Label: "BREAKING CHANGE",
|
||||||
|
Content: "change api to v2",
|
||||||
|
}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Commit: shared.Commit{
|
Commit: shared.Commit{
|
||||||
@@ -176,6 +212,9 @@ func TestChangelog(t *testing.T) {
|
|||||||
TagString: "Features",
|
TagString: "Features",
|
||||||
Print: true,
|
Print: true,
|
||||||
ParsedBreakingChangeMessage: "my next commit",
|
ParsedBreakingChangeMessage: "my next commit",
|
||||||
|
IsBreaking: true,
|
||||||
|
Subject: "my next commit",
|
||||||
|
MessageBlocks: map[string][]shared.MessageBlock{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,13 +37,22 @@ type ChangelogTemplateConfig struct {
|
|||||||
type AnalyzedCommit struct {
|
type AnalyzedCommit struct {
|
||||||
Commit Commit `yaml:"commit"`
|
Commit Commit `yaml:"commit"`
|
||||||
ParsedMessage string `yaml:"parsedMessage"`
|
ParsedMessage string `yaml:"parsedMessage"`
|
||||||
Scope Scope `yaml:"scope"`
|
|
||||||
ParsedBreakingChangeMessage string `yaml:"parsedBreakingChangeMessage"`
|
ParsedBreakingChangeMessage string `yaml:"parsedBreakingChangeMessage"`
|
||||||
Tag string `yaml:"tag"`
|
Tag string `yaml:"tag"`
|
||||||
TagString string `yaml:"tagString"`
|
TagString string `yaml:"tagString"`
|
||||||
|
Scope Scope `yaml:"scope"`
|
||||||
|
Subject string `yaml:"subject"`
|
||||||
|
MessageBlocks map[string][]MessageBlock `yaml:"messageBlocks"`
|
||||||
|
IsBreaking bool `yaml:"isBreaking"`
|
||||||
Print bool `yaml:"print"`
|
Print bool `yaml:"print"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MessageBlock represents a block in the body section of a commit message
|
||||||
|
type MessageBlock struct {
|
||||||
|
Label string `yaml:"label""`
|
||||||
|
Content string `yaml:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
//Scope of the commit, like feat, fix,..
|
//Scope of the commit, like feat, fix,..
|
||||||
type Scope string
|
type Scope string
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ const (
|
|||||||
DefaultTagPrefix = "v"
|
DefaultTagPrefix = "v"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AnalyzerConfig struct
|
||||||
|
type AnalyzerConfig struct {
|
||||||
|
BlockPrefixes []string `yaml:"blockPrefixes"`
|
||||||
|
}
|
||||||
|
|
||||||
// ChangelogConfig struct
|
// ChangelogConfig struct
|
||||||
type ChangelogConfig struct {
|
type ChangelogConfig struct {
|
||||||
PrintAll bool `yaml:"printAll,omitempty"`
|
PrintAll bool `yaml:"printAll,omitempty"`
|
||||||
@@ -83,6 +88,7 @@ type Checksum struct {
|
|||||||
type ReleaseConfig struct {
|
type ReleaseConfig struct {
|
||||||
CommitFormat string `yaml:"commitFormat"`
|
CommitFormat string `yaml:"commitFormat"`
|
||||||
Branch map[string]string `yaml:"branch"`
|
Branch map[string]string `yaml:"branch"`
|
||||||
|
Analyzer AnalyzerConfig `yaml:"analyzer"`
|
||||||
Changelog ChangelogConfig `yaml:"changelog,omitempty"`
|
Changelog ChangelogConfig `yaml:"changelog,omitempty"`
|
||||||
Release string `yaml:"release,omitempty"`
|
Release string `yaml:"release,omitempty"`
|
||||||
GitHubProvider GitHubProvider `yaml:"github,omitempty"`
|
GitHubProvider GitHubProvider `yaml:"github,omitempty"`
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ github:
|
|||||||
Compress: false}},
|
Compress: false}},
|
||||||
ReleaseTitle: "go-semantic-release release",
|
ReleaseTitle: "go-semantic-release release",
|
||||||
IsPreRelease: false,
|
IsPreRelease: false,
|
||||||
|
Analyzer: config.AnalyzerConfig{BlockPrefixes: []string{}},
|
||||||
}, result)
|
}, result)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func New(c *config.ReleaseConfig, repository string, checkConfig bool) (*Semanti
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzer, err := analyzer.New(c.CommitFormat, c.Changelog)
|
analyzer, err := analyzer.New(c.CommitFormat, c.Analyzer, c.Changelog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user