Skip to content

Commit 9dc539a

Browse files
authored
feat(template): support multiple templates (#14)
template file .git-carc, add dry-run flag
1 parent 5f52906 commit 9dc539a

File tree

14 files changed

+452
-65
lines changed

14 files changed

+452
-65
lines changed

README.md

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,105 @@
11
# commitizen
22

3-
The commitizen command line utility. Inspired by the [cz-cli](https://github.com/commitizen/cz-cli)
3+
Command line utility to standardize git commit messages, golang version. Forked from [commitizen-go](https://github.com/lintingzhen/commitizen-go).
4+
5+
The [survey](https://github.com/AlecAivazis/survey) project is no longer maintained. Therefore, this project uses [bubbletea](https://github.com/charmbracelet/bubbletea) instead.
6+
7+
## Getting Started
8+
9+
installation with source code:
10+
11+
```
12+
$ make && make install
13+
```
14+
15+
or
16+
17+
```
18+
$ make && ./commitizen-go install
19+
```
20+
21+
commit with commitizen:
22+
23+
```
24+
$ git cz
25+
```
26+
27+
## Usage
28+
29+
```
30+
Command line utility to standardize git commit messages.
31+
32+
Usage:
33+
commitizen
34+
commitizen [command]
35+
36+
Available Commands:
37+
init Initialize this tool to git-core as git-cz.
38+
load Load templates.
39+
help Help about any command
40+
41+
Flags:
42+
-s, --signoff Add a Signed-off-by trailer by the committer at the end of the commit log message.
43+
-a, --add Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected.
44+
-h, --help help for commitizen
45+
46+
Use "commitizen [command] --help" for more information about a command.
47+
```
48+
49+
## Configure
50+
51+
You can set configuration file that .git-czrc at repository root or home directory. (You can also add the extension to file, like .git-czrc.yaml) The configure file that located in repository root have a priority over the one in home directory. The format is the same as the defaultConfig string in the file [commit/defaultConfig.go]().
52+
53+
Type item like that:
54+
55+
```
56+
package render
57+
58+
const DefaultCommitTemplate = `---
59+
name: default
60+
items:
61+
- name: type
62+
desc: "Select the type of change that you're committing:"
63+
type: select
64+
options:
65+
- name: feat
66+
desc: "A new feature"
67+
- name: fix
68+
desc: "A bug fix"
69+
- name: docs
70+
desc: "Documentation only changes"
71+
- name: test
72+
desc: "Adding missing tests"
73+
- name: WIP
74+
desc: "Work in progress"
75+
- name: chore
76+
desc: "Changes to the build process or auxiliary tools\n and libraries such as documentation generation"
77+
- name: style
78+
desc: "Changes that do not affect the meaning of the code\n (white-space, formatting, missing semi-colons, etc)"
79+
- name: refactor
80+
desc: "A code change that neither fixes a bug nor adds a feature"
81+
- name: perf
82+
desc: "A code change that improves performance"
83+
- name: revert
84+
desc: "Revert to a commit"
85+
- name: scope
86+
desc: "Scope. Could be anything specifying place of the commit change:"
87+
type: input
88+
- name: subject
89+
desc: "Subject. Concise description of the changes. Imperative, lower case and no final dot:"
90+
type: input
91+
required: true
92+
- name: body
93+
desc: "Body. Motivation for the change and contrast this with previous behavior:"
94+
type: textarea
95+
- name: footer
96+
desc: "Footer. Information about Breaking Changes and reference issues that this commit closes:"
97+
type: textarea
98+
format: "{{.type}}{{with .scope}}({{.}}){{end}}: {{.subject}}{{with .body}}\n\n{{.}}{{end}}{{with .footer}}\n\n{{.}}{{end}}"`
99+
```
100+
101+
Template like that:
102+
103+
```
104+
format: "{{.type}}{{with .scope}}({{.}}){{end}}: {{.subject}}{{with .body}}\n\n{{.}}{{end}}{{with .footer}}\n\n{{.}}{{end}}"
105+
```

cmd/cz/cz.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import (
66

77
"github.com/spf13/cobra"
88

9+
"github.com/shipengqi/commitizen/internal/config"
910
"github.com/shipengqi/commitizen/internal/git"
1011
"github.com/shipengqi/commitizen/internal/options"
11-
"github.com/shipengqi/commitizen/internal/render"
1212
)
1313

1414
func New() *cobra.Command {
@@ -24,15 +24,26 @@ func New() *cobra.Command {
2424
if !isRepo {
2525
return errors.New("not a git repository")
2626
}
27-
tmpl, err := render.Load([]byte(render.DefaultCommitTemplate))
27+
28+
conf := config.New()
29+
err = conf.Initialize()
30+
if err != nil {
31+
return err
32+
}
33+
tmpl, err := conf.Run()
2834
if err != nil {
2935
return err
3036
}
37+
3138
msg, err := tmpl.Run()
3239
if err != nil {
3340
return err
3441
}
3542

43+
if o.DryRun {
44+
fmt.Println(string(msg))
45+
return nil
46+
}
3647
output, err := git.Commit(msg, o.GitOptions)
3748
if err != nil {
3849
return err
@@ -53,7 +64,6 @@ func New() *cobra.Command {
5364
o.AddFlags(f)
5465

5566
c.AddCommand(NewInitCmd())
56-
c.AddCommand(NewLoadCmd())
5767

5868
return c
5969
}

cmd/cz/load.go

Lines changed: 0 additions & 17 deletions
This file was deleted.

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/shipengqi/commitizen
33
go 1.21
44

55
require (
6-
github.com/charmbracelet/bubbles v0.17.1
6+
github.com/charmbracelet/bubbles v0.18.0
77
github.com/charmbracelet/bubbletea v0.25.0
88
github.com/charmbracelet/lipgloss v0.9.1
99
github.com/shipengqi/golib v0.2.10
@@ -26,7 +26,7 @@ require (
2626
github.com/muesli/cancelreader v0.2.2 // indirect
2727
github.com/muesli/reflow v0.3.0 // indirect
2828
github.com/muesli/termenv v0.15.2 // indirect
29-
github.com/rivo/uniseg v0.2.0 // indirect
29+
github.com/rivo/uniseg v0.4.6 // indirect
3030
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
3131
golang.org/x/sync v0.1.0 // indirect
3232
golang.org/x/sys v0.17.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
44
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
55
github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4=
66
github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o=
7+
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
8+
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
79
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
810
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
911
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
@@ -44,6 +46,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
4446
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
4547
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
4648
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
49+
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
50+
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
4751
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
4852
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
4953
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=

internal/config/config.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package config
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
10+
tea "github.com/charmbracelet/bubbletea"
11+
"github.com/shipengqi/golib/convutil"
12+
"github.com/shipengqi/golib/fsutil"
13+
"github.com/shipengqi/golib/sysutil"
14+
"gopkg.in/yaml.v3"
15+
16+
"github.com/shipengqi/commitizen/internal/render"
17+
"github.com/shipengqi/commitizen/internal/ui"
18+
)
19+
20+
const RCFilename = ".git-czrc"
21+
22+
type Config struct {
23+
defaultTmpl *render.Template
24+
others []*render.Template
25+
}
26+
27+
func New() *Config {
28+
return &Config{}
29+
}
30+
31+
func (c *Config) Initialize() error {
32+
var fpath string
33+
if fsutil.IsExists(RCFilename) {
34+
fpath = RCFilename
35+
} else {
36+
home := sysutil.HomeDir()
37+
p := filepath.Join(home, RCFilename)
38+
if fsutil.IsExists(p) {
39+
fpath = p
40+
}
41+
}
42+
43+
tmpls, err := LoadTemplates(fpath)
44+
if err != nil {
45+
return err
46+
}
47+
for _, v := range tmpls {
48+
if v.Default {
49+
c.defaultTmpl = v
50+
continue
51+
}
52+
c.others = append(c.others, v)
53+
}
54+
// If the user has not configured a default template, the built-in template is used
55+
if c.defaultTmpl == nil {
56+
defaults, err := Load(convutil.S2B(DefaultCommitTemplate))
57+
if err != nil {
58+
return err
59+
}
60+
c.defaultTmpl = defaults[0]
61+
}
62+
63+
return nil
64+
}
65+
66+
func (c *Config) Run() (*render.Template, error) {
67+
if len(c.others) > 0 {
68+
model := c.createTemplatesSelect("Select the template to be used for this commit")
69+
if _, err := tea.NewProgram(model).Run(); err != nil {
70+
return nil, err
71+
}
72+
if model.Canceled() {
73+
return nil, render.ErrCanceled
74+
}
75+
val := model.Value()
76+
if val == c.defaultTmpl.Name {
77+
return c.defaultTmpl, nil
78+
}
79+
for _, v := range c.others {
80+
if v.Name == val {
81+
return v, nil
82+
}
83+
}
84+
}
85+
return c.defaultTmpl, nil
86+
}
87+
88+
func (c *Config) createTemplatesSelect(label string) *ui.SelectModel {
89+
var choices ui.Choices
90+
var all []*render.Template
91+
all = append(all, c.others...)
92+
all = append(all, c.defaultTmpl)
93+
// list custom templates and default templates
94+
for _, v := range all {
95+
choices = append(choices, ui.Choice(v.Name))
96+
}
97+
m := ui.NewSelect(label, choices)
98+
return m
99+
}
100+
101+
func LoadTemplates(file string) ([]*render.Template, error) {
102+
if len(file) == 0 {
103+
return nil, nil
104+
}
105+
fd, err := os.Open(file)
106+
if err != nil {
107+
return nil, err
108+
}
109+
defer func() { _ = fd.Close() }()
110+
return load(fd)
111+
}
112+
113+
func Load(data []byte) ([]*render.Template, error) {
114+
return load(bytes.NewReader(data))
115+
}
116+
117+
func load(reader io.Reader) ([]*render.Template, error) {
118+
var templates []*render.Template
119+
d := yaml.NewDecoder(reader)
120+
for {
121+
tmpl := new(render.Template)
122+
err := d.Decode(tmpl)
123+
if err != nil {
124+
if errors.Is(err, io.EOF) {
125+
break
126+
}
127+
return nil, err
128+
}
129+
if tmpl == nil {
130+
continue
131+
}
132+
templates = append(templates, tmpl)
133+
}
134+
135+
return templates, nil
136+
}

internal/config/config_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package config
2+
3+
import "testing"
4+
5+
func TestLoadTemplates(t *testing.T) {
6+
ts, err := LoadTemplates("./testdata/.git-czrc")
7+
if err != nil {
8+
t.Log(err)
9+
}
10+
t.Log(ts)
11+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
package render
1+
package config
22

33
const DefaultCommitTemplate = `---
44
name: default
5+
default: true
56
items:
67
- name: type
78
desc: "Select the type of change that you're committing:"

0 commit comments

Comments
 (0)