/
gommit.go
124 lines (95 loc) · 2.87 KB
/
gommit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package gommit
import (
"fmt"
"regexp"
"strings"
"gopkg.in/src-d/go-git.v4"
"github.com/antham/gommit/reference"
)
// CommitError represents an error when something goes wrong
type CommitError struct {
ID string
Message string
MessageError error
SummaryError error
}
// Query to retrieves commits and do checking
type Query struct {
Path string
From string
To string
Matchers map[string]string
Options map[string]bool
}
// maxSummarySize represents maximum length of accommit summary
const maxSummarySize = 50
// fetchCommits retrieves all commits done in repository between 2 commits references
func fetchCommits(repoPath string, from string, to string) (*[]*git.Commit, error) {
repo, err := git.NewFilesystemRepository(repoPath + "/.git/")
if err != nil {
return nil, err
}
return reference.FetchCommitInterval(repo, from, to)
}
// messageMatchTemplate try to match a commit message against a regexp
func messageMatchTemplate(message string, template string) bool {
r := regexp.MustCompile(template)
g := r.FindStringSubmatch(message)
return len(g) > 0 && g[0] == message
}
// isValidSummaryLength return true if size length is lower than 80 characters
func isValidSummaryLength(message string) bool {
chunks := strings.Split(message, "\n")
return len(chunks) == 0 || len(chunks[0]) <= maxSummarySize
}
// isMergeCommit return true if a commit is a merge commit
func isMergeCommit(commit *git.Commit) bool {
return commit.NumParents() == 2
}
// analyzeCommit check if a commit fit a query
func analyzeCommit(commit *git.Commit, query *Query) *CommitError {
if query.Options["exclude-merge-commits"] && isMergeCommit(commit) {
return nil
}
messageError := fmt.Errorf("No template match commit message")
var summaryError error
if query.Options["check-summary-length"] {
summaryError = fmt.Errorf("Commit summary length is greater than 50 characters")
}
for _, matcher := range query.Matchers {
t := messageMatchTemplate(commit.Message, matcher)
if t {
messageError = nil
}
}
if isValidSummaryLength(commit.Message) {
summaryError = nil
}
if messageError != nil || (summaryError != nil && query.Options["check-summary-length"]) {
return &CommitError{
commit.ID().String(),
commit.Message,
messageError,
summaryError,
}
}
return nil
}
// RunMatching trigger regexp matching against a range message commits
func RunMatching(query Query) (*[]CommitError, error) {
analysis := []CommitError{}
commits, err := fetchCommits(query.Path, query.From, query.To)
if err != nil {
return &analysis, err
}
if len(*commits) == 0 {
return &analysis, fmt.Errorf(`No commits found between "%s" and "%s"`, query.From, query.To)
}
for _, commit := range *commits {
commitError := analyzeCommit(commit, &query)
if commitError != nil {
analysis = append(analysis, *commitError)
}
}
return &analysis, nil
}