2023-10-15 19:29:29 +00:00
|
|
|
package app
|
2019-11-17 16:17:24 +00:00
|
|
|
|
|
|
|
import (
|
2021-02-14 22:42:22 +00:00
|
|
|
"fmt"
|
2023-01-15 02:56:05 +00:00
|
|
|
"os"
|
2023-10-15 19:29:29 +00:00
|
|
|
"path/filepath"
|
2021-04-13 01:19:36 +00:00
|
|
|
"reflect"
|
2021-07-22 23:07:18 +00:00
|
|
|
|
2023-10-12 14:18:25 +00:00
|
|
|
"dario.cat/mergo"
|
2019-11-17 16:17:24 +00:00
|
|
|
"github.com/kelseyhightower/envconfig"
|
2023-10-15 19:29:29 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2023-10-12 14:18:25 +00:00
|
|
|
"github.com/thegeeklab/git-sv/v2/sv"
|
2021-02-14 22:42:22 +00:00
|
|
|
"gopkg.in/yaml.v3"
|
2019-11-17 16:17:24 +00:00
|
|
|
)
|
|
|
|
|
2021-07-31 19:03:58 +00:00
|
|
|
// EnvConfig env vars for cli configuration.
|
2021-02-14 22:42:22 +00:00
|
|
|
type EnvConfig struct {
|
2023-10-12 14:18:25 +00:00
|
|
|
Home string `envconfig:"GITSV_HOME" default:""`
|
2019-11-17 16:17:24 +00:00
|
|
|
}
|
|
|
|
|
2021-07-31 19:03:58 +00:00
|
|
|
// Config cli yaml config.
|
2021-02-14 22:42:22 +00:00
|
|
|
type Config struct {
|
|
|
|
Version string `yaml:"version"`
|
2023-10-15 19:29:29 +00:00
|
|
|
LogLevel string `yaml:"log-level"`
|
2021-02-14 22:42:22 +00:00
|
|
|
Versioning sv.VersioningConfig `yaml:"versioning"`
|
2023-10-15 19:29:29 +00:00
|
|
|
Tag TagConfig `yaml:"tag"`
|
2021-02-14 22:42:22 +00:00
|
|
|
ReleaseNotes sv.ReleaseNotesConfig `yaml:"release-notes"`
|
|
|
|
Branches sv.BranchesConfig `yaml:"branches"`
|
|
|
|
CommitMessage sv.CommitMessageConfig `yaml:"commit-message"`
|
|
|
|
}
|
|
|
|
|
2023-10-15 19:29:29 +00:00
|
|
|
func NewConfig(configDir, configFilename string) *Config {
|
|
|
|
workDir, _ := os.Getwd()
|
|
|
|
cfg := GetDefault()
|
|
|
|
|
|
|
|
envCfg := loadEnv()
|
|
|
|
if envCfg.Home != "" {
|
|
|
|
homeCfgFilepath := filepath.Join(envCfg.Home, configFilename)
|
|
|
|
if homeCfg, err := readFile(homeCfgFilepath); err == nil {
|
|
|
|
if merr := merge(cfg, migrate(homeCfg, homeCfgFilepath)); merr != nil {
|
|
|
|
log.Fatal().Err(merr).Msg("failed to merge user config")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
repoCfgFilepath := filepath.Join(workDir, configDir, configFilename)
|
|
|
|
if repoCfg, err := readFile(repoCfgFilepath); err == nil {
|
|
|
|
if merr := merge(cfg, migrate(repoCfg, repoCfgFilepath)); merr != nil {
|
|
|
|
log.Fatal().Err(merr).Msg("failed to merge repo config")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(repoCfg.ReleaseNotes.Headers) > 0 { // mergo is merging maps, headers will be overwritten
|
|
|
|
cfg.ReleaseNotes.Headers = repoCfg.ReleaseNotes.Headers
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cfg
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadEnv() EnvConfig {
|
|
|
|
var c EnvConfig
|
|
|
|
|
|
|
|
err := envconfig.Process("", &c)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal().Err(err).Msg("failed to load env config")
|
|
|
|
}
|
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func readFile(filepath string) (Config, error) {
|
2023-01-15 02:56:05 +00:00
|
|
|
content, rerr := os.ReadFile(filepath)
|
2021-02-14 22:42:22 +00:00
|
|
|
if rerr != nil {
|
|
|
|
return Config{}, rerr
|
|
|
|
}
|
|
|
|
|
|
|
|
var cfg Config
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2021-02-14 22:42:22 +00:00
|
|
|
cerr := yaml.Unmarshal(content, &cfg)
|
|
|
|
if cerr != nil {
|
2023-10-12 14:18:25 +00:00
|
|
|
return Config{}, fmt.Errorf("could not parse config from path: %s, error: %w", filepath, cerr)
|
2021-02-14 22:42:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return cfg, nil
|
|
|
|
}
|
|
|
|
|
2023-10-15 19:29:29 +00:00
|
|
|
func GetDefault() *Config {
|
2021-03-04 03:42:51 +00:00
|
|
|
skipDetached := false
|
2023-01-22 02:29:44 +00:00
|
|
|
pattern := "%d.%d.%d"
|
|
|
|
filter := ""
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2023-10-15 19:29:29 +00:00
|
|
|
return &Config{
|
2022-02-28 23:47:45 +00:00
|
|
|
Version: "1.1",
|
2021-02-14 22:42:22 +00:00
|
|
|
Versioning: sv.VersioningConfig{
|
|
|
|
UpdateMajor: []string{},
|
|
|
|
UpdateMinor: []string{"feat"},
|
|
|
|
UpdatePatch: []string{"build", "ci", "chore", "docs", "fix", "perf", "refactor", "style", "test"},
|
|
|
|
IgnoreUnknown: false,
|
|
|
|
},
|
2023-10-15 19:29:29 +00:00
|
|
|
Tag: TagConfig{
|
2023-01-22 02:29:44 +00:00
|
|
|
Pattern: &pattern,
|
|
|
|
Filter: &filter,
|
2022-03-31 13:57:22 +00:00
|
|
|
},
|
2022-02-07 23:40:53 +00:00
|
|
|
ReleaseNotes: sv.ReleaseNotesConfig{
|
|
|
|
Sections: []sv.ReleaseNotesSectionConfig{
|
2022-02-08 01:49:32 +00:00
|
|
|
{Name: "Features", SectionType: sv.ReleaseNotesSectionTypeCommits, CommitTypes: []string{"feat"}},
|
|
|
|
{Name: "Bug Fixes", SectionType: sv.ReleaseNotesSectionTypeCommits, CommitTypes: []string{"fix"}},
|
|
|
|
{Name: "Breaking Changes", SectionType: sv.ReleaseNotesSectionTypeBreakingChanges},
|
2022-02-07 23:40:53 +00:00
|
|
|
},
|
|
|
|
},
|
2021-02-14 22:42:22 +00:00
|
|
|
Branches: sv.BranchesConfig{
|
2021-07-31 19:39:38 +00:00
|
|
|
Prefix: "([a-z]+\\/)?",
|
|
|
|
Suffix: "(-.*)?",
|
2021-02-14 22:42:22 +00:00
|
|
|
DisableIssue: false,
|
|
|
|
Skip: []string{"master", "main", "developer"},
|
2021-03-04 03:42:51 +00:00
|
|
|
SkipDetached: &skipDetached,
|
2021-02-14 22:42:22 +00:00
|
|
|
},
|
|
|
|
CommitMessage: sv.CommitMessageConfig{
|
|
|
|
Types: []string{"build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"},
|
|
|
|
Scope: sv.CommitMessageScopeConfig{},
|
|
|
|
Footer: map[string]sv.CommitMessageFooterConfig{
|
2021-02-15 03:05:43 +00:00
|
|
|
"issue": {Key: "jira", KeySynonyms: []string{"Jira", "JIRA"}},
|
2021-02-14 22:42:22 +00:00
|
|
|
},
|
2023-01-15 02:56:05 +00:00
|
|
|
Issue: sv.CommitMessageIssueConfig{Regex: "[A-Z]+-[0-9]+"},
|
2022-03-31 13:57:22 +00:00
|
|
|
HeaderSelector: "",
|
2021-02-14 22:42:22 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2021-04-13 01:19:36 +00:00
|
|
|
|
|
|
|
func merge(dst *Config, src Config) error {
|
|
|
|
err := mergo.Merge(dst, src, mergo.WithOverride, mergo.WithTransformers(&mergeTransformer{}))
|
|
|
|
if err == nil {
|
|
|
|
if len(src.ReleaseNotes.Headers) > 0 { // mergo is merging maps, ReleaseNotes.Headers should be overwritten
|
|
|
|
dst.ReleaseNotes.Headers = src.ReleaseNotes.Headers
|
|
|
|
}
|
|
|
|
}
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2021-04-13 01:19:36 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-07-22 23:07:18 +00:00
|
|
|
type mergeTransformer struct{}
|
2021-04-13 01:19:36 +00:00
|
|
|
|
|
|
|
func (t *mergeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
|
|
|
|
if typ.Kind() == reflect.Slice {
|
|
|
|
return func(dst, src reflect.Value) error {
|
|
|
|
if dst.CanSet() && !src.IsNil() {
|
|
|
|
dst.Set(src)
|
|
|
|
}
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2021-04-13 01:19:36 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if typ.Kind() == reflect.Ptr {
|
|
|
|
return func(dst, src reflect.Value) error {
|
|
|
|
if dst.CanSet() && !src.IsNil() {
|
|
|
|
dst.Set(src)
|
|
|
|
}
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2021-04-13 01:19:36 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2021-04-13 01:19:36 +00:00
|
|
|
return nil
|
|
|
|
}
|
2022-02-07 23:40:53 +00:00
|
|
|
|
2023-10-15 19:29:29 +00:00
|
|
|
func migrate(cfg Config, filename string) Config {
|
2022-02-07 23:40:53 +00:00
|
|
|
if cfg.ReleaseNotes.Headers == nil {
|
|
|
|
return cfg
|
|
|
|
}
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2023-10-15 19:29:29 +00:00
|
|
|
log.Warn().Msgf("config 'release-notes.headers' on %s is deprecated, please use 'sections' instead!", filename)
|
2022-02-07 23:40:53 +00:00
|
|
|
|
|
|
|
return Config{
|
|
|
|
Version: cfg.Version,
|
|
|
|
Versioning: cfg.Versioning,
|
|
|
|
Tag: cfg.Tag,
|
|
|
|
ReleaseNotes: sv.ReleaseNotesConfig{
|
2023-10-15 19:29:29 +00:00
|
|
|
Sections: migrateReleaseNotes(cfg.ReleaseNotes.Headers),
|
2022-02-07 23:40:53 +00:00
|
|
|
},
|
|
|
|
Branches: cfg.Branches,
|
|
|
|
CommitMessage: cfg.CommitMessage,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-15 19:29:29 +00:00
|
|
|
func migrateReleaseNotes(headers map[string]string) []sv.ReleaseNotesSectionConfig {
|
2022-02-07 23:40:53 +00:00
|
|
|
order := []string{"feat", "fix", "refactor", "perf", "test", "build", "ci", "chore", "docs", "style"}
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2022-02-07 23:40:53 +00:00
|
|
|
var sections []sv.ReleaseNotesSectionConfig
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2022-02-07 23:40:53 +00:00
|
|
|
for _, key := range order {
|
|
|
|
if name, exists := headers[key]; exists {
|
2023-10-12 14:18:25 +00:00
|
|
|
sections = append(
|
|
|
|
sections,
|
|
|
|
sv.ReleaseNotesSectionConfig{
|
|
|
|
Name: name,
|
|
|
|
SectionType: sv.ReleaseNotesSectionTypeCommits,
|
|
|
|
CommitTypes: []string{key},
|
|
|
|
})
|
2022-02-07 23:40:53 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2022-02-07 23:40:53 +00:00
|
|
|
if name, exists := headers["breaking-change"]; exists {
|
2023-10-12 14:18:25 +00:00
|
|
|
sections = append(
|
|
|
|
sections,
|
|
|
|
sv.ReleaseNotesSectionConfig{
|
|
|
|
Name: name,
|
|
|
|
SectionType: sv.ReleaseNotesSectionTypeBreakingChanges,
|
|
|
|
})
|
2022-02-07 23:40:53 +00:00
|
|
|
}
|
2023-10-12 14:18:25 +00:00
|
|
|
|
2022-02-07 23:40:53 +00:00
|
|
|
return sections
|
|
|
|
}
|
2023-10-15 19:29:29 +00:00
|
|
|
|
|
|
|
// ==== Message ====
|
|
|
|
|
|
|
|
// CommitMessageConfig config a commit message.
|
|
|
|
|
|
|
|
// ==== Branches ====
|
|
|
|
|
|
|
|
// ==== Versioning ====
|
|
|
|
|
|
|
|
// ==== Tag ====
|
|
|
|
|
|
|
|
// TagConfig tag preferences.
|
|
|
|
type TagConfig struct {
|
|
|
|
Pattern *string `yaml:"pattern"`
|
|
|
|
Filter *string `yaml:"filter"`
|
|
|
|
}
|