From a5c860e3f4bfdcfa0e6b8ff1411e81faed2b8db7 Mon Sep 17 00:00:00 2001 From: Walter Scheper Date: Tue, 22 Mar 2022 12:28:18 -0400 Subject: [PATCH] feat: extend config file to support other options This expands the set of options covered by the `gotagger.json` config file. A side-effect of expanding how the config file is parsed, the restriction on how "release" commits increment the version was moved from parsing to enforcement by `Table.Get`. Fixes #56 --- .stentor.d/56.feat.extend-config-file.md | 7 + README.md | 118 ++++++++++--- cmd/gotagger/main.go | 65 +++++--- cmd/gotagger/main_test.go | 8 +- config.go | 157 +++++++++++++++++ config_test.go | 204 +++++++++++++++++++++++ gotagger.go | 112 +------------ gotagger.json | 8 +- gotagger_test.go | 167 +------------------ mapper/mapper.go | 5 + 10 files changed, 534 insertions(+), 317 deletions(-) create mode 100644 .stentor.d/56.feat.extend-config-file.md create mode 100644 config.go create mode 100644 config_test.go diff --git a/.stentor.d/56.feat.extend-config-file.md b/.stentor.d/56.feat.extend-config-file.md new file mode 100644 index 0000000..8969dd1 --- /dev/null +++ b/.stentor.d/56.feat.extend-config-file.md @@ -0,0 +1,7 @@ +The *gotagger.json* config file now supports additional options. + +Projects can now control the version prefix, +how to handle api breaking changes in pre-release versions, +how to increment versions for dirty worktrees, +modules to exclude from versioning, +and whether to ignore go modules altogether. diff --git a/README.md b/README.md index 134819c..47d2247 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,14 @@ - [Installation](#installation) - [Getting started](#getting-started) - [Running](#running) - - [Customizing Version Increments](#customizing-version-increments) + - [Configuration](#configuration) + - [Default Increment](#default-increment) + - [Increment Dirty Worktree](#increment-dirty-worktree) + - [Exclude Modules](#exclude-modules) + - [Ignore Modules](#ignore-modules) + - [Increment Mappings](#increment-mappings) + - [Pre-Release Incrementing](#pre-release-incrementing) + - [Version Prefix](#version-prefix) - [Go Module Support](#go-module-support) - [Using gotagger as a library](#using-gotagger-as-a-library) - [Contributing](#contributing) @@ -104,40 +111,105 @@ by using the `-push` flag. gotagger -release -push ``` -#### Customizing Version Increments +### Configuration -In some cases, -you may wish to change which semantic version -field should increment based on -which commit type is provided. -You can do this via a [config file](gotagger.json). -The config file contains a mapping of commit type to semver increment -and a default increment to use if it -encounters an unknown commit type. -For example: +Projects using `gotagger` can control some behaviors via a config file: +*gotagger.json*. +Check out the [gotagger.json](./gotagger.json) in this project +to see an example configuration. + +If a `gotagger.json` file exists in the working directory, +Gotagger will use it. +If no configuration is provided, +Gotagger defaults to the current functionality, +which is equivalent to what is defined in +[gotagger.json](gotagger.json). + +If you want to place your config file in a non-standard location, +then you must use the *-config* flag to tell `gotagger` where it is: + +```bash +gotagger -config path/to/gotagger.json +``` + +#### Default Increment + +The *defaultIncrement* option +controls how `gotagger` increments the version +for commit types that are not listed in [incrementMappings](#increment-mappings). +Allowed values are "minor", "patch", and "none". + +#### Increment Dirty Worktree + +The *incrementDirtyWorktree* option +controls how `gotagger` increments the version +when there are no new commits, +but the worktree is dirty. +Allowed values are "minor", "patch", and "none". + +#### Exclude Modules + +The *excludeModules* option +controls which modules gotagger will attempt to version. + +#### Ignore Modules + +The *ignoreModules* option +toggles `gotagger` support for [go modules](#go-module-support). +If you are using gotagger to version a project written in another language, +then set this to "true": + +```json +{ + "ignoreModules": true +} +``` + +#### Increment Mappings + +The *incrementMappings* option +controls which part of the semantic version +`gotagger` increments for a given commit type. +This option contains a mapping of commit type to semver increment +For example, if your project uses "f" for commits that implement features, +and "b" for commits that fix bugs: ```json { "incrementMappings": { - "feat": "minor", - "fix": "patch" + "f": "minor", + "b": "patch" }, - "defaultIncrement": "none" } ``` -Running: +#### Pre-Release Incrementing -```bash -gotagger -config ./gotagger.json +The *incrementPreReleaseMinor* option controls +how `gotagger` increments pre-release versions +for breaking changes. +Normally, a breaking change will increment the MAJOR version. +However, for pre-release versions, +those with a MAJOR version of "0", +some projects may want to increment the MINOR version instead. +This is done by setting *incrementPreReleaseMinor* to "true". + +#### Version Prefix + +The *versionPrefix* option controls +how `gotagger` prefixes the version it calculates. +The default prefix is "v", as in "v2.3.4". +Some projects may wish to have no prefix, +which can be done by setting *versionPrefix* to the empty string: + +```json +{ + "versionPrefix": "" +} ``` -If a `gotagger.json` file exists in the working directory, -Gotagger will use it. -If no configuration is provided, -Gotagger defaults to the current functionality, -which is equivalent to what is defined in -[gotagger.json](gotagger.json). +**Note**: go has very particular requirements about how tags are named, +so avoid changing the version prefix if you are versioning a go module. ### Go Module Support diff --git a/cmd/gotagger/main.go b/cmd/gotagger/main.go index 8c78a39..f9b61a2 100644 --- a/cmd/gotagger/main.go +++ b/cmd/gotagger/main.go @@ -4,6 +4,7 @@ package main import ( + "errors" "flag" "fmt" "io" @@ -16,6 +17,7 @@ import ( "strings" "github.com/sassoftware/gotagger" + "github.com/sassoftware/gotagger/mapper" ) const ( @@ -31,8 +33,11 @@ const ( platform : %s/%s ` - incrementMinor = "minor" - incrementPatch = "patch" + defaultConfigFlag = "gotagger.json" + defaultDirtyFlag = "none" + defaultModulesFlag = true + defaultPrefixFlag = "v" + defaultRemoteFlag = "origin" ) var ( @@ -75,21 +80,26 @@ func (g *GoTagger) Run() int { flags.SetOutput(g.Stderr) flags.BoolVar(&g.force, "force", g.boolEnv("force", false), "force creation of a tag") - flags.BoolVar(&g.modules, "modules", g.boolEnv("modules", true), "enable go module versioning") + flags.BoolVar(&g.modules, "modules", g.boolEnv("modules", defaultModulesFlag), "enable go module versioning") flags.BoolVar(&g.pushTag, "push", g.boolEnv("push", false), "push the just created tag, implies -release") - flags.StringVar(&g.remoteName, "remote", g.stringEnv("remote", "origin"), "name of the remote to push tags to") + flags.StringVar(&g.remoteName, "remote", g.stringEnv("remote", defaultRemoteFlag), "name of the remote to push tags to") flags.BoolVar(&g.showVersion, "version", false, "show version information") flags.BoolVar(&g.tagRelease, "release", g.boolEnv("release", false), "tag HEAD with the current version if it is a release commit") - flags.StringVar(&g.versionPrefix, "prefix", g.stringEnv("prefix", "v"), "set a prefix for versions") - flags.StringVar(&g.dirtyIncrement, "dirty", g.stringEnv("dirty", ""), "how to increment the version for a dirty checkout [minor, patch]") - flags.StringVar(&g.configFile, "config", g.stringEnv("config", ""), "path to the gotagger configuration file.") + flags.StringVar(&g.versionPrefix, "prefix", g.stringEnv("prefix", defaultPrefixFlag), "set a prefix for versions") + flags.StringVar(&g.dirtyIncrement, "dirty", g.stringEnv("dirty", defaultDirtyFlag), "how to increment the version for a dirty checkout [minor, patch, none]") + flags.StringVar(&g.configFile, "config", g.stringEnv("config", defaultConfigFlag), "path to the gotagger configuration file.") - if g.configFile == "" { + if g.configFile == defaultConfigFlag { // If there's no config file provided, check for one locally. - defaultConfig := filepath.Join(g.WorkingDir, "gotagger.json") - _, err := os.Stat(defaultConfig) - if err == nil { - g.configFile = defaultConfig + defaultConfig := filepath.Join(g.WorkingDir, defaultConfigFlag) + if _, err := os.Stat(defaultConfig); err != nil { + if !errors.Is(err, os.ErrNotExist) { + g.err.Println("error: unable to read config file:", err) + return genericErrorExitCode + } + + // no config file available + g.configFile = "" } } @@ -102,12 +112,6 @@ func (g *GoTagger) Run() int { return genericErrorExitCode } - // validate dirty value: empty string, patch or minor - if !(g.dirtyIncrement == "" || g.dirtyIncrement == incrementMinor || g.dirtyIncrement == incrementPatch) { - g.err.Println("error: unsupported value for -dirty:", g.dirtyIncrement) - return genericErrorExitCode - } - if *cpuprofile != "" { f, err := os.Create(filepath.Join(g.WorkingDir, *cpuprofile)) if err != nil { @@ -171,11 +175,30 @@ func (g *GoTagger) Run() int { r.Config.Force = g.force r.Config.CreateTag = g.tagRelease || g.pushTag || g.force - r.Config.IgnoreModules = !g.modules r.Config.PushTag = g.pushTag r.Config.RemoteName = g.remoteName - r.Config.VersionPrefix = g.versionPrefix - r.Config.DirtyWorktreeIncrement = g.dirtyIncrement + + //nolint: gosimple // makes this consistent with other flags, + // and avoids hard to understand double negatives + if g.modules != defaultModulesFlag { + r.Config.IgnoreModules = !g.modules + } + if g.versionPrefix != defaultPrefixFlag { + r.Config.VersionPrefix = g.versionPrefix + } + if g.dirtyIncrement != defaultDirtyFlag { + inc, err := mapper.Convert(g.dirtyIncrement) + if err != nil { + g.err.Println("error:", err) + return genericErrorExitCode + } + + if inc == mapper.IncrementMajor { + g.err.Println("error: -dirty value must be minor, patch, or none") + return genericErrorExitCode + } + r.Config.DirtyWorktreeIncrement = inc + } versions, err := r.TagRepo() if err != nil { diff --git a/cmd/gotagger/main_test.go b/cmd/gotagger/main_test.go index b381f10..dee20a8 100644 --- a/cmd/gotagger/main_test.go +++ b/cmd/gotagger/main_test.go @@ -149,7 +149,7 @@ func TestGoTagger(t *testing.T) { { title: "invalid dirty option", args: []string{"-dirty=foo"}, - wantErr: "error: unsupported value for -dirty: foo", + wantErr: "error: invalid version increment 'foo'", wantRc: 1, }, { @@ -170,6 +170,12 @@ func TestGoTagger(t *testing.T) { require.NoError(t, ioutil.WriteFile(filepath.Join(path, "foo"), []byte("foo\n"), 0600)) }, }, + { + title: "dirty major", + args: []string{"-dirty=major"}, + wantErr: "error: -dirty value must be minor, patch, or none", + wantRc: 1, + }, { title: "force flag", args: []string{"-force"}, diff --git a/config.go b/config.go new file mode 100644 index 0000000..b948327 --- /dev/null +++ b/config.go @@ -0,0 +1,157 @@ +package gotagger + +import ( + "encoding/json" + "fmt" + + "github.com/sassoftware/gotagger/mapper" +) + +type config struct { + DefaultIncrement string `json:"defaultIncrement"` + IncrementDirtyWorktree string `json:"incrementDirtyWorktree"` + ExcludeModules []string `json:"excludeModules"` + IgnoreModules bool `json:"ignoreModules"` + IncrementMappings map[string]string `json:"incrementMappings"` + IncrementPreReleaseMinor bool `json:"incrementPreReleaseMinor"` + VersionPrefix *string `json:"versionPrefix"` +} + +// Config represents how to tag a repo. +// +// If no default is mentioned, the option defaults to go's zero-value. +type Config struct { + // CreateTag represents whether to create the tag. + CreateTag bool + + // ExcludeModules is a list of module names or paths to exclude. + ExcludeModules []string + + // IgnoreModules controls whether gotagger will ignore the existence of + // go.mod files when determining how to version a project. + IgnoreModules bool + + // RemoteName represents the name of the remote repository. Defaults to origin. + RemoteName string + + // PreMajor controls whether gotagger will increase the major version from 0 + // to 1 for breaking changes. + PreMajor bool + + // PushTag represents whether to push the tag to the remote git repository. + PushTag bool + + // VersionPrefix is a string that will be added to the front of the version. Defaults to 'v'. + VersionPrefix string + + // DirtyWorktreeIncrement is a string that sets how to increment the version + // if there are no new commits, but the worktree is "dirty". + DirtyWorktreeIncrement mapper.Increment + + // CommitTypeTable used for looking up version increments based on the commit type. + CommitTypeTable mapper.Table + + // Force controlls whether gotagger will create a tag even if HEAD is not a "release" commit. + Force bool + + /* TODO + // PreRelease is the string that will be used to generate pre-release versions. The + // string may be a Golang text template. Valid arguments are: + // + // - .CommitsSince + // The number of commits since the previous release. + PreRelease string + */ +} + +// ParseJSON unmarshals a byte slice containing mappings of commit type to semver increment. Mappings determine +// how much to increment the semver based on the commit type. The 'release' commit type has special meaning to gotagger +// and cannot be overridden in the config file. Unknown commit types will fall back to the config default. +// Invalid increments will throw an error. Duplicate type definitions will take the last entry. +func (c *Config) ParseJSON(data []byte) error { + // unmarshal our private struct + cfg := config{ + IncrementMappings: make(map[string]string), + } + if err := json.Unmarshal(data, &cfg); err != nil { + return err + } + + // validate dirty worktree increment + inc, err := mapper.Convert(cfg.IncrementDirtyWorktree) + switch { + case err != nil: + return fmt.Errorf("invalid dirty worktree increment: %s", cfg.IncrementDirtyWorktree) + case inc == mapper.IncrementMajor: + return fmt.Errorf("major version increments are not allowed for dirty worktrees") + default: + c.DirtyWorktreeIncrement = inc + } + + // version prefix is a pointer + // so the config file can set it to "" + // and we can preserve the default of "v" + if cfg.VersionPrefix != nil { + c.VersionPrefix = *cfg.VersionPrefix + } + + // we do not allow configuring the release type, + // as it means something particular to gotagger + if _, ok := cfg.IncrementMappings["release"]; ok { + return fmt.Errorf("release mapping is not allowed") + } + + // generate the commit type table from the parsed mappings + var table mapper.Mapper + for typ, inc := range cfg.IncrementMappings { + conversion, err := mapper.Convert(inc) + if err != nil { + return err + } + + if conversion == mapper.IncrementMajor { + return fmt.Errorf("major version increments cannot be mapped to commit types. use the commit spec directives for this") + } + + if table == nil { + table = make(mapper.Mapper) + } + + table[typ] = conversion + continue + } + + // default increment to patch + if cfg.DefaultIncrement == "" { + cfg.DefaultIncrement = "patch" + } + def, err := mapper.Convert(cfg.DefaultIncrement) + if err != nil { + return err + } + + c.CommitTypeTable = mapper.NewTable(table, def) + + // copy over static values + c.ExcludeModules = cfg.ExcludeModules + c.IgnoreModules = cfg.IgnoreModules + c.PreMajor = cfg.IncrementPreReleaseMinor + + return nil +} + +// NewDefaultConfig returns a Config with default options set. +// +// If an option is not mentioned, then the default is the zero-value for its type. +// +// - RemoteName +// origin +// - VersionPrefix +// v +func NewDefaultConfig() Config { + return Config{ + RemoteName: "origin", + VersionPrefix: "v", + CommitTypeTable: mapper.NewTable(nil, mapper.IncrementPatch), + } +} diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..458c488 --- /dev/null +++ b/config_test.go @@ -0,0 +1,204 @@ +package gotagger + +import ( + "testing" + + "github.com/sassoftware/gotagger/mapper" + "github.com/stretchr/testify/assert" +) + +func TestConfig_ParseJSON(t *testing.T) { + tests := []struct { + title string + commitTypeTable mapper.Table + configFileData string + wantErr string + want Config + }{ + { + title: "no config", + commitTypeTable: mapper.Table{}, + configFileData: "", + wantErr: "unexpected end of JSON input", + }, + { + title: "good config", + configFileData: `{ + "incrementMappings": { + "feat": "minor", + "fix": "patch", + "refactor": "patch", + "perf": "patch", + "test": "patch", + "style": "patch", + "build": "none", + "chore": "none", + "ci": "none", + "docs": "none", + "revert": "none" + }, + "defaultIncrement": "none" +}`, + want: Config{ + RemoteName: "origin", + VersionPrefix: "v", + CommitTypeTable: mapper.NewTable( + mapper.Mapper{ + mapper.TypeFeature: mapper.IncrementMinor, + mapper.TypeBugFix: mapper.IncrementPatch, + mapper.TypeRefactor: mapper.IncrementPatch, + mapper.TypePerformance: mapper.IncrementPatch, + mapper.TypeTest: mapper.IncrementPatch, + mapper.TypeStyle: mapper.IncrementPatch, + mapper.TypeBuild: mapper.IncrementNone, + mapper.TypeChore: mapper.IncrementNone, + mapper.TypeCI: mapper.IncrementNone, + mapper.TypeDocs: mapper.IncrementNone, + mapper.TypeRevert: mapper.IncrementNone, + }, + mapper.IncrementNone, + ), + }, + }, + { + title: "duplicate mapping", + configFileData: `{ + "incrementMappings": { + "feat": "minor", + "feat": "patch" + }, + "defaultIncrement": "none" +}`, + want: Config{ + RemoteName: "origin", + VersionPrefix: "v", + CommitTypeTable: mapper.NewTable( + mapper.Mapper{ + mapper.TypeFeature: mapper.IncrementPatch, + }, + mapper.IncrementNone, + ), + }, + }, + { + title: "unknown commit type", + configFileData: `{ + "incrementMappings": { + "feet": "minor" + }, + "defaultIncrement": "none" +}`, + want: Config{ + RemoteName: "origin", + VersionPrefix: "v", + CommitTypeTable: mapper.NewTable( + mapper.Mapper{ + "feet": mapper.IncrementMinor, + }, + mapper.IncrementNone, + ), + }, + }, + { + title: "release not allowed", + configFileData: `{ + "incrementMappings": { + "release": "minor" + } +}`, + wantErr: "release mapping is not allowed", + }, + { + title: "attempt major increment", + configFileData: `{ + "incrementMappings": { + "feat": "major" + } +}`, + wantErr: "major version increments cannot be mapped to commit types. use the commit spec directives for this", + }, + { + title: "no default", + configFileData: `{ + "incrementMappings": { + "feat": "minor" + } +}`, + want: Config{ + RemoteName: "origin", + VersionPrefix: "v", + CommitTypeTable: mapper.NewTable( + mapper.Mapper{ + mapper.TypeFeature: mapper.IncrementMinor, + }, + mapper.IncrementPatch, + ), + }, + }, + { + title: "invalid increment", + configFileData: `{ + "incrementMappings": { + "feat": "supermajor" + }, + "defaultIncrement": "none" +}`, + wantErr: "invalid version increment 'supermajor'", + }, + { + title: "invalid json", + configFileData: "{ this is bad json", + wantErr: "invalid character 't' looking for beginning of object key string", + }, + { + title: "empty version prefix", + configFileData: `{"versionPrefix":""}`, + want: Config{ + RemoteName: "origin", + CommitTypeTable: mapper.NewTable( + mapper.Mapper{ + mapper.TypeFeature: mapper.IncrementMinor, + }, + mapper.IncrementPatch, + ), + }, + }, + { + title: "major dirty worktree increment", + configFileData: `{"incrementDirtyWorktree": "major"}`, + wantErr: "major version increments are not allowed for dirty worktrees", + }, + { + title: "default config", + configFileData: `{}`, + want: Config{ + CreateTag: false, + ExcludeModules: nil, + IgnoreModules: false, + RemoteName: "origin", + PreMajor: false, + PushTag: false, + VersionPrefix: "v", + DirtyWorktreeIncrement: mapper.IncrementNone, + CommitTypeTable: mapper.NewTable(mapper.Mapper{"feat": mapper.IncrementMinor}, mapper.IncrementPatch), + Force: false, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.title, func(t *testing.T) { + t.Parallel() + cfg := NewDefaultConfig() + + err := cfg.ParseJSON([]byte(tt.configFileData)) + if tt.wantErr == "" { + assert.NoError(t, err) + assert.Equal(t, tt.want, cfg) + } else { + assert.EqualError(t, err, tt.wantErr) + } + }) + } +} diff --git a/gotagger.go b/gotagger.go index 5ae5658..7c5186f 100644 --- a/gotagger.go +++ b/gotagger.go @@ -4,7 +4,6 @@ package gotagger import ( - "encoding/json" "errors" "fmt" "io/ioutil" @@ -36,113 +35,6 @@ var ( ErrNotRelease = errors.New("HEAD is not a release commit") ) -// Config represents how to tag a repo. If not default is mentioned, the option defaults -// to go's zero-value. -type Config struct { - // CreateTag represents whether to create the tag. - CreateTag bool - - // ExcludeModules is a list of module names or paths to exclude. - ExcludeModules []string - - // IgnoreModules controls whether gotagger will ignore the existence of - // go.mod files when determining how to version a project. - IgnoreModules bool - - // RemoteName represents the name of the remote repository. Defaults to origin. - RemoteName string - - // PreMajor controls whether gotagger will increase the major version from 0 - // to 1 for breaking changes. - PreMajor bool - - // PushTag represents whether to push the tag to the remote git repository. - PushTag bool - - // VersionPrefix is a string that will be added to the front of the version. Defaults to 'v'. - VersionPrefix string - - // DirtyWorktreeIncrement is a string that sets how to increment the version - // if there are no new commits, but the worktree is "dirty". - DirtyWorktreeIncrement string - - // Mappings mapping of commit type to semantic version increment. Used to populate a mapper.Table. - Mappings map[string]string `json:"incrementMappings"` - - // DefaultIncrement if the commit type is not available in Mappings, increment by this amount. - DefaultIncrement string `json:"defaultIncrement"` - - // CommitTypeTable used for looking up version increments based on the commit type. - CommitTypeTable mapper.Table - - // Force controlls whether gotagger will create a tag even if HEAD is not a "release" commit. - Force bool - - /* TODO - // PreRelease is the string that will be used to generate pre-release versions. The - // string may be a Golang text template. Valid arguments are: - // - // - .CommitsSince - // The number of commits since the previous release. - PreRelease string - */ -} - -// ParseJSON unmarshals a byte slic containing mappings of commit type to semver increment. Mappings determine -// how much to increment the semver based on the commit type. The 'release' commit type has special meaning to gotagger -// and cannot be overridden in the config file. Unknown commit types will fall back to the config default. -// Invalid increments will throw an error. Duplicate type definitions will take the last entry. -func (c *Config) ParseJSON(data []byte) error { - err := json.Unmarshal(data, &c) - if err != nil { - return err - } - - if _, ok := c.Mappings["release"]; ok { - return fmt.Errorf("release mapping is not allowed") - } - c.Mappings["release"] = "patch" - - table := mapper.Mapper{} - for typ, inc := range c.Mappings { - conversion, err := mapper.Convert(inc) - if err != nil { - return err - } - - if conversion == mapper.IncrementMajor { - return fmt.Errorf("major version increments cannot be mapped to commit types. use the commit spec directives for this") - } - - table[typ] = conversion - continue - } - - def, err := mapper.Convert(c.DefaultIncrement) - if err != nil { - return err - } - c.CommitTypeTable = mapper.NewTable(table, def) - - return nil -} - -// NewDefaultConfig returns a Config with default options set. -// -// If an option is not mentioned, then the default is the zero-value for its type. -// -// - RemoteName -// origin -// - VersionPrefix -// v -func NewDefaultConfig() Config { - return Config{ - RemoteName: "origin", - VersionPrefix: "v", - CommitTypeTable: mapper.NewTable(nil, mapper.IncrementPatch), - } -} - type Gotagger struct { Config Config @@ -394,9 +286,9 @@ func (g *Gotagger) incrementVersion(v *semver.Version, commits []igit.Commit) (s } switch { - case isDirty && g.Config.DirtyWorktreeIncrement == "minor": + case isDirty && g.Config.DirtyWorktreeIncrement == mapper.IncrementMinor: return v.IncMinor().String(), nil - case isDirty && g.Config.DirtyWorktreeIncrement == "patch": + case isDirty && g.Config.DirtyWorktreeIncrement == mapper.IncrementPatch: return v.IncPatch().String(), nil default: return v.String(), nil diff --git a/gotagger.json b/gotagger.json index 531c386..63cd631 100644 --- a/gotagger.json +++ b/gotagger.json @@ -1,6 +1,10 @@ { + "defaultIncrement": "patch", + "excludeModules": null, + "ignoreModules": false, + "incrementPreReleaseMinor": true, + "incrementDirtyWorktree": "patch", "incrementMappings": { "feat": "minor" - }, - "defaultIncrement": "patch" + } } diff --git a/gotagger_test.go b/gotagger_test.go index 236f69d..7630c84 100644 --- a/gotagger_test.go +++ b/gotagger_test.go @@ -591,7 +591,7 @@ func TestGotagger_versioning(t *testing.T) { }, }, { - title: "mulit-module commit", + title: "multi-module commit", prefix: "v", repoFunc: func(t testutils.T, r *sgit.Repository, p string) { simpleGoRepo(t, r, p) @@ -1299,7 +1299,7 @@ func TestGotagger_incrementVersion(t *testing.T) { tests := []struct { title string repoFunc func(testutils.T, *sgit.Repository, string) - dirtyIncrement string + dirtyIncrement mapper.Increment preMajor bool commits []git.Commit want string @@ -1351,17 +1351,17 @@ func TestGotagger_incrementVersion(t *testing.T) { }, { title: "dirty minor", - dirtyIncrement: "minor", + dirtyIncrement: mapper.IncrementMinor, want: "0.2.0", }, { title: "dirty patch", - dirtyIncrement: "patch", + dirtyIncrement: mapper.IncrementPatch, want: "0.1.1", }, { title: "dirty unknown", - dirtyIncrement: "unknown", + dirtyIncrement: mapper.Increment(23), want: "0.1.0", }, } @@ -1372,8 +1372,8 @@ func TestGotagger_incrementVersion(t *testing.T) { t.Run(tt.title, func(t *testing.T) { t.Parallel() - g, repo, path, teardwon := newGotagger(t) - defer teardwon() + g, repo, path, teardown := newGotagger(t) + defer teardown() if tt.repoFunc != nil { tt.repoFunc(t, repo, path) @@ -1578,159 +1578,6 @@ func TestGotagger_validateModules(t *testing.T) { } } -func TestGotagger_ReadCommitTypeMappings(t *testing.T) { - normalConfigFileData := `{ - "incrementMappings": { - "feat": "minor", - "fix": "patch", - "refactor": "patch", - "perf": "patch", - "test": "patch", - "style": "patch", - "build": "none", - "chore": "none", - "ci": "none", - "docs": "none", - "revert": "none" - }, - "defaultIncrement": "none" -}` - duplicateMappingConfigData := `{ - "incrementMappings": { - "feat": "minor", - "feat": "patch" - }, - "defaultIncrement": "none" -}` - unknownTypeConfigData := `{ - "incrementMappings": { - "feet": "minor" - }, - "defaultIncrement": "none" -}` - noDefaultConfigData := `{ - "incrementMappings": { - "feat": "minor" - } -}` - releaseDefinedConfigData := `{ - "incrementMappings": { - "release": "minor" - } -}` - bumpMajorConfigData := `{ - "incrementMappings": { - "feat": "major" - } -}` - invalidIncrementConfigData := `{ - "incrementMappings": { - "feat": "supermajor" - }, - "defaultIncrement": "none" -}` - - tests := []struct { - title string - commitTypeTable mapper.Table - configFileData string - wantErr string - }{ - { - title: "no config", - commitTypeTable: mapper.Table{}, - configFileData: "", - wantErr: "unexpected end of JSON input", - }, - { - title: "good config", - commitTypeTable: mapper.NewTable(mapper.Mapper{ - mapper.TypeFeature: mapper.IncrementMinor, - mapper.TypeRelease: mapper.IncrementPatch, - mapper.TypeBugFix: mapper.IncrementPatch, - mapper.TypeRefactor: mapper.IncrementPatch, - mapper.TypePerformance: mapper.IncrementPatch, - mapper.TypeTest: mapper.IncrementPatch, - mapper.TypeStyle: mapper.IncrementPatch, - mapper.TypeBuild: mapper.IncrementNone, - mapper.TypeChore: mapper.IncrementNone, - mapper.TypeCI: mapper.IncrementNone, - mapper.TypeDocs: mapper.IncrementNone, - mapper.TypeRevert: mapper.IncrementNone, - }, mapper.IncrementNone), - configFileData: normalConfigFileData, - wantErr: "", - }, - { - title: "duplicate mapping", - commitTypeTable: mapper.NewTable(mapper.Mapper{ - mapper.TypeFeature: mapper.IncrementPatch, - mapper.TypeRelease: mapper.IncrementPatch, - }, mapper.IncrementNone), - configFileData: duplicateMappingConfigData, - wantErr: "", - }, - { - title: "unknown commit type", - commitTypeTable: mapper.NewTable(mapper.Mapper{ - "feet": mapper.IncrementMinor, - mapper.TypeRelease: mapper.IncrementPatch, - }, mapper.IncrementNone), - configFileData: unknownTypeConfigData, - wantErr: "", - }, - { - title: "release not allowed", - commitTypeTable: mapper.Table{}, - configFileData: releaseDefinedConfigData, - wantErr: "release mapping is not allowed", - }, - { - title: "attempt major increment", - commitTypeTable: mapper.Table{}, - configFileData: bumpMajorConfigData, - wantErr: "major version increments cannot be mapped to commit types. use the commit spec directives for this", - }, - { - title: "no default", - commitTypeTable: mapper.NewTable(mapper.Mapper{ - mapper.TypeFeature: mapper.IncrementMinor, - mapper.TypeRelease: mapper.IncrementPatch, - }, mapper.IncrementNone), - configFileData: noDefaultConfigData, - wantErr: "", - }, - { - title: "invalid increment", - commitTypeTable: mapper.Table{}, - configFileData: invalidIncrementConfigData, - wantErr: "invalid version increment 'supermajor'", - }, - { - title: "invalid json", - commitTypeTable: mapper.Table{}, - configFileData: "{ this is bad json", - wantErr: "invalid character 't' looking for beginning of object key string", - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.title, func(t *testing.T) { - t.Parallel() - cfg := Config{CommitTypeTable: tt.commitTypeTable} - - err := cfg.ParseJSON([]byte(tt.configFileData)) - if tt.wantErr == "" { - assert.NoError(t, err) - assert.Equal(t, tt.commitTypeTable, cfg.CommitTypeTable) - } else { - assert.EqualError(t, err, tt.wantErr) - } - }) - } -} - func newGotagger(t testutils.T) (g *Gotagger, repo *sgit.Repository, path string, teardown func()) { t.Helper() diff --git a/mapper/mapper.go b/mapper/mapper.go index feeb511..4941d7d 100644 --- a/mapper/mapper.go +++ b/mapper/mapper.go @@ -70,6 +70,11 @@ func NewTable(tm Mapper, defInc Increment) Table { // Get returns the configured increment for the provided commit type. Returns the default increment if no mapping for // the input type is found. func (t Table) Get(typ string) Increment { + // release type is always a patch increment + if typ == TypeRelease { + return IncrementPatch + } + inc, ok := t.Mapper[typ] if !ok { return t.defaultInc