2015-10-05 20:16:52 +00:00
|
|
|
package validate
|
|
|
|
|
2015-10-05 22:29:24 +00:00
|
|
|
import (
|
2017-10-09 17:21:23 +00:00
|
|
|
"sort"
|
2015-10-05 22:29:24 +00:00
|
|
|
"strings"
|
2017-10-09 17:21:23 +00:00
|
|
|
"sync"
|
2015-10-05 22:29:24 +00:00
|
|
|
|
|
|
|
"github.com/vbatts/git-validation/git"
|
|
|
|
)
|
2015-10-05 20:16:52 +00:00
|
|
|
|
|
|
|
var (
|
2017-10-09 19:49:59 +00:00
|
|
|
// RegisteredRules are the avaible validation to perform on git commits
|
2017-10-09 17:21:23 +00:00
|
|
|
RegisteredRules = []Rule{}
|
|
|
|
registerRuleLock = sync.Mutex{}
|
2015-10-05 20:16:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// RegisterRule includes the Rule in the avaible set to use
|
|
|
|
func RegisterRule(vr Rule) {
|
2017-10-09 17:21:23 +00:00
|
|
|
registerRuleLock.Lock()
|
|
|
|
defer registerRuleLock.Unlock()
|
2015-10-05 20:16:52 +00:00
|
|
|
RegisteredRules = append(RegisteredRules, vr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rule will operate over a provided git.CommitEntry, and return a result.
|
|
|
|
type Rule struct {
|
|
|
|
Name string // short name for reference in in the `-run=...` flag
|
2017-10-09 17:21:23 +00:00
|
|
|
Value string // value to configure for the rule (i.e. a regexp to check for in the commit message)
|
2015-10-05 20:16:52 +00:00
|
|
|
Description string // longer Description for readability
|
2017-10-09 19:49:59 +00:00
|
|
|
Run func(Rule, git.CommitEntry) Result
|
|
|
|
Default bool // whether the registered rule is run by default
|
2015-10-05 20:16:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2017-10-09 19:49:59 +00:00
|
|
|
results = append(results, r.Run(r, c))
|
2015-10-05 20:16:52 +00:00
|
|
|
}
|
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
|
|
|
// Result is the result for a single validation of a commit.
|
|
|
|
type Result struct {
|
|
|
|
CommitEntry git.CommitEntry
|
|
|
|
Pass bool
|
|
|
|
Msg string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Results is a set of results. This is type makes it easy for the following function.
|
|
|
|
type Results []Result
|
|
|
|
|
|
|
|
// PassFail gives a quick over/under of passes and failures of the results in this set
|
|
|
|
func (vr Results) PassFail() (pass int, fail int) {
|
|
|
|
for _, res := range vr {
|
|
|
|
if res.Pass {
|
|
|
|
pass++
|
|
|
|
} else {
|
|
|
|
fail++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pass, fail
|
|
|
|
}
|
2015-10-05 22:29:24 +00:00
|
|
|
|
2017-10-09 17:21:23 +00:00
|
|
|
// SanitizeFilters takes a comma delimited list and returns the trimmend and
|
|
|
|
// split (on ",") items in the list
|
|
|
|
func SanitizeFilters(filtStr string) (filters []string) {
|
|
|
|
for _, item := range strings.Split(filtStr, ",") {
|
|
|
|
filters = append(filters, strings.TrimSpace(item))
|
2015-10-05 22:29:24 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-09 17:21:23 +00:00
|
|
|
// FilterRules takes a set of rules and a list of short names to include, and
|
|
|
|
// returns the reduced set. The comparison is case insensitive.
|
|
|
|
//
|
|
|
|
// Some `includes` rules have values assigned to them.
|
|
|
|
// i.e. -run "dco,message_regexp='^JIRA-[0-9]+ [A-Z].*$'"
|
|
|
|
//
|
|
|
|
func FilterRules(rules []Rule, includes []string) []Rule {
|
2015-10-05 22:29:24 +00:00
|
|
|
ret := []Rule{}
|
|
|
|
|
|
|
|
for _, r := range rules {
|
2017-10-09 17:21:23 +00:00
|
|
|
for i := range includes {
|
|
|
|
if strings.Contains(includes[i], "=") {
|
|
|
|
chunks := strings.SplitN(includes[i], "=", 2)
|
|
|
|
if strings.ToLower(r.Name) == strings.ToLower(chunks[0]) {
|
|
|
|
// for these rules, the Name won't be unique per se. There may be
|
|
|
|
// multiple "regexp=" with different values. We'll need to set the
|
|
|
|
// .Value = chunk[1] and ensure r is dup'ed so they don't clobber
|
|
|
|
// each other.
|
|
|
|
newR := Rule(r)
|
|
|
|
newR.Value = chunks[1]
|
|
|
|
ret = append(ret, newR)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if strings.ToLower(r.Name) == strings.ToLower(includes[i]) {
|
|
|
|
ret = append(ret, r)
|
|
|
|
}
|
2015-10-05 22:29:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
2017-10-09 17:21:23 +00:00
|
|
|
|
|
|
|
// StringsSliceEqual compares two string arrays for equality
|
|
|
|
func StringsSliceEqual(a, b []string) bool {
|
|
|
|
if !sort.StringsAreSorted(a) {
|
|
|
|
sort.Strings(a)
|
|
|
|
}
|
|
|
|
if !sort.StringsAreSorted(b) {
|
|
|
|
sort.Strings(b)
|
|
|
|
}
|
|
|
|
for i := range b {
|
|
|
|
if !StringsSliceContains(a, b[i]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for i := range a {
|
|
|
|
if !StringsSliceContains(b, a[i]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringsSliceContains checks for the presence of a word in string array
|
|
|
|
func StringsSliceContains(a []string, b string) bool {
|
|
|
|
if !sort.StringsAreSorted(a) {
|
|
|
|
sort.Strings(a)
|
|
|
|
}
|
|
|
|
i := sort.SearchStrings(a, b)
|
|
|
|
return i < len(a) && a[i] == b
|
|
|
|
}
|