Skip to content

Commit

Permalink
(#52) Ignore authors by name and email (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
llorllale authored Sep 14, 2019
1 parent e79a964 commit 58c0b8c
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 36 deletions.
1 change: 1 addition & 0 deletions .dependency_license
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ go\.sum, Ignore
\.pdd, Ignore
\.travis\.yml, Ignore
\.idea, Ignore
\.vscode, Ignore
download-gitlint.sh, Ignore
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea/
.vscode/

" goreleaser
dist/
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Flags:
--since="1970-01-01" A date in "yyyy-MM-dd" format starting from which commits will be analyzed (default: "1970-01-01").
--msg-file="" Only analyze the commit message found in this file (default: "").
--max-parents=1 Max number of parents a commit can have in order to be analyzed (default: 1). Useful for excluding merge commits.
--excl-author-names="$a" Don't lint commits with authors whose names match these comma-separated regular expressions (default: '$a').
--excl-author-emails="$a" Don't lint commits with authors whose emails match these comma-separated regular expressions (default: '$a').
```
Additionally, it will look for configurations in a file `.gitlint` in the current directory if it exists. This file's format is just the same command line flags but each on a separate line. *Flags passed through the command line take precedence.*

Expand Down
38 changes: 23 additions & 15 deletions cmd/go-gitlint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ import (
// Figure out a way to remove these global variables. Whatever command line
// parser we choose should be able to auto-generate usage.
var (
path = kingpin.Flag("path", `Path to the git repo (default: ".").`).Default(".").String() //nolint[gochecknoglobals]
subjectRegex = kingpin.Flag("subject-regex", `Commit subject line must conform to this regular expression (default: ".*").`).Default(".*").String() //nolint[gochecknoglobals]
subjectMaxLength = kingpin.Flag("subject-maxlen", "Max length for commit subject line (default: math.MaxInt32 - 1).").Default(strconv.Itoa(math.MaxInt32 - 1)).Int() //nolint[gochecknoglobals]
subjectMinLength = kingpin.Flag("subject-minlen", "Min length for commit subject line (default: 0).").Default("0").Int() //nolint[gochecknoglobals]
bodyRegex = kingpin.Flag("body-regex", `Commit message body must conform to this regular expression (default: ".*").`).Default(".*").String() //nolint[gochecknoglobals]
bodyMaxLength = kingpin.Flag("body-maxlen", `Max length for commit body (default: math.MaxInt32 - 1)`).Default(strconv.Itoa(math.MaxInt32 - 1)).Int() //nolint[gochecknoglobals]
since = kingpin.Flag("since", `A date in "yyyy-MM-dd" format starting from which commits will be analyzed (default: "1970-01-01").`).Default("1970-01-01").String() //nolint[gochecknoglobals]
msgFile = kingpin.Flag("msg-file", `Only analyze the commit message found in this file (default: "").`).Default("").String() //nolint[gochecknoglobals]
maxParents = kingpin.Flag("max-parents", `Max number of parents a commit can have in order to be analyzed (default: 1). Useful for excluding merge commits.`).Default("1").Int() //nolint[gochecknoglobals]
path = kingpin.Flag("path", `Path to the git repo (default: ".").`).Default(".").String() //nolint[gochecknoglobals]
subjectRegex = kingpin.Flag("subject-regex", `Commit subject line must conform to this regular expression (default: ".*").`).Default(".*").String() //nolint[gochecknoglobals]
subjectMaxLength = kingpin.Flag("subject-maxlen", "Max length for commit subject line (default: math.MaxInt32 - 1).").Default(strconv.Itoa(math.MaxInt32 - 1)).Int() //nolint[gochecknoglobals]
subjectMinLength = kingpin.Flag("subject-minlen", "Min length for commit subject line (default: 0).").Default("0").Int() //nolint[gochecknoglobals]
bodyRegex = kingpin.Flag("body-regex", `Commit message body must conform to this regular expression (default: ".*").`).Default(".*").String() //nolint[gochecknoglobals]
bodyMaxLength = kingpin.Flag("body-maxlen", `Max length for commit body (default: math.MaxInt32 - 1)`).Default(strconv.Itoa(math.MaxInt32 - 1)).Int() //nolint[gochecknoglobals]
since = kingpin.Flag("since", `A date in "yyyy-MM-dd" format starting from which commits will be analyzed (default: "1970-01-01").`).Default("1970-01-01").String() //nolint[gochecknoglobals]
msgFile = kingpin.Flag("msg-file", `Only analyze the commit message found in this file (default: "").`).Default("").String() //nolint[gochecknoglobals]
maxParents = kingpin.Flag("max-parents", `Max number of parents a commit can have in order to be analyzed (default: 1). Useful for excluding merge commits.`).Default("1").Int() //nolint[gochecknoglobals]
authorNames = kingpin.Flag("excl-author-names", "Don't lint commits with authors whose names match these comma-separated regular expressions (default: '$a').").Default("$a").String() //nolint[gochecknoglobals]
authorEmails = kingpin.Flag("excl-author-emails", "Don't lint commits with authors whose emails match these comma-separated regular expressions (default: '$a').").Default("$a").String() //nolint[gochecknoglobals]
)

func main() {
Expand All @@ -66,12 +68,18 @@ func main() {
return commits.MsgIn(file)
},
func() commits.Commits {
return commits.WithMaxParents(
*maxParents,
commits.Since(
*since,
commits.In(
repo.Filesystem(*path),
return commits.NotAuthoredByNames(
strings.Split(*authorNames, ","),
commits.NotAuthoredByEmails(
strings.Split(*authorEmails, ","),
commits.WithMaxParents(
*maxParents,
commits.Since(
*since,
commits.In(
repo.Filesystem(*path),
),
),
),
),
)
Expand Down
99 changes: 78 additions & 21 deletions internal/commits/commits.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package commits
import (
"io"
"io/ioutil"
"regexp"
"strings"
"time"

Expand All @@ -39,6 +40,13 @@ type Commit struct {
Message string
Date time.Time
NumParents int
Author *Author
}

// Author is the author of a commit.
type Author struct {
Name string
Email string
}

// ID is the commit's hash.
Expand Down Expand Up @@ -90,6 +98,10 @@ func In(repository repo.Repo) Commits {
Message: c.Message,
Date: c.Author.When,
NumParents: len(c.ParentHashes),
Author: &Author{
Name: c.Author.Name,
Email: c.Author.Email,
},
},
)
return nil
Expand All @@ -100,33 +112,66 @@ func In(repository repo.Repo) Commits {

// Since returns commits authored since time t (format: yyyy-MM-dd).
func Since(t string, cmts Commits) Commits {
return func() []*Commit {
start, err := time.Parse("2006-01-02", t)
if err != nil {
panic(err)
}
filtered := make([]*Commit, 0)
for _, c := range cmts() {
if !c.Date.Before(start) {
filtered = append(filtered, c)
return filtered(
func(c *Commit) bool {
start, err := time.Parse("2006-01-02", t)
if err != nil {
panic(err)
}
}
return filtered
}
return !c.Date.Before(start)
},
cmts,
)
}

// NotAuthoredByNames filters out commits with authors whose names match any of the given patterns.
func NotAuthoredByNames(patterns []string, cmts Commits) Commits {
return filtered(
func(c *Commit) bool {
for _, p := range patterns {
match, err := regexp.MatchString(p, c.Author.Name)
if err != nil {
panic(err)
}
if match {
return false
}
}
return true
},
cmts,
)
}

// NotAuthoredByEmails filters out commits with authors whose emails match any
// of the given patterns.
func NotAuthoredByEmails(patterns []string, cmts Commits) Commits {
return filtered(
func(c *Commit) bool {
for _, p := range patterns {
match, err := regexp.MatchString(p, c.Author.Email)
if err != nil {
panic(err)
}
if match {
return false
}
}
return true
},
cmts,
)
}

// WithMaxParents returns commits that have at most n number of parents.
// Useful for excluding merge commits.
func WithMaxParents(n int, cmts Commits) Commits {
return func() []*Commit {
filtered := make([]*Commit, 0)
for _, c := range cmts() {
if c.NumParents <= n {
filtered = append(filtered, c)
}
}
return filtered
}
return filtered(
func(c *Commit) bool {
return c.NumParents <= n
},
cmts,
)
}

// MsgIn returns a single fake commit with the message read from this reader.
Expand All @@ -144,3 +189,15 @@ func MsgIn(reader io.Reader) Commits {
}}
}
}

func filtered(filter func(*Commit) bool, in Commits) (out Commits) {
return func() []*Commit {
f := make([]*Commit, 0)
for _, c := range in() {
if filter(c) {
f = append(f, c)
}
}
return f
}
}
30 changes: 30 additions & 0 deletions internal/commits/commits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,36 @@ func TestWithMaxParents(t *testing.T) {
assert.Equal(t, commits[0].NumParents, max)
}

func TestNotAuthoredByNames(t *testing.T) { //nolint:dupl
filtered := &Commit{Author: &Author{Name: uuid.New().String()}}
expected := []*Commit{
{Author: &Author{Name: uuid.New().String()}},
{Author: &Author{Name: uuid.New().String()}},
{Author: &Author{Name: uuid.New().String()}},
{Author: &Author{Name: uuid.New().String()}},
}
actual := NotAuthoredByNames(
[]string{filtered.Author.Name},
func() []*Commit { return append(expected, filtered) },
)()
assert.Equal(t, expected, actual)
}

func TestNotAuthoredByEmails(t *testing.T) { //nolint:dupl
filtered := &Commit{Author: &Author{Email: uuid.New().String()}}
expected := []*Commit{
{Author: &Author{Email: uuid.New().String()}},
{Author: &Author{Email: uuid.New().String()}},
{Author: &Author{Email: uuid.New().String()}},
{Author: &Author{Email: uuid.New().String()}},
}
actual := NotAuthoredByEmails(
[]string{filtered.Author.Email},
func() []*Commit { return append(expected, filtered) },
)()
assert.Equal(t, expected, actual)
}

// A git repo initialized and with one commit per each of the messages provided.
// This repo is created in a temporary directory; use the cleanup function
// to delete it afterwards.
Expand Down

0 comments on commit 58c0b8c

Please sign in to comment.