0
0
mirror of https://github.com/thegeeklab/git-sv.git synced 2024-11-22 08:20:39 +00:00

Merge pull request #12 from bvieira/#11

Feature: skip detached branches
This commit is contained in:
Beatriz Vieira 2021-03-04 00:54:32 -03:00 committed by GitHub
commit 4b15622117
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 88 additions and 20 deletions

View File

@ -97,6 +97,7 @@ branches: # git branches config
- master - master
- main - main
- developer - developer
skip-detached: false # set true if a detached branch should be ignored on commit message validation
commit-message: commit-message:
types: # supported commit types types: # supported commit types
@ -203,7 +204,7 @@ COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2 COMMIT_SOURCE=$2
SHA1=$3 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! 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!

View File

@ -62,6 +62,7 @@ func loadConfig(filepath string) (Config, error) {
} }
func defaultConfig() Config { func defaultConfig() Config {
skipDetached := false
return Config{ return Config{
Version: "1.0", Version: "1.0",
Versioning: sv.VersioningConfig{ Versioning: sv.VersioningConfig{
@ -77,6 +78,7 @@ func defaultConfig() Config {
SuffixRegex: "(-.*)?", SuffixRegex: "(-.*)?",
DisableIssue: false, DisableIssue: false,
Skip: []string{"master", "main", "developer"}, Skip: []string{"master", "main", "developer"},
SkipDetached: &skipDetached,
}, },
CommitMessage: sv.CommitMessageConfig{ CommitMessage: sv.CommitMessageConfig{
Types: []string{"build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"}, Types: []string{"build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"},

View File

@ -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 { func validateCommitMessageHandler(git sv.Git, messageProcessor sv.MessageProcessor) func(c *cli.Context) error {
return func(c *cli.Context) error { return func(c *cli.Context) error {
branch := git.Branch() branch := git.Branch()
if messageProcessor.SkipBranch(branch) { detached, derr := git.IsDetached()
warn("commit message validation skipped, branch in ignore list...")
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 return nil
} }

View File

@ -4,6 +4,7 @@ import (
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"sv4git/sv" "sv4git/sv"
"github.com/imdario/mergo" "github.com/imdario/mergo"
@ -27,7 +28,7 @@ func main() {
if envCfg.Home != "" { if envCfg.Home != "" {
if homeCfg, err := loadConfig(filepath.Join(envCfg.Home, configFilename)); err == nil { 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) log.Fatal(merr)
} }
} }
@ -39,7 +40,7 @@ func main() {
} }
if repoCfg, err := loadConfig(filepath.Join(repoPath, repoConfigFilename)); err == nil { 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) log.Fatal(merr)
} }
if len(repoCfg.ReleaseNotes.Headers) > 0 { // mergo is merging maps, headers will be overwritten if len(repoCfg.ReleaseNotes.Headers) > 0 { // mergo is merging maps, headers will be overwritten
@ -144,7 +145,7 @@ func main() {
{ {
Name: "validate-commit-message", Name: "validate-commit-message",
Aliases: []string{"vcm"}, 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), Action: validateCommitMessageHandler(git, messageProcessor),
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{Name: "path", Required: true, Usage: "git working directory"}, &cli.StringFlag{Name: "path", Required: true, Usage: "git working directory"},
@ -159,3 +160,18 @@ func main() {
log.Fatal(apperr) 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
}

View File

@ -43,6 +43,7 @@ type BranchesConfig struct {
SuffixRegex string `yaml:"suffix"` SuffixRegex string `yaml:"suffix"`
DisableIssue bool `yaml:"disable-issue"` DisableIssue bool `yaml:"disable-issue"`
Skip []string `yaml:"skip"` Skip []string `yaml:"skip"`
SkipDetached *bool `yaml:"skip-detached"`
} }
// ==== Versioning ==== // ==== Versioning ====

View File

@ -3,6 +3,7 @@ package sv
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"errors"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
@ -25,6 +26,7 @@ type Git interface {
Tag(version semver.Version) error Tag(version semver.Version) error
Tags() ([]GitTag, error) Tags() ([]GitTag, error)
Branch() string Branch() string
IsDetached() (bool, error)
} }
// GitCommitLog description of a single commit log // GitCommitLog description of a single commit log
@ -154,6 +156,19 @@ func (GitImpl) Branch() string {
return strings.TrimSpace(strings.Trim(string(out), "\n")) 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) { func parseTagsOutput(input string) ([]GitTag, error) {
scanner := bufio.NewScanner(strings.NewReader(input)) scanner := bufio.NewScanner(strings.NewReader(input))
var result []GitTag var result []GitTag

View File

@ -2,7 +2,6 @@ package sv
import ( import (
"bufio" "bufio"
"errors"
"fmt" "fmt"
"regexp" "regexp"
"strings" "strings"
@ -48,7 +47,7 @@ func (m CommitMessage) BreakingMessage() string {
// MessageProcessor interface. // MessageProcessor interface.
type MessageProcessor interface { type MessageProcessor interface {
SkipBranch(branch string) bool SkipBranch(branch string, detached bool) bool
Validate(message string) error Validate(message string) error
Enhance(branch string, message string) (string, error) Enhance(branch string, message string) (string, error)
IssueID(branch string) (string, error) IssueID(branch string) (string, error)
@ -71,8 +70,8 @@ type MessageProcessorImpl struct {
} }
// SkipBranch check if branch should be ignored. // SkipBranch check if branch should be ignored.
func (p MessageProcessorImpl) SkipBranch(branch string) bool { func (p MessageProcessorImpl) SkipBranch(branch string, detached bool) bool {
return contains(branch, p.branchesCfg.Skip) return contains(branch, p.branchesCfg.Skip) || (p.branchesCfg.SkipDetached != nil && *p.branchesCfg.SkipDetached && detached)
} }
// Validate commit message. // Validate commit message.
@ -81,7 +80,7 @@ func (p MessageProcessorImpl) Validate(message string) error {
msg := p.Parse(subject, body) msg := p.Parse(subject, body)
if !regexp.MustCompile("^[a-z+]+(\\(.+\\))?!?: .+$").MatchString(subject) { 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) { if msg.Type == "" || !contains(msg.Type, p.messageCfg.Types) {

View File

@ -25,10 +25,13 @@ var ccfgWithScope = CommitMessageConfig{
Issue: CommitMessageIssueConfig{Regex: "[A-Z]+-[0-9]+"}, Issue: CommitMessageIssueConfig{Regex: "[A-Z]+-[0-9]+"},
} }
var bcfg = BranchesConfig{ func newBranchCfg(skipDetached bool) BranchesConfig {
return BranchesConfig{
PrefixRegex: "([a-z]+\\/)?", PrefixRegex: "([a-z]+\\/)?",
SuffixRegex: "(-.*)?", SuffixRegex: "(-.*)?",
Skip: []string{"develop", "master"}, Skip: []string{"develop", "master"},
SkipDetached: &skipDetached,
}
} }
// messages samples start // messages samples start
@ -67,6 +70,30 @@ BREAKING CHANGE: refactor to use JavaScript features not available in Node 6.`
// multiline samples end // 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) { func TestMessageProcessorImpl_Validate(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -94,7 +121,7 @@ func TestMessageProcessorImpl_Validate(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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 { if err := p.Validate(tt.message); (err != nil) != tt.wantErr {
t.Errorf("MessageProcessorImpl.Validate() error = %v, wantErr %v", err, 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) { func TestMessageProcessorImpl_Enhance(t *testing.T) {
p := NewMessageProcessor(ccfg, bcfg) p := NewMessageProcessor(ccfg, newBranchCfg(false))
tests := []struct { tests := []struct {
name string name string
@ -136,7 +163,7 @@ func TestMessageProcessorImpl_Enhance(t *testing.T) {
} }
func TestMessageProcessorImpl_IssueID(t *testing.T) { func TestMessageProcessorImpl_IssueID(t *testing.T) {
p := NewMessageProcessor(ccfg, bcfg) p := NewMessageProcessor(ccfg, newBranchCfg(false))
tests := []struct { tests := []struct {
name string name string
@ -248,7 +275,7 @@ Jira: JIRA-999
Refs #123` Refs #123`
func TestMessageProcessorImpl_Parse(t *testing.T) { func TestMessageProcessorImpl_Parse(t *testing.T) {
p := NewMessageProcessor(ccfg, bcfg) p := NewMessageProcessor(ccfg, newBranchCfg(false))
tests := []struct { tests := []struct {
name string name string
@ -275,7 +302,7 @@ func TestMessageProcessorImpl_Parse(t *testing.T) {
} }
func TestMessageProcessorImpl_Format(t *testing.T) { func TestMessageProcessorImpl_Format(t *testing.T) {
p := NewMessageProcessor(ccfg, bcfg) p := NewMessageProcessor(ccfg, newBranchCfg(false))
tests := []struct { tests := []struct {
name string name string