Files
Paul Holzinger 3b9177995e vendor test tools in submodule
Instead of using the main module we should vendor the test tools in a
different directory. That way we do not add extra dependencies to the
main module which can be problemetic for packages or other users.

This is already done in buildah so this makes us more consitent.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2022-05-04 17:11:43 +02:00

135 lines
3.5 KiB
Go

package validate
import (
"sort"
"strings"
"sync"
"github.com/vbatts/git-validation/git"
)
var (
// RegisteredRules are the avaible validation to perform on git commits
RegisteredRules = []Rule{}
registerRuleLock = sync.Mutex{}
)
// RegisterRule includes the Rule in the avaible set to use
func RegisterRule(vr Rule) {
registerRuleLock.Lock()
defer registerRuleLock.Unlock()
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
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(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(r, c))
}
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
}
// 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))
}
return
}
// 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 {
ret := []Rule{}
for _, r := range rules {
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)
}
}
}
}
return ret
}
// 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
}