diff --git a/git/commits.go b/git/commits.go index 1e2c484..aad1039 100644 --- a/git/commits.go +++ b/git/commits.go @@ -17,7 +17,7 @@ import ( // If commitrange is a single commit, all ancestor commits up through the hash provided. func Commits(commitrange string) ([]CommitEntry, error) { cmdArgs := []string{"git", "log", prettyFormat + formatCommit, commitrange} - if Verbose { + if debug() { logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " ")) } output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output() @@ -40,9 +40,6 @@ func Commits(commitrange string) ([]CommitEntry, error) { type CommitEntry map[string]string var ( - // Verbose output of commands run - Verbose = false - prettyFormat = `--pretty=format:` formatSubject = `%s` formatBody = `%b` @@ -60,7 +57,7 @@ var ( func LogCommit(commit string) (*CommitEntry, error) { buf := bytes.NewBuffer([]byte{}) cmdArgs := []string{"git", "log", "-1", prettyFormat + formatMap, commit} - if Verbose { + if debug() { logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " ")) } cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) @@ -99,10 +96,14 @@ func LogCommit(commit string) (*CommitEntry, error) { return &c, nil } +func debug() bool { + return len(os.Getenv("DEBUG")) > 0 +} + // FetchHeadCommit returns the hash of FETCH_HEAD func FetchHeadCommit() (string, error) { cmdArgs := []string{"git", "rev-parse", "--verify", "FETCH_HEAD"} - if Verbose { + if debug() { logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " ")) } output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output() @@ -115,7 +116,7 @@ func FetchHeadCommit() (string, error) { // HeadCommit returns the hash of HEAD func HeadCommit() (string, error) { cmdArgs := []string{"git", "rev-parse", "--verify", "HEAD"} - if Verbose { + if debug() { logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " ")) } output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output() diff --git a/main.go b/main.go index e6e843b..46fc995 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,6 @@ import ( "log" "os" - "github.com/vbatts/git-validation/git" _ "github.com/vbatts/git-validation/rules/dco" _ "github.com/vbatts/git-validation/rules/shortsubject" "github.com/vbatts/git-validation/validate" @@ -17,13 +16,15 @@ var ( flListRules = flag.Bool("list-rules", false, "list the rules registered") flRun = flag.String("run", "", "comma delimited list of rules to run. Defaults to all.") flVerbose = flag.Bool("v", false, "verbose") + flDebug = flag.Bool("D", false, "debug output") + flDir = flag.String("d", ".", "git directory to validate from") ) func main() { flag.Parse() - if *flVerbose { - git.Verbose = true + if *flDebug { + os.Setenv("DEBUG", "1") } if *flListRules { @@ -39,53 +40,18 @@ func main() { rules = validate.FilterRules(rules, validate.SanitizeFilters(*flRun)) } - // Guess the commits we're working with - var commitrange string - if *flCommitRange != "" { - commitrange = *flCommitRange - } else { - var err error - commitrange, err = git.FetchHeadCommit() - if err != nil { - commitrange, err = git.HeadCommit() - if err != nil { - log.Fatal(err) - } - } - } - - // collect the entries - c, err := git.Commits(commitrange) + runner, err := validate.NewRunner(*flDir, rules, *flCommitRange, *flVerbose) if err != nil { log.Fatal(err) } - // run them and show results - results := validate.Results{} - for _, commit := range c { - fmt.Printf(" * %s %s ... ", commit["abbreviated_commit"], commit["subject"]) - vr := validate.Commit(commit, rules) - results = append(results, vr...) - if _, fail := vr.PassFail(); fail == 0 { - fmt.Println("PASS") - } else { - fmt.Println("FAIL") - } - for _, r := range vr { - if *flVerbose { - if r.Pass { - fmt.Printf("ok %s\n", r.Msg) - } else { - fmt.Printf("not ok %s\n", r.Msg) - } - } else if !r.Pass { - fmt.Printf("not ok %s\n", r.Msg) - } - } + if err := runner.Run(); err != nil { + log.Fatal(err) } - _, fail := results.PassFail() + _, fail := runner.Results.PassFail() if fail > 0 { fmt.Printf("%d issues to fix\n", fail) os.Exit(1) } + } diff --git a/rules/shortsubject/rule.go b/rules/shortsubject/rule.go index adebe64..64d918d 100644 --- a/rules/shortsubject/rule.go +++ b/rules/shortsubject/rule.go @@ -6,7 +6,7 @@ import ( ) var ( - // DcoRule is the rule being registered + // ShortSubjectRule is the rule being registered ShortSubjectRule = validate.Rule{ Name: "short-subject", Description: "commit subjects are strictly less than 90 (github ellipsis length)", @@ -18,6 +18,8 @@ func init() { validate.RegisterRule(ShortSubjectRule) } +// ValidateShortSubject checks that the commit's subject is strictly less than +// 90 characters (preferrably not more than 72 chars). func ValidateShortSubject(c git.CommitEntry) (vr validate.Result) { if len(c["subject"]) >= 90 { vr.Pass = false @@ -25,6 +27,10 @@ func ValidateShortSubject(c git.CommitEntry) (vr validate.Result) { return } vr.Pass = true - vr.Msg = "commit subject is not more than 90 characters" + if len(c["subject"]) > 72 { + vr.Msg = "commit subject is not more than 90 characters, but is still more than 72 chars" + } else { + vr.Msg = "commit subject is 72 characters or less! *yay*" + } return } diff --git a/validate/runner.go b/validate/runner.go new file mode 100644 index 0000000..8986685 --- /dev/null +++ b/validate/runner.go @@ -0,0 +1,94 @@ +package validate + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/vbatts/git-validation/git" +) + +// Runner is the for processing a set of rules against a range of commits +type Runner struct { + Root string + Rules []Rule + Results Results + Verbose bool + CommitRange string // if this is empty, then it will default to FETCH_HEAD, then HEAD +} + +// NewRunner returns an initiallized Runner. +func NewRunner(root string, rules []Rule, commitrange string, verbose bool) (*Runner, error) { + newroot, err := filepath.Abs(root) + if err != nil { + return nil, fmt.Errorf("failed to get absolute path of %q: %s", root, err) + } + if commitrange == "" { + var err error + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + defer os.Chdir(cwd) + + if err := os.Chdir(newroot); err != nil { + return nil, err + } + commitrange, err = git.FetchHeadCommit() + if err != nil { + commitrange, err = git.HeadCommit() + if err != nil { + return nil, err + } + } + } + return &Runner{ + Root: newroot, + Rules: rules, + CommitRange: commitrange, + Verbose: verbose, + }, nil +} + +// Run processes the rules for each commit in the range provided +func (r *Runner) Run() error { + cwd, err := os.Getwd() + if err != nil { + return err + } + defer os.Chdir(cwd) + + if err := os.Chdir(r.Root); err != nil { + return err + } + + // collect the entries + c, err := git.Commits(r.CommitRange) + if err != nil { + return err + } + + // run them and show results + for _, commit := range c { + fmt.Printf(" * %s %q ... ", commit["abbreviated_commit"], commit["subject"]) + vr := Commit(commit, r.Rules) + r.Results = append(r.Results, vr...) + if _, fail := vr.PassFail(); fail == 0 { + fmt.Println("PASS") + } else { + fmt.Println("FAIL") + } + for _, res := range vr { + if r.Verbose { + if res.Pass { + fmt.Printf(" - PASS - %s\n", res.Msg) + } else { + fmt.Printf(" - FAIL - %s\n", res.Msg) + } + } else if !res.Pass { + fmt.Printf(" - FAIL - %s\n", res.Msg) + } + } + } + return nil +}