feat(angular): update angular to include new structured fields

This commit is contained in:
maulik13
2021-02-22 12:02:39 +01:00
committed by Felix Wiedmann
parent a20992af14
commit 7208daed1f
4 changed files with 107 additions and 80 deletions

View File

@@ -2,6 +2,7 @@
package analyzer package analyzer
import ( import (
"bufio"
"fmt" "fmt"
"regexp" "regexp"
"strings" "strings"
@@ -90,21 +91,6 @@ func (a *Analyzer) Analyze(commits []shared.Commit) map[shared.Release][]shared.
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
}
// //
// getRegexMatchedMap will match a regex with named groups and map the matching // getRegexMatchedMap will match a regex with named groups and map the matching
// results to corresponding group names // results to corresponding group names
@@ -162,3 +148,56 @@ func findFooterToken(text string, separators []string) (token string, sep string
} }
return "", "" return "", ""
} }
//
// getDefaultMessageBlockMap parses a text block and splits in to different sections.
// default logic to distinguish different parts is:
// - Body starts right after the header (without beginning with a token)
// - Body ends when a footer is discovered or text ends
// - A footer is detected when it starts with a token ending with a separator
// - A footer ends when another footer is found or text ends
//
func getDefaultMessageBlockMap(txtBlock string, tokenSep []string) map[string][]shared.MessageBlock{
msgBlockMap := make(map[string][]shared.MessageBlock)
footers := make([]string, 0)
body, footerBlock, line := "", "", ""
footerFound := false
// Look through each line
scanner := bufio.NewScanner(strings.NewReader(txtBlock))
for scanner.Scan() {
line = scanner.Text()
if token, _ := findFooterToken(line, tokenSep); len(token) > 0 {
// if footer was already found from before
if len(footerBlock) > 0{
footers = append(footers, strings.TrimSpace(footerBlock))
}
footerFound = true
footerBlock = ""
}
//'\n' is removed when reading from scanner
if !footerFound {
body += line + "\n"
}else{
footerBlock += line + "\n"
}
}
if len(footerBlock) > 0 {
footers = append(footers, strings.TrimSpace(footerBlock))
}
body = strings.TrimSpace(body)
if len(body) > 0{
msgBlockMap["body"] = []shared.MessageBlock {{
Label: "",
Content: body,
} }
}
footerBlocks := getMessageBlocksFromTexts(footers, tokenSep)
if len(footerBlocks) > 0 {
msgBlockMap["footer"] = footerBlocks
}
return msgBlockMap
}

View File

