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
46
README.md
46
README.md
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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{"b"}, Usage: "define commit breaking change message"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "validate-commit-message",
|
Name: "validate-commit-message",
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user