github.com/autonomy/conform@v0.1.0-alpha.16/internal/policy/commit/commit.go (about) 1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 package commit 6 7 import ( 8 "io/ioutil" 9 "regexp" 10 "strings" 11 12 "github.com/autonomy/conform/internal/git" 13 "github.com/autonomy/conform/internal/policy" 14 "github.com/pkg/errors" 15 ) 16 17 // Commit implements the policy.Policy interface and enforces commit 18 // messages to conform the Conventional Commit standard. 19 type Commit struct { 20 // HeaderLength is the maximum length of the commit subject. 21 HeaderLength int `mapstructure:"headerLength"` 22 // DCO enables the Developer Certificate of Origin check. 23 DCO bool `mapstructure:"dco"` 24 // GPG enables the GPG signature check. 25 GPG bool `mapstructure:"gpg"` 26 // Imperative enforces the use of imperative verbs as the first word of a 27 // commit message. 28 Imperative bool `mapstructure:"imperative"` 29 // MaximumOfOneCommit enforces that the current commit is only one commit 30 // ahead of a specified ref. 31 MaximumOfOneCommit bool `mapstructure:"maximumOfOneCommit"` 32 // RequireCommitBody enforces that the current commit has a body. 33 RequireCommitBody bool `mapstructure:"requireCommitBody"` 34 // Conventional is the user specified settings for conventional commits. 35 Conventional *Conventional `mapstructure:"conventional"` 36 37 msg string 38 } 39 40 // FirstWordRegex is theregular expression used to find the first word in a 41 // commit. 42 var FirstWordRegex = regexp.MustCompile(`^\s*([a-zA-Z0-9]+)`) 43 44 // Compliance implements the policy.Policy.Compliance function. 45 // nolint: gocyclo 46 func (c *Commit) Compliance(options *policy.Options) (*policy.Report, error) { 47 var err error 48 49 report := &policy.Report{} 50 51 // Setup the policy for all checks. 52 53 var g *git.Git 54 if g, err = git.NewGit(); err != nil { 55 return report, errors.Errorf("failed to open git repo: %v", err) 56 } 57 58 var msg string 59 if options.CommitMsgFile != nil { 60 var contents []byte 61 if contents, err = ioutil.ReadFile(*options.CommitMsgFile); err != nil { 62 return report, errors.Errorf("failed to read commit message file: %v", err) 63 } 64 msg = string(contents) 65 } else if msg, err = g.Message(); err != nil { 66 return report, errors.Errorf("failed to get commit message: %v", err) 67 } 68 c.msg = msg 69 70 if c.HeaderLength != 0 { 71 report.AddCheck(c.ValidateHeaderLength()) 72 } 73 74 if c.DCO { 75 report.AddCheck(c.ValidateDCO()) 76 } 77 78 if c.GPG { 79 report.AddCheck(c.ValidateGPGSign(g)) 80 } 81 82 if c.Imperative { 83 report.AddCheck(c.ValidateImperative()) 84 } 85 86 if c.Conventional != nil { 87 report.AddCheck(c.ValidateConventionalCommit()) 88 } 89 90 if c.MaximumOfOneCommit { 91 report.AddCheck(c.ValidateNumberOfCommits(g, "refs/heads/master")) 92 } 93 94 if c.RequireCommitBody { 95 report.AddCheck(c.ValidateBody()) 96 } 97 98 return report, nil 99 } 100 101 func (c Commit) firstWord() (string, error) { 102 var header string 103 var groups []string 104 var msg string 105 if c.Conventional != nil { 106 groups = parseHeader(c.msg) 107 msg = groups[4] 108 } else { 109 msg = c.msg 110 } 111 if header = strings.Split(strings.TrimPrefix(msg, "\n"), "\n")[0]; header == "" { 112 return "", errors.Errorf("Invalid msg: %s", msg) 113 } 114 if groups = FirstWordRegex.FindStringSubmatch(header); groups == nil { 115 return "", errors.Errorf("Invalid msg: %s", msg) 116 } 117 return groups[0], nil 118 }