0
0
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:
Beatriz Vieira 2021-02-14 19:42:22 -03:00
parent 9b63aacd8d
commit e70283a0c5
10 changed files with 190 additions and 68 deletions

View File

@ -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]+"},
},
}
}

View File

@ -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()

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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"`
}

View File

@ -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
}

View File

@ -17,7 +17,6 @@ var ccfg = CommitMessageConfig{
}
var bcfg = BranchesConfig{
ExpectIssue: true,
PrefixRegex: "([a-z]+\\/)?",
SuffixRegex: "(-.*)?",
Skip: []string{"develop", "master"},

View File

@ -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),

View File

@ -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)
}