mirror of
https://github.com/thegeeklab/git-sv.git
synced 2024-11-21 12:00:40 +00:00
Merge pull request #12 from bvieira/#11
Feature: skip detached branches
This commit is contained in:
commit
4b15622117
@ -97,6 +97,7 @@ branches: # git branches config
|
||||
- master
|
||||
- main
|
||||
- developer
|
||||
skip-detached: false # set true if a detached branch should be ignored on commit message validation
|
||||
|
||||
commit-message:
|
||||
types: # supported commit types
|
||||
@ -203,7 +204,7 @@ COMMIT_MSG_FILE=$1
|
||||
COMMIT_SOURCE=$2
|
||||
SHA1=$3
|
||||
|
||||
git sv vcm --path "$(pwd)" --file $COMMIT_MSG_FILE --source $COMMIT_SOURCE
|
||||
git sv vcm --path "$(pwd)" --file "$COMMIT_MSG_FILE" --source "$COMMIT_SOURCE"
|
||||
```
|
||||
|
||||
tip: you can configure a directory as your global git templates using the command below, check [git config docs](https://git-scm.com/docs/git-config#Documentation/git-config.txt-inittemplateDir) for more information!
|
||||
|
@ -62,6 +62,7 @@ func loadConfig(filepath string) (Config, error) {
|
||||
}
|
||||
|
||||
func defaultConfig() Config {
|
||||
skipDetached := false
|
||||
return Config{
|
||||
Version: "1.0",
|
||||
Versioning: sv.VersioningConfig{
|
||||
@ -77,6 +78,7 @@ func defaultConfig() Config {
|
||||
SuffixRegex: "(-.*)?",
|
||||
DisableIssue: false,
|
||||
Skip: []string{"master", "main", "developer"},
|
||||
SkipDetached: &skipDetached,
|
||||
},
|
||||
CommitMessage: sv.CommitMessageConfig{
|
||||
Types: []string{"build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"},
|
||||
|
@ -371,8 +371,15 @@ func changelogHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor, rnP
|
||||
func validateCommitMessageHandler(git sv.Git, messageProcessor sv.MessageProcessor) func(c *cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
branch := git.Branch()
|
||||
if messageProcessor.SkipBranch(branch) {
|
||||
warn("commit message validation skipped, branch in ignore list...")
|
||||
detached, derr := git.IsDetached()
|
||||
|
||||
if messageProcessor.SkipBranch(branch, derr == nil && detached) {
|
||||
warn("commit message validation skipped, branch in ignore list or detached...")
|
||||
return nil
|
||||
}
|
||||
|
||||
if source := c.String("source"); source == "merge" {
|
||||
warn("commit message validation skipped, ignoring source: %s...", source)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sv4git/sv"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
@ -27,7 +28,7 @@ func main() {
|
||||
|
||||
if envCfg.Home != "" {
|
||||
if homeCfg, err := loadConfig(filepath.Join(envCfg.Home, configFilename)); err == nil {
|
||||
if merr := mergo.Merge(&cfg, homeCfg, mergo.WithOverride); merr != nil {
|
||||
if merr := mergo.Merge(&cfg, homeCfg, mergo.WithOverride, mergo.WithTransformers(&nullTransformer{})); merr != nil {
|
||||
log.Fatal(merr)
|
||||
}
|
||||
}
|
||||
@ -39,7 +40,7 @@ func main() {
|
||||
}
|
||||
|
||||
if repoCfg, err := loadConfig(filepath.Join(repoPath, repoConfigFilename)); err == nil {
|
||||
if merr := mergo.Merge(&cfg, repoCfg, mergo.WithOverride); merr != nil {
|
||||
if merr := mergo.Merge(&cfg, repoCfg, mergo.WithOverride, mergo.WithTransformers(&nullTransformer{})); merr != nil {
|
||||
log.Fatal(merr)
|
||||
}
|
||||
if len(repoCfg.ReleaseNotes.Headers) > 0 { // mergo is merging maps, headers will be overwritten
|
||||
@ -144,7 +145,7 @@ func main() {
|
||||
{
|
||||
Name: "validate-commit-message",
|
||||
Aliases: []string{"vcm"},
|
||||
Usage: "use as prepare-commit-message hook to validate message",
|
||||
Usage: "use as prepare-commit-message hook to validate and enhance commit message",
|
||||
Action: validateCommitMessageHandler(git, messageProcessor),
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{Name: "path", Required: true, Usage: "git working directory"},
|
||||
@ -159,3 +160,18 @@ func main() {
|
||||
log.Fatal(apperr)
|
||||
}
|
||||
}
|
||||
|
||||
type nullTransformer struct {
|
||||
}
|
||||
|
||||
func (t *nullTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
return func(dst, src reflect.Value) error {
|
||||
if dst.CanSet() && !src.IsNil() {
|
||||
dst.Set(src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ type BranchesConfig struct {
|
||||
SuffixRegex string `yaml:"suffix"`
|
||||
DisableIssue bool `yaml:"disable-issue"`
|
||||
Skip []string `yaml:"skip"`
|
||||
SkipDetached *bool `yaml:"skip-detached"`
|
||||
}
|
||||
|
||||
// ==== Versioning ====
|
||||
|
15
sv/git.go
15
sv/git.go
@ -3,6 +3,7 @@ package sv
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -25,6 +26,7 @@ type Git interface {
|
||||
Tag(version semver.Version) error
|
||||
Tags() ([]GitTag, error)
|
||||
Branch() string
|
||||
IsDetached() (bool, error)
|
||||
}
|
||||
|
||||
// GitCommitLog description of a single commit log
|
||||
@ -154,6 +156,19 @@ func (GitImpl) Branch() string {
|
||||
return strings.TrimSpace(strings.Trim(string(out), "\n"))
|
||||
}
|
||||
|
||||
// IsDetached check if is detached.
|
||||
func (GitImpl) IsDetached() (bool, error) {
|
||||
cmd := exec.Command("git", "symbolic-ref", "-q", "HEAD")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if output := string(out); err != nil { //-q: do not issue an error message if the <name> is not a symbolic ref, but a detached HEAD; instead exit with non-zero status silently.
|
||||
if output == "" {
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.New(output)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func parseTagsOutput(input string) ([]GitTag, error) {
|
||||
scanner := bufio.NewScanner(strings.NewReader(input))
|
||||
var result []GitTag
|
||||
|
@ -2,7 +2,6 @@ package sv
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
@ -48,7 +47,7 @@ func (m CommitMessage) BreakingMessage() string {
|
||||
|
||||
// MessageProcessor interface.
|
||||
type MessageProcessor interface {
|
||||
SkipBranch(branch string) bool
|
||||
SkipBranch(branch string, detached bool) bool
|
||||
Validate(message string) error
|
||||
Enhance(branch string, message string) (string, error)
|
||||
IssueID(branch string) (string, error)
|
||||
@ -71,8 +70,8 @@ type MessageProcessorImpl struct {
|
||||
}
|
||||
|
||||
// SkipBranch check if branch should be ignored.
|
||||
func (p MessageProcessorImpl) SkipBranch(branch string) bool {
|
||||
return contains(branch, p.branchesCfg.Skip)
|
||||
func (p MessageProcessorImpl) SkipBranch(branch string, detached bool) bool {
|
||||
return contains(branch, p.branchesCfg.Skip) || (p.branchesCfg.SkipDetached != nil && *p.branchesCfg.SkipDetached && detached)
|
||||
}
|
||||
|
||||
// Validate commit message.
|
||||
@ -81,7 +80,7 @@ func (p MessageProcessorImpl) Validate(message string) error {
|
||||
msg := p.Parse(subject, body)
|
||||
|
||||
if !regexp.MustCompile("^[a-z+]+(\\(.+\\))?!?: .+$").MatchString(subject) {
|
||||
return errors.New("message should be valid according with conventional commits")
|
||||
return fmt.Errorf("subject [%s] should be valid according with conventional commits", subject)
|
||||
}
|
||||
|
||||
if msg.Type == "" || !contains(msg.Type, p.messageCfg.Types) {
|
||||
|
@ -25,10 +25,13 @@ var ccfgWithScope = CommitMessageConfig{
|
||||
Issue: CommitMessageIssueConfig{Regex: "[A-Z]+-[0-9]+"},
|
||||
}
|
||||
|
||||
var bcfg = BranchesConfig{
|
||||
PrefixRegex: "([a-z]+\\/)?",
|
||||
SuffixRegex: "(-.*)?",
|
||||
Skip: []string{"develop", "master"},
|
||||
func newBranchCfg(skipDetached bool) BranchesConfig {
|
||||
return BranchesConfig{
|
||||
PrefixRegex: "([a-z]+\\/)?",
|
||||
SuffixRegex: "(-.*)?",
|
||||
Skip: []string{"develop", "master"},
|
||||
SkipDetached: &skipDetached,
|
||||
}
|
||||
}
|
||||
|
||||
// messages samples start
|
||||
@ -67,6 +70,30 @@ BREAKING CHANGE: refactor to use JavaScript features not available in Node 6.`
|
||||
|
||||
// multiline samples end
|
||||
|
||||
func TestMessageProcessorImpl_SkipBranch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
bcfg BranchesConfig
|
||||
branch string
|
||||
detached bool
|
||||
want bool
|
||||
}{
|
||||
{"normal branch", newBranchCfg(false), "JIRA-123", false, false},
|
||||
{"dont ignore detached branch", newBranchCfg(false), "JIRA-123", true, false},
|
||||
{"ignore branch on skip list", newBranchCfg(false), "master", false, true},
|
||||
{"ignore detached branch", newBranchCfg(true), "JIRA-123", true, true},
|
||||
{"null skip detached", BranchesConfig{Skip: []string{}}, "JIRA-123", true, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := NewMessageProcessor(ccfg, tt.bcfg)
|
||||
if got := p.SkipBranch(tt.branch, tt.detached); got != tt.want {
|
||||
t.Errorf("MessageProcessorImpl.SkipBranch() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageProcessorImpl_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -94,7 +121,7 @@ func TestMessageProcessorImpl_Validate(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := NewMessageProcessor(tt.cfg, bcfg)
|
||||
p := NewMessageProcessor(tt.cfg, newBranchCfg(false))
|
||||
if err := p.Validate(tt.message); (err != nil) != tt.wantErr {
|
||||
t.Errorf("MessageProcessorImpl.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
@ -103,7 +130,7 @@ func TestMessageProcessorImpl_Validate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMessageProcessorImpl_Enhance(t *testing.T) {
|
||||
p := NewMessageProcessor(ccfg, bcfg)
|
||||
p := NewMessageProcessor(ccfg, newBranchCfg(false))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -136,7 +163,7 @@ func TestMessageProcessorImpl_Enhance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMessageProcessorImpl_IssueID(t *testing.T) {
|
||||
p := NewMessageProcessor(ccfg, bcfg)
|
||||
p := NewMessageProcessor(ccfg, newBranchCfg(false))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -248,7 +275,7 @@ Jira: JIRA-999
|
||||
Refs #123`
|
||||
|
||||
func TestMessageProcessorImpl_Parse(t *testing.T) {
|
||||
p := NewMessageProcessor(ccfg, bcfg)
|
||||
p := NewMessageProcessor(ccfg, newBranchCfg(false))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -275,7 +302,7 @@ func TestMessageProcessorImpl_Parse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMessageProcessorImpl_Format(t *testing.T) {
|
||||
p := NewMessageProcessor(ccfg, bcfg)
|
||||
p := NewMessageProcessor(ccfg, newBranchCfg(false))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
Loading…
Reference in New Issue
Block a user