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:
commit
c037311d1a
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user