@@ -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,16 @@ type angular struct {
rules []Rule rules []Rule
regex string regex string
log *log.Entry log *log.Entry
config config.AnalyzerConfig
} }
// ANGULAR identifier // ANGULAR identifier
const ANGULAR = "angular" const ANGULAR = "angular"
var angularFooterTokenSep = defaultTokenSeparators
func newAngular() *angular { func newAngular() *angular {
return &angular{ return &angular{
regex: `^(TAG)(?:\((.*)\))?: (?s)(.*)`, regex: `^(?P<type>\w*)(?:\((?P<scope>.*)\))?: (?P<subject>.*)`,
log: log.WithField("analyzer", ANGULAR), log: log.WithField("analyzer", ANGULAR),
rules: []Rule{ rules: []Rule{
{ {
@@ -87,39 +89,51 @@ func (a *angular) getRules() []Rule {
} }
func (a *angular) analyze(commit shared.Commit, rule Rule) *shared.AnalyzedCommit { func (a *angular) analyze(commit shared.Commit, rule Rule) *shared.AnalyzedCommit {
re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1)) tokenSep := append(a.config.TokenSeparators, conventionalFooterTokenSep[:]...)
matches := re.FindStringSubmatch(commit.Message)
if matches == nil { firstSplit := strings.SplitN(commit.Message, "\n", 2)
header := firstSplit[0]
body := ""
if len(firstSplit) > 1 {
body = firstSplit[1]
}
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 return nil
} }
msgBlockMap := getDefaultMessageBlockMap(body, tokenSep)
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[3:], "") isBreaking := strings.Contains(commit.Message, defaultBreakingChangePrefix)
if !strings.Contains(message, "BREAKING CHANGE:") { analyzed.IsBreaking = isBreaking
analyzed.ParsedMessage = strings.Trim(message, " ")
oldFormatMessage := strings.TrimSpace(matches["subject"] + "\n" + body)
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 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(oldFormatMessage, defaultBreakingChangePrefix, 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])
} else {
return analyzed analyzed.ParsedBreakingChangeMessage = breakingChange[0]
} }
analyzed.ParsedBreakingChangeMessage = breakingChange[0]
return analyzed return analyzed
} }

View File

@@ -31,6 +31,8 @@ func TestAngular(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": {},
@@ -60,6 +62,8 @@ func TestAngular(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": {
@@ -76,6 +80,8 @@ func TestAngular(t *testing.T) {
Print: true, Print: true,
ParsedBreakingChangeMessage: "change api to v2", ParsedBreakingChangeMessage: "change api to v2",
IsBreaking: true, IsBreaking: true,
Subject: "my first break BREAKING CHANGE: change api to v2",
MessageBlocks: map[string][]shared.MessageBlock{},
}, },
}, },
"patch": {}, "patch": {},
@@ -121,6 +127,8 @@ func TestAngular(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": {
@@ -137,6 +145,15 @@ func TestAngular(t *testing.T) {
Print: true, Print: true,
ParsedBreakingChangeMessage: "change api to v2", ParsedBreakingChangeMessage: "change api to v2",
IsBreaking: true, IsBreaking: true,
Subject: "my first break",
MessageBlocks: map[string][]shared.MessageBlock{
"footer": {
shared.MessageBlock{
Label: "BREAKING CHANGE",
Content: "change api to v2",
},
},
},
}, },
}, },
"patch": {}, "patch": {},
@@ -179,6 +196,8 @@ func TestAngular(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": {
@@ -194,6 +213,8 @@ func TestAngular(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": {},

View File

@@ -2,7 +2,6 @@
package analyzer package analyzer
import ( import (
"bufio"
"github.com/Nightapes/go-semantic-release/pkg/config" "github.com/Nightapes/go-semantic-release/pkg/config"
"strings" "strings"
@@ -107,7 +106,7 @@ func (a *conventional) analyze(commit shared.Commit, rule Rule) *shared.Analyzed
return nil return nil
} }
msgBlockMap := getConventionalMessageBlockMap(body, tokenSep) msgBlockMap := getDefaultMessageBlockMap(body, tokenSep)
analyzed := &shared.AnalyzedCommit{ analyzed := &shared.AnalyzedCommit{
Commit: commit, Commit: commit,
@@ -122,6 +121,7 @@ func (a *conventional) analyze(commit shared.Commit, rule Rule) *shared.Analyzed
analyzed.IsBreaking = isBreaking analyzed.IsBreaking = isBreaking
oldFormatMessage := strings.TrimSpace(matches["subject"] + "\n" + body) oldFormatMessage := strings.TrimSpace(matches["subject"] + "\n" + body)
if !isBreaking { if !isBreaking {
analyzed.ParsedMessage = strings.Trim(oldFormatMessage, " ") 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)
@@ -141,50 +141,3 @@ func (a *conventional) analyze(commit shared.Commit, rule Rule) *shared.Analyzed
return analyzed return analyzed
} }
func getConventionalMessageBlockMap(txtBlock string, tokenSep []string) map[string][]shared.MessageBlock{
msgBlockMap := make(map[string][]shared.MessageBlock)
footers := make([]string, 0)
body := ""
footerBlock := ""
line := ""
footerFound := false
// Look through each line
scanner := bufio.NewScanner(strings.NewReader(txtBlock))
for scanner.Scan() {
line = scanner.Text()
if token, _ := findFooterToken(line, tokenSep); len(token) > 0 {
// if footer was already found from before
if len(footerBlock) > 0{
footers = append(footers, strings.TrimSpace(footerBlock))
}
footerFound = true
footerBlock = ""
}
//'\n' is removed when reading from scanner
if !footerFound {
body += line + "\n"
}else{
footerBlock += line + "\n"
}
}
if len(footerBlock) > 0 {
footers = append(footers, strings.TrimSpace(footerBlock))
}
body = strings.TrimSpace(body)
if len(body) > 0{
msgBlockMap["body"] = []shared.MessageBlock {{
Label: "",
Content: body,
} }
}
footerBlocks := getMessageBlocksFromTexts(footers, tokenSep)
if len(footerBlocks) > 0 {
msgBlockMap["footer"] = footerBlocks
}
return msgBlockMap
}