From 00823a324b853af2005ede5374d659869f041d21 Mon Sep 17 00:00:00 2001 From: Vincent Batts Date: Mon, 9 Oct 2017 15:49:59 -0400 Subject: [PATCH] message_regexp: regular expression rule for messages Fixes: #30 now you can add a regular expression to the rules to be run i.e. `git-validation -run "dco,message_regexp='^JIRA-[0-9]+ [A-Z].*$'"` Signed-off-by: Vincent Batts --- README.md | 3 ++ main.go | 17 +++++++-- rules/danglingwhitespace/rule.go | 3 +- rules/dco/dco.go | 3 +- rules/messageregexp/rule.go | 61 ++++++++++++++++++++++++++++++ rules/shortsubject/shortsubject.go | 3 +- validate/rules.go | 7 ++-- validate/rules_test.go | 2 +- 8 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 rules/messageregexp/rule.go diff --git a/README.md b/README.md index 6ecab1d..cbeb355 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ Usage of git-validation: The entire default rule set is run by default: ```console vbatts@valse ~/src/vb/git-validation (master) $ git-validation -list-rules +"dangling-whitespace" -- checking the presence of dangling whitespaces on line endings "DCO" -- makes sure the commits are signed +"message_regexp" -- checks the commit message for a user provided regular expression "short-subject" -- commit subjects are strictly less than 90 (github ellipsis length) ``` @@ -92,6 +94,7 @@ vbatts@valse ~/src/vb/git-validation (master) $ GIT_CHECK_EXCLUDE="./vendor" git ``` using the `GIT_CHECK_EXCLUDE` environment variable + ## Rules Default rules are added by registering them to the `validate` package. diff --git a/main.go b/main.go index 8b6876b..cd5f271 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( _ "github.com/vbatts/git-validation/rules/danglingwhitespace" _ "github.com/vbatts/git-validation/rules/dco" + _ "github.com/vbatts/git-validation/rules/messageregexp" _ "github.com/vbatts/git-validation/rules/shortsubject" "github.com/vbatts/git-validation/validate" ) @@ -47,10 +48,20 @@ func main() { return } - // reduce the set being run - rules := validate.RegisteredRules + // rules to be used + var rules []validate.Rule + for _, r := range validate.RegisteredRules { + // only those that are Default + if r.Default { + rules = append(rules, r) + } + } + // or reduce the set being run to what the user provided if *flRun != "" { - rules = validate.FilterRules(rules, validate.SanitizeFilters(*flRun)) + rules = validate.FilterRules(validate.RegisteredRules, validate.SanitizeFilters(*flRun)) + } + if os.Getenv("DEBUG") != "" { + log.Printf("%#v", rules) // XXX maybe reduce this list } var commitRange = *flCommitRange diff --git a/rules/danglingwhitespace/rule.go b/rules/danglingwhitespace/rule.go index 14ad034..dab3a98 100644 --- a/rules/danglingwhitespace/rule.go +++ b/rules/danglingwhitespace/rule.go @@ -12,6 +12,7 @@ var ( Name: "dangling-whitespace", Description: "checking the presence of dangling whitespaces on line endings", Run: ValidateDanglingWhitespace, + Default: true, } ) @@ -20,7 +21,7 @@ func init() { } // ValidateDanglingWhitespace runs Git's check to look for whitespace errors. -func ValidateDanglingWhitespace(c git.CommitEntry) (vr validate.Result) { +func ValidateDanglingWhitespace(r validate.Rule, c git.CommitEntry) (vr validate.Result) { vr.CommitEntry = c vr.Msg = "commit does not have any whitespace errors" vr.Pass = true diff --git a/rules/dco/dco.go b/rules/dco/dco.go index 6371720..a42ea06 100644 --- a/rules/dco/dco.go +++ b/rules/dco/dco.go @@ -20,11 +20,12 @@ var ( Name: "DCO", Description: "makes sure the commits are signed", Run: ValidateDCO, + Default: true, } ) // ValidateDCO checks that the commit has been signed off, per the DCO process -func ValidateDCO(c git.CommitEntry) (vr validate.Result) { +func ValidateDCO(r validate.Rule, c git.CommitEntry) (vr validate.Result) { vr.CommitEntry = c if len(strings.Split(c["parent"], " ")) > 1 { vr.Pass = true diff --git a/rules/messageregexp/rule.go b/rules/messageregexp/rule.go new file mode 100644 index 0000000..98587f2 --- /dev/null +++ b/rules/messageregexp/rule.go @@ -0,0 +1,61 @@ +package messageregexp + +import ( + "fmt" + "regexp" + "strings" + + "github.com/vbatts/git-validation/git" + "github.com/vbatts/git-validation/validate" +) + +func init() { + validate.RegisterRule(RegexpRule) +} + +var ( + // RegexpRule for validating a user provided regex on the commit messages + RegexpRule = validate.Rule{ + Name: "message_regexp", + Description: "checks the commit message for a user provided regular expression", + Run: ValidateMessageRegexp, + Default: false, // only for users specifically calling it through -run ... + } +) + +// ValidateMessageRegexp is the message regex func to run +func ValidateMessageRegexp(r validate.Rule, c git.CommitEntry) (vr validate.Result) { + if r.Value == "" { + vr.Pass = true + vr.Msg = "noop: message_regexp value is blank" + return vr + } + + re := regexp.MustCompile(r.Value) + vr.CommitEntry = c + if len(strings.Split(c["parent"], " ")) > 1 { + vr.Pass = true + vr.Msg = "merge commits are not checked for message_regexp" + return vr + } + + hasValid := false + for _, line := range strings.Split(c["subject"], "\n") { + if re.MatchString(line) { + hasValid = true + } + } + for _, line := range strings.Split(c["body"], "\n") { + if re.MatchString(line) { + hasValid = true + } + } + if !hasValid { + vr.Pass = false + vr.Msg = fmt.Sprintf("commit message does not match %q", r.Value) + } else { + vr.Pass = true + vr.Msg = fmt.Sprintf("commit message matches %q", r.Value) + } + return vr +} diff --git a/rules/shortsubject/shortsubject.go b/rules/shortsubject/shortsubject.go index f09e884..2aff131 100644 --- a/rules/shortsubject/shortsubject.go +++ b/rules/shortsubject/shortsubject.go @@ -11,6 +11,7 @@ var ( Name: "short-subject", Description: "commit subjects are strictly less than 90 (github ellipsis length)", Run: ValidateShortSubject, + Default: true, } ) @@ -20,7 +21,7 @@ func init() { // ValidateShortSubject checks that the commit's subject is strictly less than // 90 characters (preferably not more than 72 chars). -func ValidateShortSubject(c git.CommitEntry) (vr validate.Result) { +func ValidateShortSubject(r validate.Rule, c git.CommitEntry) (vr validate.Result) { if len(c["subject"]) >= 90 { vr.Pass = false vr.Msg = "commit subject exceeds 90 characters" diff --git a/validate/rules.go b/validate/rules.go index d08ecfd..38126a4 100644 --- a/validate/rules.go +++ b/validate/rules.go @@ -9,7 +9,7 @@ import ( ) var ( - // RegisteredRules are the standard validation to perform on git commits + // RegisteredRules are the avaible validation to perform on git commits RegisteredRules = []Rule{} registerRuleLock = sync.Mutex{} ) @@ -26,14 +26,15 @@ type Rule struct { Name string // short name for reference in in the `-run=...` flag Value string // value to configure for the rule (i.e. a regexp to check for in the commit message) Description string // longer Description for readability - Run func(git.CommitEntry) Result + Run func(Rule, git.CommitEntry) Result + Default bool // whether the registered rule is run by default } // Commit processes the given rules on the provided commit, and returns the result set. func Commit(c git.CommitEntry, rules []Rule) Results { results := Results{} for _, r := range rules { - results = append(results, r.Run(c)) + results = append(results, r.Run(r, c)) } return results } diff --git a/validate/rules_test.go b/validate/rules_test.go index 290973f..e9fe3f9 100644 --- a/validate/rules_test.go +++ b/validate/rules_test.go @@ -46,7 +46,7 @@ func TestSliceHelpers(t *testing.T) { for i := range set { got := StringsSliceEqual(set[i].A, set[i].B) if got != set[i].Equal { - t.Errorf("expected %d A and B comparison to be %q, but got %q", i, set[i].Equal, got) + t.Errorf("expected %d A and B comparison to be %t, but got %t", i, set[i].Equal, got) } } }