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

Merge pull request #27 from bvieira/commit-cmd-flags

Feature: commit command flags
This commit is contained in:
Beatriz Vieira 2021-07-31 16:01:11 -03:00 committed by GitHub
commit c037311d1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 227 additions and 59 deletions

View File

@ -142,7 +142,7 @@ git-sv rn -h
##### Available commands
| Variable | description | has options or subcommands |
| ---------------------------- | ------------------------------------------------------------- | :------------------------: |
| ---------------------------- | -------------------------------------------------------------- | :------------------------: |
| config, cfg | Show config information. | :heavy_check_mark: |
| current-version, cv | Get last released version from git. | :x: |
| next-version, nv | Generate the next version based on git commit messages. | :x: |
@ -151,7 +151,7 @@ git-sv rn -h
| release-notes, rn | Generate release notes. | :heavy_check_mark: |
| changelog, cgl | Generate changelog. | :heavy_check_mark: |
| tag, tg | Generate tag with version based on git commit messages. | :x: |
| commit, cmt | Execute git commit with convetional commit message helper. | :x: |
| commit, cmt | Execute git commit with convetional commit message helper. | :heavy_check_mark: |
| validate-commit-message, vcm | Use as prepare-commit-message hook to validate commit message. | :heavy_check_mark: |
| help, h | Shows a list of commands or help for one command. | :x: |
@ -210,7 +210,7 @@ make
#### Make configs
| Variable | description |
| ---------- | ---------------------- |
| ---------- | ----------------------- |
| BUILDOS | Build OS. |
| BUILDARCH | Build arch. |
| ECHOFLAGS | Flags used on echo. |
@ -218,7 +218,7 @@ make
| BUILDFLAGS | Flags used on build. |
| Parameters | description |
| ---------- | ----------------------------------- |
| ---------- | ------------------------------------ |
| args | Parameters that will be used on run. |
```bash

View File

@ -266,27 +266,37 @@ func tagHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) func(c *c
}
}
func commitHandler(cfg Config, git sv.Git, messageProcessor sv.MessageProcessor) func(c *cli.Context) error {
return func(c *cli.Context) error {
ctype, err := promptType(cfg.CommitMessage.Types)
if err != nil {
return err
func getCommitType(cfg Config, p sv.MessageProcessor, input string) (string, error) {
if input == "" {
t, err := promptType(cfg.CommitMessage.Types)
return t.Type, err
}
return input, p.ValidateType(input)
}
scope, err := promptScope(cfg.CommitMessage.Scope.Values)
if err != nil {
return err
func getCommitScope(cfg Config, p sv.MessageProcessor, input string, noScope bool) (string, error) {
if input == "" && !noScope {
return promptScope(cfg.CommitMessage.Scope.Values)
}
return input, p.ValidateScope(input)
}
subject, err := promptSubject()
if err != nil {
return err
func getCommitDescription(cfg Config, p sv.MessageProcessor, input string) (string, error) {
if input == "" {
return promptSubject()
}
return input, p.ValidateDescription(input)
}
func getCommitBody(noBody bool) (string, error) {
if noBody {
return "", nil
}
var fullBody strings.Builder
for body, err := promptBody(); body != "" || err != nil; body, err = promptBody() {
if err != nil {
return err
return "", err
}
if fullBody.Len() > 0 {
fullBody.WriteString("\n")
@ -295,33 +305,88 @@ func commitHandler(cfg Config, git sv.Git, messageProcessor sv.MessageProcessor)
fullBody.WriteString(body)
}
}
return fullBody.String(), nil
}
branchIssue, err := messageProcessor.IssueID(git.Branch())
func getCommitIssue(cfg Config, p sv.MessageProcessor, branch string, noIssue bool) (string, error) {
branchIssue, err := p.IssueID(branch)
if err != nil {
return "", err
}
if cfg.CommitMessage.IssueFooterConfig().Key == "" || cfg.CommitMessage.Issue.Regex == "" {
return "", nil
}
if noIssue {
return branchIssue, nil
}
return promptIssueID("issue id", cfg.CommitMessage.Issue.Regex, branchIssue)
}
func getCommitBreakingChange(noBreaking bool, input string) (string, error) {
if noBreaking {
return "", nil
}
if strings.TrimSpace(input) != "" {
return input, nil
}
hasBreakingChanges, err := promptConfirm("has breaking change?")
if err != nil {
return "", err
}
if !hasBreakingChanges {
return "", nil
}
return promptBreakingChanges()
}
func commitHandler(cfg Config, git sv.Git, messageProcessor sv.MessageProcessor) func(c *cli.Context) error {
return func(c *cli.Context) error {
noBreaking := c.Bool("no-breaking")
noBody := c.Bool("no-body")
noIssue := c.Bool("no-issue")
noScope := c.Bool("no-scope")
inputType := c.String("type")
inputScope := c.String("scope")
inputDescription := c.String("description")
inputBreakingChange := c.String("breaking-change")
ctype, err := getCommitType(cfg, messageProcessor, inputType)
if err != nil {
return err
}
var issue string
if cfg.CommitMessage.IssueFooterConfig().Key != "" && cfg.CommitMessage.Issue.Regex != "" {
issue, err = promptIssueID("issue id", cfg.CommitMessage.Issue.Regex, branchIssue)
scope, err := getCommitScope(cfg, messageProcessor, inputScope, noScope)
if err != nil {
return err
}
}
hasBreakingChanges, err := promptConfirm("has breaking changes?")
subject, err := getCommitDescription(cfg, messageProcessor, inputDescription)
if err != nil {
return err
}
breakingChanges := ""
if hasBreakingChanges {
breakingChanges, err = promptBreakingChanges()
if err != nil {
return err
}
}
header, body, footer := messageProcessor.Format(sv.NewCommitMessage(ctype.Type, scope, subject, fullBody.String(), issue, breakingChanges))
fullBody, err := getCommitBody(noBody)
if err != nil {
return err
}
issue, err := getCommitIssue(cfg, messageProcessor, git.Branch(), noIssue)
if err != nil {
return err
}
breakingChange, err := getCommitBreakingChange(noBreaking, inputBreakingChange)
if err != nil {
return err
}
header, body, footer := messageProcessor.Format(sv.NewCommitMessage(ctype, scope, subject, fullBody, issue, breakingChange))
err = git.Commit(header, body, footer)
if err != nil {

View File

@ -141,6 +141,16 @@ func main() {
Aliases: []string{"cmt"},
Usage: "execute git commit with convetional commit message helper",
Action: commitHandler(cfg, git, messageProcessor),
Flags: []cli.Flag{
&cli.BoolFlag{Name: "no-scope", Aliases: []string{"nsc"}, Usage: "do not prompt for commit scope"},
&cli.BoolFlag{Name: "no-body", Aliases: []string{"nbd"}, Usage: "do not prompt for commit body"},
&cli.BoolFlag{Name: "no-issue", Aliases: []string{"nis"}, Usage: "do not prompt for commit issue, will try to recover from branch if enabled"},
&cli.BoolFlag{Name: "no-breaking", Aliases: []string{"nbc"}, Usage: "do not prompt for breaking changes"},
&cli.StringFlag{Name: "type", Aliases: []string{"t"}, Usage: "define commit type"},
&cli.StringFlag{Name: "scope", Aliases: []string{"s"}, Usage: "define commit scope"},
&cli.StringFlag{Name: "description", Aliases: []string{"d"}, Usage: "define commit description"},
&cli.StringFlag{Name: "breaking-change", Aliases: []string{"b"}, Usage: "define commit breaking change message"},
},
},
{
Name: "validate-commit-message",

View File

@ -80,7 +80,7 @@ func promptIssueID(issueLabel, issueRegex, defaultValue string) (string, error)
}
func promptBreakingChanges() (string, error) {
return promptText("Breaking changes description", "[a-z].+", "")
return promptText("Breaking change description", "[a-z].+", "")
}
func promptSelect(label string, items interface{}, template *promptui.SelectTemplates) (int, error) {

View File

@ -49,6 +49,9 @@ func (m CommitMessage) BreakingMessage() string {
type MessageProcessor interface {
SkipBranch(branch string, detached bool) bool
Validate(message string) error
ValidateType(ctype string) error
ValidateScope(scope string) error
ValidateDescription(description string) error
Enhance(branch string, message string) (string, error)
IssueID(branch string) (string, error)
Format(msg CommitMessage) (string, string, string)
@ -83,14 +86,39 @@ func (p MessageProcessorImpl) Validate(message string) error {
return fmt.Errorf("subject [%s] should be valid according with conventional commits", subject)
}
if msg.Type == "" || !contains(msg.Type, p.messageCfg.Types) {
if err := p.ValidateType(msg.Type); err != nil {
return err
}
if err := p.ValidateScope(msg.Scope); err != nil {
return err
}
if err := p.ValidateDescription(msg.Description); err != nil {
return err
}
return nil
}
func (p MessageProcessorImpl) ValidateType(ctype string) error {
if ctype == "" || !contains(ctype, p.messageCfg.Types) {
return fmt.Errorf("message type should be one of [%v]", strings.Join(p.messageCfg.Types, ", "))
}
if len(p.messageCfg.Scope.Values) > 0 && !contains(msg.Scope, p.messageCfg.Scope.Values) {
return fmt.Errorf("message scope should one of [%v]", strings.Join(p.messageCfg.Scope.Values, ", "))
return nil
}
func (p MessageProcessorImpl) ValidateScope(scope string) error {
if len(p.messageCfg.Scope.Values) > 0 && !contains(scope, p.messageCfg.Scope.Values) {
return fmt.Errorf("message scope should one of [%v]", strings.Join(p.messageCfg.Scope.Values, ", "))
}
return nil
}
func (p MessageProcessorImpl) ValidateDescription(description string) error {
if !regexp.MustCompile("^[a-z]+.*$").MatchString(description) {
return fmt.Errorf("description [%s] should begins with lowercase letter", description)
}
return nil
}

View File

@ -157,6 +157,71 @@ func TestMessageProcessorImpl_Validate(t *testing.T) {
}
}
func TestMessageProcessorImpl_ValidateType(t *testing.T) {
tests := []struct {
name string
cfg CommitMessageConfig
ctype string
wantErr bool
}{
{"valid type", ccfg, "feat", false},
{"invalid type", ccfg, "aaa", true},
{"empty type", ccfg, "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewMessageProcessor(tt.cfg, newBranchCfg(false))
if err := p.ValidateType(tt.ctype); (err != nil) != tt.wantErr {
t.Errorf("MessageProcessorImpl.ValidateType() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestMessageProcessorImpl_ValidateScope(t *testing.T) {
tests := []struct {
name string
cfg CommitMessageConfig
scope string
wantErr bool
}{
{"any scope", ccfg, "aaa", false},
{"valid scope with scope list", ccfgWithScope, "scope", false},
{"invalid scope with scope list", ccfgWithScope, "aaa", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewMessageProcessor(tt.cfg, newBranchCfg(false))
if err := p.ValidateScope(tt.scope); (err != nil) != tt.wantErr {
t.Errorf("MessageProcessorImpl.ValidateScope() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestMessageProcessorImpl_ValidateDescription(t *testing.T) {
tests := []struct {
name string
cfg CommitMessageConfig
description string
wantErr bool
}{
{"empty description", ccfg, "", true},
{"sigle letter description", ccfg, "a", false},
{"number description", ccfg, "1", true},
{"valid description", ccfg, "add some feature", false},
{"invalid capital letter description", ccfg, "Add some feature", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewMessageProcessor(tt.cfg, newBranchCfg(false))
if err := p.ValidateDescription(tt.description); (err != nil) != tt.wantErr {
t.Errorf("MessageProcessorImpl.ValidateDescription() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestMessageProcessorImpl_Enhance(t *testing.T) {
tests := []struct {
name string