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  }