diff --git a/README.md b/README.md index 4f25f2f..0065e93 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ### Installing -- Download the latest release and add the binary on your path +- Download the latest release and add the binary to your path - Optional: Set `SV4GIT_HOME` to define user configs, check [config](#config) for more information. ### Config @@ -117,7 +117,7 @@ commit-message: # don't forget to add "" on your list if you need to define scopes and keep it optional values: [] footer: - issue: + issue: # use "issue: {}" if you wish to disable issue footer key: jira # name used to define an issue on footer metadata key-synonyms: # supported variations for footer metadata - Jira diff --git a/cmd/git-sv/handlers.go b/cmd/git-sv/handlers.go index efe94e2..e1d062d 100644 --- a/cmd/git-sv/handlers.go +++ b/cmd/git-sv/handlers.go @@ -298,9 +298,13 @@ func commitHandler(cfg Config, git sv.Git, messageProcessor sv.MessageProcessor) if err != nil { return err } - issue, err := promptIssueID(cfg.CommitMessage.IssueFooterConfig().Key, cfg.CommitMessage.Issue.Regex, branchIssue) - if err != nil { - return err + + var issue string + if cfg.CommitMessage.IssueFooterConfig().Key != "" && cfg.CommitMessage.Issue.Regex != "" { + issue, err = promptIssueID("issue id", cfg.CommitMessage.Issue.Regex, branchIssue) + if err != nil { + return err + } } hasBreakingChanges, err := promptConfirm("has breaking changes?") diff --git a/sv/message.go b/sv/message.go index 53fc2bc..b057a34 100644 --- a/sv/message.go +++ b/sv/message.go @@ -119,6 +119,10 @@ func (p MessageProcessorImpl) Enhance(branch string, message string) (string, er // IssueID try to extract issue id from branch, return empty if not found. func (p MessageProcessorImpl) IssueID(branch string) (string, error) { + if p.branchesCfg.DisableIssue || p.messageCfg.Issue.Regex == "" { + return "", nil + } + rstr := fmt.Sprintf("^%s(%s)%s$", p.branchesCfg.PrefixRegex, p.messageCfg.Issue.Regex, p.branchesCfg.SuffixRegex) r, err := regexp.Compile(rstr) if err != nil { @@ -146,7 +150,7 @@ func (p MessageProcessorImpl) Format(msg CommitMessage) (string, string, string) if msg.BreakingMessage() != "" { footer.WriteString(fmt.Sprintf("%s: %s", breakingChangeFooterKey, msg.BreakingMessage())) } - if issue, exists := msg.Metadata[issueMetadataKey]; exists { + if issue, exists := msg.Metadata[issueMetadataKey]; exists && p.messageCfg.IssueFooterConfig().Key != "" { if footer.Len() > 0 { footer.WriteString("\n") } @@ -166,11 +170,13 @@ func (p MessageProcessorImpl) Parse(subject, body string) CommitMessage { metadata := make(map[string]string) for key, mdCfg := range p.messageCfg.Footer { - prefixes := append([]string{mdCfg.Key}, mdCfg.KeySynonyms...) - for _, prefix := range prefixes { - if tagValue := extractFooterMetadata(prefix, body, mdCfg.UseHash); tagValue != "" { - metadata[key] = tagValue - break + if mdCfg.Key != "" { + prefixes := append([]string{mdCfg.Key}, mdCfg.KeySynonyms...) + for _, prefix := range prefixes { + if tagValue := extractFooterMetadata(prefix, body, mdCfg.UseHash); tagValue != "" { + metadata[key] = tagValue + break + } } } } diff --git a/sv/message_test.go b/sv/message_test.go index 7a5f5a2..ee78c8e 100644 --- a/sv/message_test.go +++ b/sv/message_test.go @@ -15,6 +15,15 @@ var ccfg = CommitMessageConfig{ Issue: CommitMessageIssueConfig{Regex: "[A-Z]+-[0-9]+"}, } +var ccfgEmptyIssue = CommitMessageConfig{ + Types: []string{"feat", "fix"}, + Scope: CommitMessageScopeConfig{}, + Footer: map[string]CommitMessageFooterConfig{ + "issue": {}, + }, + Issue: CommitMessageIssueConfig{Regex: "[A-Z]+-[0-9]+"}, +} + var ccfgWithScope = CommitMessageConfig{ Types: []string{"feat", "fix"}, Scope: CommitMessageScopeConfig{Values: []string{"", "scope"}}, @@ -203,6 +212,7 @@ jira: JIRA-123` func Test_hasIssueID(t *testing.T) { cfgColon := CommitMessageFooterConfig{Key: "jira"} cfgHash := CommitMessageFooterConfig{Key: "jira", UseHash: true} + cfgEmpty := CommitMessageFooterConfig{} tests := []struct { name string @@ -223,6 +233,9 @@ jira: JIRA-123`, cfgColon, true}, {"multi line with issue and hash", `feat: something jira #JIRA-123`, cfgHash, true}, + {"empty config", `feat: something + +jira #JIRA-123`, cfgEmpty, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -275,26 +288,26 @@ Jira: JIRA-999 Refs #123` func TestMessageProcessorImpl_Parse(t *testing.T) { - p := NewMessageProcessor(ccfg, newBranchCfg(false)) - tests := []struct { name string + cfg CommitMessageConfig subject string body string want CommitMessage }{ - {"simple message", "feat: something awesome", "", CommitMessage{Type: "feat", Scope: "", Description: "something awesome", Body: "", IsBreakingChange: false, Metadata: map[string]string{}}}, - {"message with scope", "feat(scope): something awesome", "", CommitMessage{Type: "feat", Scope: "scope", Description: "something awesome", Body: "", IsBreakingChange: false, Metadata: map[string]string{}}}, - {"unmapped type", "unkn: something unknown", "", CommitMessage{Type: "unkn", Scope: "", Description: "something unknown", Body: "", IsBreakingChange: false, Metadata: map[string]string{}}}, - {"jira and breaking change metadata", "feat: something new", completeBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: completeBody, IsBreakingChange: true, Metadata: map[string]string{issueMetadataKey: "JIRA-123", breakingChangeMetadataKey: "this change breaks everything"}}}, - {"jira only metadata", "feat: something new", issueOnlyBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: issueOnlyBody, IsBreakingChange: false, Metadata: map[string]string{issueMetadataKey: "JIRA-456"}}}, - {"jira synonyms metadata", "feat: something new", issueSynonymsBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: issueSynonymsBody, IsBreakingChange: false, Metadata: map[string]string{issueMetadataKey: "JIRA-789"}}}, - {"breaking change with exclamation mark", "feat!: something new", "", CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: "", IsBreakingChange: true, Metadata: map[string]string{}}}, - {"hash metadata", "feat: something new", hashMetadataBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: hashMetadataBody, IsBreakingChange: false, Metadata: map[string]string{issueMetadataKey: "JIRA-999", "refs": "#123"}}}, + {"simple message", ccfg, "feat: something awesome", "", CommitMessage{Type: "feat", Scope: "", Description: "something awesome", Body: "", IsBreakingChange: false, Metadata: map[string]string{}}}, + {"message with scope", ccfg, "feat(scope): something awesome", "", CommitMessage{Type: "feat", Scope: "scope", Description: "something awesome", Body: "", IsBreakingChange: false, Metadata: map[string]string{}}}, + {"unmapped type", ccfg, "unkn: something unknown", "", CommitMessage{Type: "unkn", Scope: "", Description: "something unknown", Body: "", IsBreakingChange: false, Metadata: map[string]string{}}}, + {"jira and breaking change metadata", ccfg, "feat: something new", completeBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: completeBody, IsBreakingChange: true, Metadata: map[string]string{issueMetadataKey: "JIRA-123", breakingChangeMetadataKey: "this change breaks everything"}}}, + {"jira only metadata", ccfg, "feat: something new", issueOnlyBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: issueOnlyBody, IsBreakingChange: false, Metadata: map[string]string{issueMetadataKey: "JIRA-456"}}}, + {"jira synonyms metadata", ccfg, "feat: something new", issueSynonymsBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: issueSynonymsBody, IsBreakingChange: false, Metadata: map[string]string{issueMetadataKey: "JIRA-789"}}}, + {"breaking change with exclamation mark", ccfg, "feat!: something new", "", CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: "", IsBreakingChange: true, Metadata: map[string]string{}}}, + {"hash metadata", ccfg, "feat: something new", hashMetadataBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: hashMetadataBody, IsBreakingChange: false, Metadata: map[string]string{issueMetadataKey: "JIRA-999", "refs": "#123"}}}, + {"empty issue cfg", ccfgEmptyIssue, "feat: something new", hashMetadataBody, CommitMessage{Type: "feat", Scope: "", Description: "something new", Body: hashMetadataBody, IsBreakingChange: false, Metadata: map[string]string{}}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := p.Parse(tt.subject, tt.body); !reflect.DeepEqual(got, tt.want) { + if got := NewMessageProcessor(tt.cfg, newBranchCfg(false)).Parse(tt.subject, tt.body); !reflect.DeepEqual(got, tt.want) { t.Errorf("MessageProcessorImpl.Parse() = %v, want %v", got, tt.want) } }) @@ -302,26 +315,26 @@ func TestMessageProcessorImpl_Parse(t *testing.T) { } func TestMessageProcessorImpl_Format(t *testing.T) { - p := NewMessageProcessor(ccfg, newBranchCfg(false)) - tests := []struct { name string + cfg CommitMessageConfig msg CommitMessage wantHeader string wantBody string wantFooter string }{ - {"simple message", NewCommitMessage("feat", "", "something", "", "", ""), "feat: something", "", ""}, - {"with issue", NewCommitMessage("feat", "", "something", "", "JIRA-123", ""), "feat: something", "", "jira: JIRA-123"}, - {"with breaking change", NewCommitMessage("feat", "", "something", "", "", "breaks"), "feat: something", "", "BREAKING CHANGE: breaks"}, - {"with scope", NewCommitMessage("feat", "scope", "something", "", "", ""), "feat(scope): something", "", ""}, - {"with body", NewCommitMessage("feat", "", "something", "body", "", ""), "feat: something", "body", ""}, - {"with multiline body", NewCommitMessage("feat", "", "something", multilineBody, "", ""), "feat: something", multilineBody, ""}, - {"full message", NewCommitMessage("feat", "scope", "something", multilineBody, "JIRA-123", "breaks"), "feat(scope): something", multilineBody, fullFooter}, + {"simple message", ccfg, NewCommitMessage("feat", "", "something", "", "", ""), "feat: something", "", ""}, + {"with issue", ccfg, NewCommitMessage("feat", "", "something", "", "JIRA-123", ""), "feat: something", "", "jira: JIRA-123"}, + {"with breaking change", ccfg, NewCommitMessage("feat", "", "something", "", "", "breaks"), "feat: something", "", "BREAKING CHANGE: breaks"}, + {"with scope", ccfg, NewCommitMessage("feat", "scope", "something", "", "", ""), "feat(scope): something", "", ""}, + {"with body", ccfg, NewCommitMessage("feat", "", "something", "body", "", ""), "feat: something", "body", ""}, + {"with multiline body", ccfg, NewCommitMessage("feat", "", "something", multilineBody, "", ""), "feat: something", multilineBody, ""}, + {"full message", ccfg, NewCommitMessage("feat", "scope", "something", multilineBody, "JIRA-123", "breaks"), "feat(scope): something", multilineBody, fullFooter}, + {"config without issue key", ccfgEmptyIssue, NewCommitMessage("feat", "", "something", "", "JIRA-123", ""), "feat: something", "", ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, got1, got2 := p.Format(tt.msg) + got, got1, got2 := NewMessageProcessor(tt.cfg, newBranchCfg(false)).Format(tt.msg) if got != tt.wantHeader { t.Errorf("MessageProcessorImpl.Format() header got = %v, want %v", got, tt.wantHeader) }