mirror of
https://github.com/thegeeklab/git-sv.git
synced 2024-11-24 21:20:40 +00:00
refactor: create branches config
BREAKING CHANGE: remove BRANCH_ISSUE_REGEX varenv
This commit is contained in:
parent
0e7438b3a9
commit
f6debee45e
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
15
sv/config.go
15
sv/config.go
@ -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
|
||||||
|
}
|
||||||
|
@ -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 != "" {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user