Skip to content

Commit

Permalink
feat: extend config file to support other options
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wfscheper committed Jun 21, 2022
1 parent b59fbd5 commit a5c860e
Show file tree
Hide file tree
Showing 10 changed files with 534 additions and 317 deletions.
7 changes: 7 additions & 0 deletions .stentor.d/56.feat.extend-config-file.md
Original file line number Diff line number Diff line change
@@ -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.
118 changes: 95 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down
65 changes: 44 additions & 21 deletions cmd/gotagger/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package main

import (
"errors"
"flag"
"fmt"
"io"
Expand All @@ -16,6 +17,7 @@ import (
"strings"

"github.com/sassoftware/gotagger"
"github.com/sassoftware/gotagger/mapper"
)

const (
Expand All @@ -31,8 +33,11 @@ const (
platform : %s/%s
`

incrementMinor = "minor"
incrementPatch = "patch"
defaultConfigFlag = "gotagger.json"
defaultDirtyFlag = "none"
defaultModulesFlag = true
defaultPrefixFlag = "v"
defaultRemoteFlag = "origin"
)

var (
Expand Down Expand Up @@ -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 = ""
}
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 7 additions & 1 deletion cmd/gotagger/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
{
Expand All @@ -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"},
Expand Down
Loading

0 comments on commit a5c860e

Please sign in to comment.