0
0
mirror of https://github.com/thegeeklab/git-sv.git synced 2024-11-12 15:00:39 +00:00

refactor: create branches config

BREAKING CHANGE: remove BRANCH_ISSUE_REGEX varenv
This commit is contained in:
Beatriz Vieira 2021-02-14 01:48:11 -03:00
parent 0e7438b3a9
commit f6debee45e
5 changed files with 56 additions and 33 deletions

View File

@ -20,7 +20,8 @@ type Config struct {
CommitMessageTypes []string `envconfig:"COMMIT_MESSAGE_TYPES" default:"build,ci,chore,docs,feat,fix,perf,refactor,revert,style,test"` CommitMessageTypes []string `envconfig:"COMMIT_MESSAGE_TYPES" default:"build,ci,chore,docs,feat,fix,perf,refactor,revert,style,test"`
IssueKeyName string `envconfig:"ISSUE_KEY_NAME" default:"jira"` IssueKeyName string `envconfig:"ISSUE_KEY_NAME" default:"jira"`
IssueRegex string `envconfig:"ISSUE_REGEX" default:"[A-Z]+-[0-9]+"` IssueRegex string `envconfig:"ISSUE_REGEX" default:"[A-Z]+-[0-9]+"`
BranchIssueRegex string `envconfig:"BRANCH_ISSUE_REGEX" default:"^([a-z]+\\/)?([A-Z]+-[0-9]+)(-.*)?"` //TODO breaking change: use issue regex instead of duplicating issue regex BranchIssuePrefixRegex string `envconfig:"BRANCH_ISSUE_PREFIX_REGEX" default:"([a-z]+\\/)?"`
BranchIssueSuffixRegex string `envconfig:"BRANCH_ISSUE_SUFFIX_REGEX" default:"(-.*)?"`
} }
func loadConfig() Config { func loadConfig() Config {

View File

@ -21,13 +21,20 @@ func main() {
Types: cfg.CommitMessageTypes, Types: cfg.CommitMessageTypes,
Scope: sv.CommitMessageScopeConfig{}, Scope: sv.CommitMessageScopeConfig{},
Footer: map[string]sv.CommitMessageFooterConfig{ Footer: map[string]sv.CommitMessageFooterConfig{
"issue": {Key: cfg.IssueIDPrefixes[0], KeySynonyms: cfg.IssueIDPrefixes[1:], Regex: cfg.IssueRegex}, "issue": {Key: cfg.IssueIDPrefixes[0], KeySynonyms: cfg.IssueIDPrefixes[1:]},
"breaking-change": {Key: cfg.BreakingChangePrefixes[0], KeySynonyms: cfg.BreakingChangePrefixes[1:]}, "breaking-change": {Key: cfg.BreakingChangePrefixes[0], KeySynonyms: cfg.BreakingChangePrefixes[1:]},
}, },
Issue: sv.CommitMessageIssueConfig{Regex: cfg.IssueRegex},
}
branchesConfig := sv.BranchesConfig{
Skip: cfg.ValidateMessageSkipBranches,
ExpectIssue: true,
PrefixRegex: cfg.BranchIssuePrefixRegex,
SuffixRegex: cfg.BranchIssueSuffixRegex,
} }
//// ////
messageProcessor := sv.NewMessageProcessor(commitMessageCfg, cfg.ValidateMessageSkipBranches, cfg.BranchIssueRegex) messageProcessor := sv.NewMessageProcessor(commitMessageCfg, branchesConfig)
git := sv.NewGit(messageProcessor, cfg.TagPattern) git := sv.NewGit(messageProcessor, cfg.TagPattern)
semverProcessor := sv.NewSemVerCommitsProcessor(cfg.IncludeUnknownTypeAsPatch, cfg.MajorVersionTypes, cfg.MinorVersionTypes, cfg.PatchVersionTypes) semverProcessor := sv.NewSemVerCommitsProcessor(cfg.IncludeUnknownTypeAsPatch, cfg.MajorVersionTypes, cfg.MinorVersionTypes, cfg.PatchVersionTypes)
releasenotesProcessor := sv.NewReleaseNoteProcessor(cfg.ReleaseNotesTags) releasenotesProcessor := sv.NewReleaseNoteProcessor(cfg.ReleaseNotesTags)

View File

@ -5,6 +5,7 @@ type CommitMessageConfig struct {
Types []string Types []string
Scope CommitMessageScopeConfig Scope CommitMessageScopeConfig
Footer map[string]CommitMessageFooterConfig Footer map[string]CommitMessageFooterConfig
Issue CommitMessageIssueConfig
} }
// IssueConfig config for issue. // IssueConfig config for issue.
@ -33,6 +34,18 @@ type CommitMessageScopeConfig struct {
type CommitMessageFooterConfig struct { type CommitMessageFooterConfig struct {
Key string Key string
KeySynonyms []string KeySynonyms []string
Regex string
UseHash bool UseHash bool
} }
// CommitMessageIssueConfig issue preferences.
type CommitMessageIssueConfig struct {
Regex string
}
// BranchesConfig branches preferences.
type BranchesConfig struct {
PrefixRegex string
SuffixRegex string
ExpectIssue bool
Skip []string
}

View File

@ -56,41 +56,39 @@ type MessageProcessor interface {
} }
// NewMessageProcessor MessageProcessorImpl constructor // NewMessageProcessor MessageProcessorImpl constructor
func NewMessageProcessor(cfg CommitMessageConfig, skipBranches []string, branchIssueRegex string) *MessageProcessorImpl { func NewMessageProcessor(mcfg CommitMessageConfig, bcfg BranchesConfig) *MessageProcessorImpl {
return &MessageProcessorImpl{ return &MessageProcessorImpl{
cfg: cfg, messageCfg: mcfg,
skipBranches: skipBranches, branchesCfg: bcfg,
branchIssueRegex: branchIssueRegex,
} }
} }
// MessageProcessorImpl process validate message hook. // MessageProcessorImpl process validate message hook.
type MessageProcessorImpl struct { type MessageProcessorImpl struct {
cfg CommitMessageConfig messageCfg CommitMessageConfig
skipBranches []string branchesCfg BranchesConfig
branchIssueRegex string
} }
// SkipBranch check if branch should be ignored. // SkipBranch check if branch should be ignored.
func (p MessageProcessorImpl) SkipBranch(branch string) bool { func (p MessageProcessorImpl) SkipBranch(branch string) bool {
return contains(branch, p.skipBranches) return contains(branch, p.branchesCfg.Skip)
} }
// Validate commit message. // Validate commit message.
func (p MessageProcessorImpl) Validate(message string) error { func (p MessageProcessorImpl) Validate(message string) error {
valid, err := regexp.MatchString("^("+strings.Join(p.cfg.Types, "|")+")(\\(.+\\))?!?: .*$", firstLine(message)) valid, err := regexp.MatchString("^("+strings.Join(p.messageCfg.Types, "|")+")(\\(.+\\))?!?: .*$", firstLine(message))
if err != nil { if err != nil {
return err return err
} }
if !valid { if !valid {
return fmt.Errorf("message should contain type: %v, and should be valid according with conventional commits", p.cfg.Types) return fmt.Errorf("message should contain type: %v, and should be valid according with conventional commits", p.messageCfg.Types)
} }
return nil return nil
} }
// Enhance add metadata on commit message. // Enhance add metadata on commit message.
func (p MessageProcessorImpl) Enhance(branch string, message string) (string, error) { func (p MessageProcessorImpl) Enhance(branch string, message string) (string, error) {
if p.branchIssueRegex == "" || p.cfg.IssueConfig().Key == "" || hasIssueID(message, p.cfg.IssueConfig().Key) { if !p.branchesCfg.ExpectIssue || p.messageCfg.IssueConfig().Key == "" || hasIssueID(message, p.messageCfg.IssueConfig().Key) {
return "", nil //enhance disabled return "", nil //enhance disabled
} }
@ -102,9 +100,9 @@ func (p MessageProcessorImpl) Enhance(branch string, message string) (string, er
return "", fmt.Errorf("could not find issue id using configured regex") return "", fmt.Errorf("could not find issue id using configured regex")
} }
footer := fmt.Sprintf("%s: %s", p.cfg.IssueConfig().Key, issue) footer := fmt.Sprintf("%s: %s", p.messageCfg.IssueConfig().Key, issue)
if !hasFooter(message, p.cfg.Footer[breakingKey].Key) { if !hasFooter(message, p.messageCfg.Footer[breakingKey].Key) {
return "\n" + footer, nil return "\n" + footer, nil
} }
@ -113,9 +111,10 @@ func (p MessageProcessorImpl) Enhance(branch string, message string) (string, er
// IssueID try to extract issue id from branch, return empty if not found. // IssueID try to extract issue id from branch, return empty if not found.
func (p MessageProcessorImpl) IssueID(branch string) (string, error) { func (p MessageProcessorImpl) IssueID(branch string) (string, error) {
r, err := regexp.Compile(p.branchIssueRegex) rstr := fmt.Sprintf("^%s(%s)%s$", p.branchesCfg.PrefixRegex, p.messageCfg.Issue.Regex, p.branchesCfg.SuffixRegex)
r, err := regexp.Compile(rstr)
if err != nil { if err != nil {
return "", fmt.Errorf("could not compile issue regex: %s, error: %v", p.branchIssueRegex, err.Error()) return "", fmt.Errorf("could not compile issue regex: %s, error: %v", rstr, err.Error())
} }
groups := r.FindStringSubmatch(branch) groups := r.FindStringSubmatch(branch)
@ -137,13 +136,13 @@ func (p MessageProcessorImpl) Format(msg CommitMessage) (string, string, string)
var footer strings.Builder var footer strings.Builder
if msg.BreakingMessage() != "" { if msg.BreakingMessage() != "" {
footer.WriteString(fmt.Sprintf("%s: %s", p.cfg.BreakingChangeConfig().Key, msg.BreakingMessage())) footer.WriteString(fmt.Sprintf("%s: %s", p.messageCfg.BreakingChangeConfig().Key, msg.BreakingMessage()))
} }
if issue, exists := msg.Metadata[issueKey]; exists { if issue, exists := msg.Metadata[issueKey]; exists {
if footer.Len() > 0 { if footer.Len() > 0 {
footer.WriteString("\n") footer.WriteString("\n")
} }
footer.WriteString(fmt.Sprintf("%s: %s", p.cfg.IssueConfig().Key, issue)) footer.WriteString(fmt.Sprintf("%s: %s", p.messageCfg.IssueConfig().Key, issue))
} }
return header.String(), msg.Body, footer.String() return header.String(), msg.Body, footer.String()
@ -154,7 +153,7 @@ func (p MessageProcessorImpl) Parse(subject, body string) CommitMessage {
commitType, scope, description, hasBreakingChange := parseSubjectMessage(subject) commitType, scope, description, hasBreakingChange := parseSubjectMessage(subject)
metadata := make(map[string]string) metadata := make(map[string]string)
for key, mdCfg := range p.cfg.Footer { for key, mdCfg := range p.messageCfg.Footer {
prefixes := append([]string{mdCfg.Key}, mdCfg.KeySynonyms...) prefixes := append([]string{mdCfg.Key}, mdCfg.KeySynonyms...)
for _, prefix := range prefixes { for _, prefix := range prefixes {
if tagValue := extractFooterMetadata(prefix, body, mdCfg.UseHash); tagValue != "" { if tagValue := extractFooterMetadata(prefix, body, mdCfg.UseHash); tagValue != "" {

View File

@ -5,20 +5,23 @@ import (
"testing" "testing"
) )
var cfg = CommitMessageConfig{ var ccfg = CommitMessageConfig{
Types: []string{"feat", "fix"}, Types: []string{"feat", "fix"},
Scope: CommitMessageScopeConfig{}, Scope: CommitMessageScopeConfig{},
Footer: map[string]CommitMessageFooterConfig{ Footer: map[string]CommitMessageFooterConfig{
"issue": {Key: "jira", KeySynonyms: []string{"Jira"}, Regex: "[A-Z]+-[0-9]+"}, "issue": {Key: "jira", KeySynonyms: []string{"Jira"}},
"breaking-change": {Key: "BREAKING CHANGE", KeySynonyms: []string{"BREAKING CHANGES"}}, "breaking-change": {Key: "BREAKING CHANGE", KeySynonyms: []string{"BREAKING CHANGES"}},
"refs": {Key: "Refs", UseHash: true}, "refs": {Key: "Refs", UseHash: true},
}, },
Issue: CommitMessageIssueConfig{Regex: "[A-Z]+-[0-9]+"},
} }
const ( var bcfg = BranchesConfig{
branchIssueRegex = "^([a-z]+\\/)?([A-Z]+-[0-9]+)(-.*)?" ExpectIssue: true,
issueRegex = "[A-Z]+-[0-9]+" PrefixRegex: "([a-z]+\\/)?",
) SuffixRegex: "(-.*)?",
Skip: []string{"develop", "master"},
}
// messages samples start // messages samples start
var fullMessage = `fix: correct minor typos in code var fullMessage = `fix: correct minor typos in code
@ -57,7 +60,7 @@ BREAKING CHANGE: refactor to use JavaScript features not available in Node 6.`
// multiline samples end // multiline samples end
func TestMessageProcessorImpl_Validate(t *testing.T) { func TestMessageProcessorImpl_Validate(t *testing.T) {
p := NewMessageProcessor(cfg, []string{"develop", "master"}, branchIssueRegex) p := NewMessageProcessor(ccfg, bcfg)
tests := []struct { tests := []struct {
name string name string
@ -90,7 +93,7 @@ func TestMessageProcessorImpl_Validate(t *testing.T) {
} }
func TestMessageProcessorImpl_Enhance(t *testing.T) { func TestMessageProcessorImpl_Enhance(t *testing.T) {
p := NewMessageProcessor(cfg, []string{"develop", "master"}, branchIssueRegex) p := NewMessageProcessor(ccfg, bcfg)
tests := []struct { tests := []struct {
name string name string
@ -123,7 +126,7 @@ func TestMessageProcessorImpl_Enhance(t *testing.T) {
} }
func TestMessageProcessorImpl_IssueID(t *testing.T) { func TestMessageProcessorImpl_IssueID(t *testing.T) {
p := NewMessageProcessor(cfg, []string{"develop", "master"}, branchIssueRegex) p := NewMessageProcessor(ccfg, bcfg)
tests := []struct { tests := []struct {
name string name string
@ -251,7 +254,7 @@ Jira: JIRA-999
Refs #123` Refs #123`
func TestMessageProcessorImpl_Parse(t *testing.T) { func TestMessageProcessorImpl_Parse(t *testing.T) {
p := NewMessageProcessor(cfg, []string{"develop", "master"}, branchIssueRegex) p := NewMessageProcessor(ccfg, bcfg)
tests := []struct { tests := []struct {
name string name string
@ -278,7 +281,7 @@ func TestMessageProcessorImpl_Parse(t *testing.T) {
} }
func TestMessageProcessorImpl_Format(t *testing.T) { func TestMessageProcessorImpl_Format(t *testing.T) {
p := NewMessageProcessor(cfg, []string{"develop", "master"}, branchIssueRegex) p := NewMessageProcessor(ccfg, bcfg)
tests := []struct { tests := []struct {
name string name string