mirror of
https://github.com/thegeeklab/git-sv.git
synced 2024-11-21 22:10:39 +00:00
feat: config sv4git using yaml
BREAKING CHANGE: stop using var envs to config sv4git
This commit is contained in:
parent
9b63aacd8d
commit
e70283a0c5
@ -1,13 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sv4git/sv"
|
||||
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Config env vars for cli configuration
|
||||
type Config struct {
|
||||
// EnvConfig env vars for cli configuration
|
||||
type EnvConfig struct {
|
||||
Home string `envconfig:"SV4GIT_HOME" default:""`
|
||||
|
||||
MajorVersionTypes []string `envconfig:"MAJOR_VERSION_TYPES" default:""`
|
||||
MinorVersionTypes []string `envconfig:"MINOR_VERSION_TYPES" default:"feat"`
|
||||
PatchVersionTypes []string `envconfig:"PATCH_VERSION_TYPES" default:"build,ci,chore,docs,fix,perf,refactor,style,test"`
|
||||
@ -24,11 +33,74 @@ type Config struct {
|
||||
BranchIssueSuffixRegex string `envconfig:"BRANCH_ISSUE_SUFFIX_REGEX" default:"(-.*)?"`
|
||||
}
|
||||
|
||||
func loadConfig() Config {
|
||||
var c Config
|
||||
func loadEnvConfig() EnvConfig {
|
||||
var c EnvConfig
|
||||
err := envconfig.Process("SV4GIT", &c)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Config cli yaml config
|
||||
type Config struct {
|
||||
Version string `yaml:"version"`
|
||||
Versioning sv.VersioningConfig `yaml:"versioning"`
|
||||
Tag sv.TagConfig `yaml:"tag"`
|
||||
ReleaseNotes sv.ReleaseNotesConfig `yaml:"release-notes"`
|
||||
Branches sv.BranchesConfig `yaml:"branches"`
|
||||
CommitMessage sv.CommitMessageConfig `yaml:"commit-message"`
|
||||
}
|
||||
|
||||
func getRepoPath() (string, error) {
|
||||
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", errors.New(string(out))
|
||||
}
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
|
||||
func loadConfig(filepath string) (Config, error) {
|
||||
content, rerr := ioutil.ReadFile(filepath)
|
||||
if rerr != nil {
|
||||
return Config{}, rerr
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
cerr := yaml.Unmarshal(content, &cfg)
|
||||
if cerr != nil {
|
||||
return Config{}, fmt.Errorf("could not parse config from path: %s, error: %v", filepath, cerr)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func defaultConfig() Config {
|
||||
return Config{
|
||||
Version: "1.0",
|
||||
Versioning: sv.VersioningConfig{
|
||||
UpdateMajor: []string{},
|
||||
UpdateMinor: []string{"feat"},
|
||||
UpdatePatch: []string{"build", "ci", "chore", "docs", "fix", "perf", "refactor", "style", "test"},
|
||||
IgnoreUnknown: false,
|
||||
},
|
||||
Tag: sv.TagConfig{Pattern: "%d.%d.%d"},
|
||||
ReleaseNotes: sv.ReleaseNotesConfig{Headers: map[string]string{"fix": "Bug Fixes", "feat": "Features", "breaking-change": "Breaking Changes"}},
|
||||
Branches: sv.BranchesConfig{
|
||||
PrefixRegex: "([a-z]+\\/)?",
|
||||
SuffixRegex: "(-.*)?",
|
||||
DisableIssue: false,
|
||||
Skip: []string{"master", "main", "developer"},
|
||||
},
|
||||
CommitMessage: sv.CommitMessageConfig{
|
||||
Types: []string{"build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"},
|
||||
Scope: sv.CommitMessageScopeConfig{},
|
||||
Footer: map[string]sv.CommitMessageFooterConfig{
|
||||
"issue": {Key: "jira", KeySynonyms: []string{"Jira", "JIRA"}},
|
||||
"breaking-change": {Key: "BREAKING CHANGE", KeySynonyms: []string{"BREAKING CHANGES"}},
|
||||
},
|
||||
Issue: sv.CommitMessageIssueConfig{Regex: "[A-Z]+-[0-9]+"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,32 @@ import (
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func configDefaultHandler() func(c *cli.Context) error {
|
||||
cfg := defaultConfig()
|
||||
return func(c *cli.Context) error {
|
||||
content, err := yaml.Marshal(&cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(content))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func configShowHandler(cfg Config) func(c *cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
content, err := yaml.Marshal(&cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(content))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func currentVersionHandler(git sv.Git) func(c *cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
describe := git.Describe()
|
||||
@ -238,7 +262,7 @@ 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 {
|
||||
func commitHandler(cfg EnvConfig, git sv.Git, messageProcessor sv.MessageProcessor) func(c *cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
|
||||
ctype, err := promptType()
|
||||
|
@ -3,49 +3,51 @@ package main
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sv4git/sv"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Version for git-sv
|
||||
var Version = ""
|
||||
|
||||
const (
|
||||
configFilename = "config.yml"
|
||||
repoConfigFilename = ".sv4git.yml"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
|
||||
cfg := loadConfig()
|
||||
envCfg := loadEnvConfig()
|
||||
|
||||
// TODO: config using yaml
|
||||
commitMessageCfg := sv.CommitMessageConfig{
|
||||
Types: cfg.CommitMessageTypes,
|
||||
Scope: sv.CommitMessageScopeConfig{},
|
||||
Footer: map[string]sv.CommitMessageFooterConfig{
|
||||
"issue": {Key: cfg.IssueIDPrefixes[0], KeySynonyms: cfg.IssueIDPrefixes[1:]},
|
||||
"breaking-change": {Key: cfg.BreakingChangePrefixes[0], KeySynonyms: cfg.BreakingChangePrefixes[1:]},
|
||||
},
|
||||
Issue: sv.CommitMessageIssueConfig{Regex: cfg.IssueRegex},
|
||||
}
|
||||
branchesConfig := sv.BranchesConfig{
|
||||
Skip: cfg.ValidateMessageSkipBranches,
|
||||
ExpectIssue: true,
|
||||
PrefixRegex: cfg.BranchIssuePrefixRegex,
|
||||
SuffixRegex: cfg.BranchIssueSuffixRegex,
|
||||
}
|
||||
versioningConfig := sv.VersioningConfig{
|
||||
UpdateMajor: cfg.MajorVersionTypes,
|
||||
UpdateMinor: cfg.MinorVersionTypes,
|
||||
UpdatePatch: cfg.PatchVersionTypes,
|
||||
UnknownTypeAsPatch: cfg.IncludeUnknownTypeAsPatch,
|
||||
}
|
||||
tagConfig := sv.TagConfig{Pattern: cfg.TagPattern}
|
||||
releaseNotesConfig := sv.ReleaseNotesConfig{Headers: cfg.ReleaseNotesTags}
|
||||
////
|
||||
cfg := defaultConfig()
|
||||
|
||||
messageProcessor := sv.NewMessageProcessor(commitMessageCfg, branchesConfig)
|
||||
git := sv.NewGit(messageProcessor, tagConfig)
|
||||
semverProcessor := sv.NewSemVerCommitsProcessor(versioningConfig)
|
||||
releasenotesProcessor := sv.NewReleaseNoteProcessor(releaseNotesConfig)
|
||||
if envCfg.Home != "" {
|
||||
if homeCfg, err := loadConfig(filepath.Join(envCfg.Home, configFilename)); err == nil {
|
||||
if merr := mergo.Merge(&cfg, homeCfg, mergo.WithOverride); merr != nil {
|
||||
log.Fatal(merr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repoPath, rerr := getRepoPath()
|
||||
if rerr != nil {
|
||||
log.Fatal(rerr)
|
||||
}
|
||||
|
||||
if repoCfg, err := loadConfig(filepath.Join(repoPath, repoConfigFilename)); err == nil {
|
||||
if merr := mergo.Merge(&cfg, repoCfg, mergo.WithOverride); merr != nil {
|
||||
log.Fatal(merr)
|
||||
}
|
||||
}
|
||||
|
||||
messageProcessor := sv.NewMessageProcessor(cfg.CommitMessage, cfg.Branches)
|
||||
git := sv.NewGit(messageProcessor, cfg.Tag)
|
||||
semverProcessor := sv.NewSemVerCommitsProcessor(cfg.Versioning)
|
||||
releasenotesProcessor := sv.NewReleaseNoteProcessor(cfg.ReleaseNotes)
|
||||
outputFormatter := sv.NewOutputFormatter()
|
||||
|
||||
app := cli.NewApp()
|
||||
@ -53,6 +55,23 @@ func main() {
|
||||
app.Version = Version
|
||||
app.Usage = "semantic version for git"
|
||||
app.Commands = []*cli.Command{
|
||||
{
|
||||
Name: "config",
|
||||
Aliases: []string{"cfg"},
|
||||
Usage: "cli configuration",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "default",
|
||||
Usage: "show default config",
|
||||
Action: configDefaultHandler(),
|
||||
},
|
||||
{
|
||||
Name: "show",
|
||||
Usage: "show current config",
|
||||
Action: configShowHandler(cfg),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "current-version",
|
||||
Aliases: []string{"cv"},
|
||||
@ -117,7 +136,7 @@ func main() {
|
||||
Name: "commit",
|
||||
Aliases: []string{"cmt"},
|
||||
Usage: "execute git commit with convetional commit message helper",
|
||||
Action: commitHandler(cfg, git, messageProcessor),
|
||||
Action: commitHandler(envCfg, git, messageProcessor),
|
||||
},
|
||||
{
|
||||
Name: "validate-commit-message",
|
||||
|
2
go.mod
2
go.mod
@ -5,6 +5,7 @@ go 1.15
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lunixbochs/vtclean v1.0.0 // indirect
|
||||
@ -14,4 +15,5 @@ require (
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
6
go.sum
6
go.sum
@ -12,6 +12,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
@ -61,3 +63,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
40
sv/config.go
40
sv/config.go
@ -4,10 +4,10 @@ package sv
|
||||
|
||||
// CommitMessageConfig config a commit message.
|
||||
type CommitMessageConfig struct {
|
||||
Types []string
|
||||
Scope CommitMessageScopeConfig
|
||||
Footer map[string]CommitMessageFooterConfig
|
||||
Issue CommitMessageIssueConfig
|
||||
Types []string `yaml:"types"`
|
||||
Scope CommitMessageScopeConfig `yaml:"scope"`
|
||||
Footer map[string]CommitMessageFooterConfig `yaml:"footer"`
|
||||
Issue CommitMessageIssueConfig `yaml:"issue"`
|
||||
}
|
||||
|
||||
// IssueConfig config for issue.
|
||||
@ -28,52 +28,52 @@ func (c CommitMessageConfig) BreakingChangeConfig() CommitMessageFooterConfig {
|
||||
|
||||
// CommitMessageScopeConfig config scope preferences.
|
||||
type CommitMessageScopeConfig struct {
|
||||
Mandatory bool
|
||||
Values []string
|
||||
Mandatory bool `yaml:"mandatory"`
|
||||
Values []string `yaml:"values"`
|
||||
}
|
||||
|
||||
// CommitMessageFooterConfig config footer metadata.
|
||||
type CommitMessageFooterConfig struct {
|
||||
Key string
|
||||
KeySynonyms []string
|
||||
UseHash bool
|
||||
Key string `yaml:"key"`
|
||||
KeySynonyms []string `yaml:"key-synonyms"`
|
||||
UseHash bool `yaml:"use-hash"`
|
||||
}
|
||||
|
||||
// CommitMessageIssueConfig issue preferences.
|
||||
type CommitMessageIssueConfig struct {
|
||||
Regex string
|
||||
Regex string `yaml:"regex"`
|
||||
}
|
||||
|
||||
// ==== Branches ====
|
||||
|
||||
// BranchesConfig branches preferences.
|
||||
type BranchesConfig struct {
|
||||
PrefixRegex string
|
||||
SuffixRegex string
|
||||
ExpectIssue bool
|
||||
Skip []string
|
||||
PrefixRegex string `yaml:"prefix"`
|
||||
SuffixRegex string `yaml:"sufix"`
|
||||
DisableIssue bool `yaml:"disable-issue"`
|
||||
Skip []string `yaml:"skip"`
|
||||
}
|
||||
|
||||
// ==== Versioning ====
|
||||
|
||||
// VersioningConfig versioning preferences.
|
||||
type VersioningConfig struct {
|
||||
UpdateMajor []string
|
||||
UpdateMinor []string
|
||||
UpdatePatch []string
|
||||
UnknownTypeAsPatch bool
|
||||
UpdateMajor []string `yaml:"update-major"`
|
||||
UpdateMinor []string `yaml:"update-minor"`
|
||||
UpdatePatch []string `yaml:"update-patch"`
|
||||
IgnoreUnknown bool `yaml:"ignore-unknown"`
|
||||
}
|
||||
|
||||
// ==== Tag ====
|
||||
|
||||
// TagConfig tag preferences.
|
||||
type TagConfig struct {
|
||||
Pattern string
|
||||
Pattern string `yaml:"pattern"`
|
||||
}
|
||||
|
||||
// ==== Release Notes ====
|
||||
|
||||
// ReleaseNotesConfig release notes preferences.
|
||||
type ReleaseNotesConfig struct {
|
||||
Headers map[string]string
|
||||
Headers map[string]string `yaml:"headers"`
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func (p MessageProcessorImpl) Validate(message string) error {
|
||||
|
||||
// Enhance add metadata on commit message.
|
||||
func (p MessageProcessorImpl) Enhance(branch string, message string) (string, error) {
|
||||
if !p.branchesCfg.ExpectIssue || p.messageCfg.IssueConfig().Key == "" || hasIssueID(message, p.messageCfg.IssueConfig().Key) {
|
||||
if p.branchesCfg.DisableIssue || p.messageCfg.IssueConfig().Key == "" || hasIssueID(message, p.messageCfg.IssueConfig().Key) {
|
||||
return "", nil //enhance disabled
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ var ccfg = CommitMessageConfig{
|
||||
}
|
||||
|
||||
var bcfg = BranchesConfig{
|
||||
ExpectIssue: true,
|
||||
PrefixRegex: "([a-z]+\\/)?",
|
||||
SuffixRegex: "(-.*)?",
|
||||
Skip: []string{"develop", "master"},
|
||||
|
@ -40,7 +40,7 @@ type SemVerCommitsProcessorImpl struct {
|
||||
// NewSemVerCommitsProcessor SemanticVersionCommitsProcessorImpl constructor
|
||||
func NewSemVerCommitsProcessor(cfg VersioningConfig) *SemVerCommitsProcessorImpl {
|
||||
return &SemVerCommitsProcessorImpl{
|
||||
IncludeUnknownTypeAsPatch: cfg.UnknownTypeAsPatch,
|
||||
IncludeUnknownTypeAsPatch: !cfg.IgnoreUnknown,
|
||||
MajorVersionTypes: toMap(cfg.UpdateMajor),
|
||||
MinorVersionTypes: toMap(cfg.UpdateMinor),
|
||||
PatchVersionTypes: toMap(cfg.UpdatePatch),
|
||||
|
@ -10,14 +10,14 @@ import (
|
||||
func TestSemVerCommitsProcessorImpl_NextVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
unknownAsPatch bool
|
||||
ignoreUnknown bool
|
||||
version semver.Version
|
||||
commits []GitCommitLog
|
||||
want semver.Version
|
||||
}{
|
||||
{"no update", false, version("0.0.0"), []GitCommitLog{}, version("0.0.0")},
|
||||
{"no update on unknown type", false, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.0")},
|
||||
{"update patch on unknown type", true, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.1")},
|
||||
{"no update", true, version("0.0.0"), []GitCommitLog{}, version("0.0.0")},
|
||||
{"no update on unknown type", true, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.0")},
|
||||
{"update patch on unknown type", false, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.1")},
|
||||
{"patch update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{})}, version("0.0.1")},
|
||||
{"minor update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("minor", map[string]string{})}, version("0.1.0")},
|
||||
{"major update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("major", map[string]string{})}, version("1.0.0")},
|
||||
@ -25,7 +25,7 @@ func TestSemVerCommitsProcessorImpl_NextVersion(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := NewSemVerCommitsProcessor(VersioningConfig{UpdateMajor: []string{"major"}, UpdateMinor: []string{"minor"}, UpdatePatch: []string{"patch"}, UnknownTypeAsPatch: tt.unknownAsPatch})
|
||||
p := NewSemVerCommitsProcessor(VersioningConfig{UpdateMajor: []string{"major"}, UpdateMinor: []string{"minor"}, UpdatePatch: []string{"patch"}, IgnoreUnknown: tt.ignoreUnknown})
|
||||
if got := p.NextVersion(tt.version, tt.commits); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("SemVerCommitsProcessorImpl.NextVersion() = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user