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

feat: add flags to commit command

This commit is contained in:
Beatriz Vieira 2021-07-31 00:47:58 -03:00
parent 1023742e7d
commit ff34b484a5
6 changed files with 227 additions and 59 deletions

View File

@ -141,19 +141,19 @@ git-sv rn -h
##### Available commands ##### Available commands
| Variable | description | has options or subcommands | | Variable | description | has options or subcommands |
| ---------------------------- | ------------------------------------------------------------- | :------------------------: | | ---------------------------- | -------------------------------------------------------------- | :------------------------: |
| config, cfg | Show config information. | :heavy_check_mark: | | config, cfg | Show config information. | :heavy_check_mark: |
| current-version, cv | Get last released version from git. | :x: | | current-version, cv | Get last released version from git. | :x: |
| next-version, nv | Generate the next version based on git commit messages. | :x: | | next-version, nv | Generate the next version based on git commit messages. | :x: |
| commit-log, cl | List all commit logs according to range as jsons. | :heavy_check_mark: | | commit-log, cl | List all commit logs according to range as jsons. | :heavy_check_mark: |
| commit-notes, cn | Generate a commit notes according to range. | :heavy_check_mark: | | commit-notes, cn | Generate a commit notes according to range. | :heavy_check_mark: |
| release-notes, rn | Generate release notes. | :heavy_check_mark: | | release-notes, rn | Generate release notes. | :heavy_check_mark: |
| changelog, cgl | Generate changelog. | :heavy_check_mark: | | changelog, cgl | Generate changelog. | :heavy_check_mark: |
| tag, tg | Generate tag with version based on git commit messages. | :x: | | 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: | | 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: | | help, h | Shows a list of commands or help for one command. | :x: |
##### Use range ##### Use range
@ -209,17 +209,17 @@ make
#### Make configs #### Make configs
| Variable | description | | Variable | description |
| ---------- | ---------------------- | | ---------- | ----------------------- |
| BUILDOS | Build OS. | | BUILDOS | Build OS. |
| BUILDARCH | Build arch. | | BUILDARCH | Build arch. |
| ECHOFLAGS | Flags used on echo. | | ECHOFLAGS | Flags used on echo. |
| BUILDENVS | Var envs used on build.| | BUILDENVS | Var envs used on build. |
| BUILDFLAGS | Flags used on build. | | BUILDFLAGS | Flags used on build. |
| Parameters | description | | Parameters | description |
| ---------- | ----------------------------------- | | ---------- | ------------------------------------ |
| args | Parameters that will be used on run.| | args | Parameters that will be used on run. |
```bash ```bash
#variables #variables

View File

@ -266,62 +266,127 @@ func tagHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) func(c *c
} }
} }
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)
}
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)
}
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
}
if fullBody.Len() > 0 {
fullBody.WriteString("\n")
}
if body != "" {
fullBody.WriteString(body)
}
}
return fullBody.String(), nil
}
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 { func commitHandler(cfg Config, git sv.Git, messageProcessor sv.MessageProcessor) func(c *cli.Context) error {
return func(c *cli.Context) error { return func(c *cli.Context) error {
ctype, err := promptType(cfg.CommitMessage.Types) 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 { if err != nil {
return err return err
} }
scope, err := promptScope(cfg.CommitMessage.Scope.Values) scope, err := getCommitScope(cfg, messageProcessor, inputScope, noScope)
if err != nil { if err != nil {
return err return err
} }
subject, err := promptSubject() subject, err := getCommitDescription(cfg, messageProcessor, inputDescription)
if err != nil { if err != nil {
return err return err
} }
var fullBody strings.Builder fullBody, err := getCommitBody(noBody)
for body, err := promptBody(); body != "" || err != nil; body, err = promptBody() {
if err != nil {
return err
}
if fullBody.Len() > 0 {
fullBody.WriteString("\n")
}
if body != "" {
fullBody.WriteString(body)
}
}
branchIssue, err := messageProcessor.IssueID(git.Branch())
if err != nil { if err != nil {
return err return err
} }
var issue string issue, err := getCommitIssue(cfg, messageProcessor, git.Branch(), noIssue)
if cfg.CommitMessage.IssueFooterConfig().Key != "" && cfg.CommitMessage.Issue.Regex != "" {
issue, err = promptIssueID("issue id", cfg.CommitMessage.Issue.Regex, branchIssue)
if err != nil {
return err
}
}
hasBreakingChanges, err := promptConfirm("has breaking changes?")
if err != nil { if err != nil {
return err return err
} }
breakingChanges := ""
if hasBreakingChanges { breakingChange, err := getCommitBreakingChange(noBreaking, inputBreakingChange)
breakingChanges, err = promptBreakingChanges() if err != nil {
if err != nil { return err
return err
}
} }
header, body, footer := messageProcessor.Format(sv.NewCommitMessage(ctype.Type, scope, subject, fullBody.String(), issue, breakingChanges)) header, body, footer := messageProcessor.Format(sv.NewCommitMessage(ctype, scope, subject, fullBody, issue, breakingChange))
err = git.Commit(header, body, footer) err = git.Commit(header, body, footer)
if err != nil { if err != nil {

View File

@ -141,6 +141,16 @@ func main() {
Aliases: []string{"cmt"}, Aliases: []string{"cmt"},
Usage: "execute git commit with convetional commit message helper", Usage: "execute git commit with convetional commit message helper",
Action: commitHandler(cfg, git, messageProcessor), 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{"bc"}, Usage: "define commit breaking change message"},
},
}, },
{ {
Name: "validate-commit-message", Name: "validate-commit-message",

View File

@ -80,7 +80,7 @@ func promptIssueID(issueLabel, issueRegex, defaultValue string) (string, error)
} }
func promptBreakingChanges() (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) { 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 { type MessageProcessor interface {
SkipBranch(branch string, detached bool) bool SkipBranch(branch string, detached bool) bool
Validate(message string) error Validate(message string) error
ValidateType(ctype string) error
ValidateScope(scope string) error
ValidateDescription(description 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)
Format(msg CommitMessage) (string, string, string) 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) 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, ", ")) return fmt.Errorf("message type should be one of [%v]", strings.Join(p.messageCfg.Types, ", "))
} }
return nil
}
if len(p.messageCfg.Scope.Values) > 0 && !contains(msg.Scope, p.messageCfg.Scope.Values) { 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 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 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) { func TestMessageProcessorImpl_Enhance(t *testing.T) {
tests := []struct { tests := []struct {
name string name string