Repository: goreleaser/goreleaser
Pull request #2358 (2021-07-22 01:08:23)

feat: inc parts of semver, change snapshot default name
revives https://github.com/goreleaser/goreleaser/pull/1457 adding some new template functions:

- incpatch
- incminor
- incmajor

and change the `goreleaser init`  generated config snapshot name to `{{ incpatch .Tag }}-next`

refs https://github.com/goreleaser/goreleaser/issues/2355

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).  
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/goreleaser/goreleaser/7ZNBYzpLtFqHARZkU1RkJWb4uPRM](https://vercel.com/goreleaser/goreleaser/7ZNBYzpLtFqHARZkU1RkJWb4uPRM)  
✅ Preview: [https://goreleaser-git-snap-goreleaser.vercel.app](https://goreleaser-git-snap-goreleaser.vercel.app)


radeksimko: Apologies for bit of a trigger happiness with the last comment, after re-reading the discussion in https://github.com/goreleaser/goreleaser/issues/2355 I suppose that the problem you're all trying to solve is predictability of the name of the resulting artifact.

I obviously can't speak for any majority of users, but from my experience this has not been a common concern for us at HashiCorp. I understand the CI-testability use case, but a real concern I would have if snapshots were named the same as the upcoming version is that someone will accidentally publish that as a "stable" version and then override with a real stable release later.

Having the commit SHA there is from my experience valuable to make releasing more error-prone and to easily set expectations for end-users _by default_.

I suppose that one group of users will have to end up specifying this manually. I would personally be more inclined to keep the _default_ options _safer_.

https://goreleaser.com/customization/snapshots/

myitcv: @radeksimko I'm very inexperienced with `goreleaser`, so my comments should be taken with a pinch of salt!

> but a real concern I would have if snapshots were named the same as the upcoming version is that someone will accidentally publish that as a "stable" version and then override with a real stable release later.

Please can you explain this point a bit further? Because it's entirely possible I'm not entirely following, or I have a different mental model. I expect `goreleaser test` to, by default, leave no trace of what it did locally. No files, no docker images, nothing. It goes without saying this mode would also not upload/push anything, ever. For those people who do want to test a docker image, perhaps a flag `--nocleanup` could be provided... but at this point I'm getting out of my depth!

My desire to have `goreleaser test` accept a version string is that it allows us to simulate the entire release process without actually mutating any remote state (GitHub release assets, docker images etc). The stable output of `goreleaser test` would allow us to affirm what _would_ happen if we were to run things for real. Because without that, the only time we can truly test what will be published (in terms of asset names) is when it comes to cutting a release. That just feels fragile, especially if you consider `goreleaser` updates along the way (which will hopefully be stable!)

But again, I really defer to those with greater experience of `goreleaser`. Please consider my experience reports as just that: I'm very happy being guided to doing things a different way, just so long as it gives us comfort with our `goreleaser` setup.
radeksimko: > I expect goreleaser test to, by default, leave no trace of what it did locally. No files, no docker images, nothing. It goes without saying this mode would also not upload/push anything, ever.

That more or less aligns with my thinking but my this PR would change behaviour of the `goreleaser build --snapshot` and `goreleaser release --snapshot` commands. I would be more than happy for a `test` command to behave as you described.

I do believe the `test` command should perhaps use different (default?) naming convention for the snapshot as it has different use cases.
caarlos0: Yeah, I think that changing the default here is indeed no good.

Maybe we could instead have a different field for the actual `test` command later, which will actually be something @myitcv is interested in as well, e.g. `test.tag_template: v99.9.9` (which by default could be `{{ inpatch .Tag }}`).

wdyt?
caarlos0: should be better now.

I did change the default on the config generated by `goreleaser init`, which shiould not a breaking change, but will be a bit better than the previous value `TAG-next`...
codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2358?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) Report
> Merging [#2358](https://codecov.io/gh/goreleaser/goreleaser/pull/2358?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (3943397) into [master](https://codecov.io/gh/goreleaser/goreleaser/commit/bf19dc1079887d31e58065771bb069953b1704d6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (bf19dc1) will **increase** coverage by `0.03%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 3943397 differs from pull request most recent head 1a330d6. Consider uploading reports for the commit 1a330d6 to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/goreleaser/goreleaser/pull/2358/graphs/tree.svg?width=650&height=150&src=pr&token=3bJvv6b7hN&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)](https://codecov.io/gh/goreleaser/goreleaser/pull/2358?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)

```diff
@@            Coverage Diff             @@
##           master    #2358      +/-   ##
==========================================
+ Coverage   82.96%   82.99%   +0.03%     
==========================================
  Files          81       81              
  Lines        5992     6004      +12     
==========================================
+ Hits         4971     4983      +12     
  Misses        878      878              
  Partials      143      143              
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/2358?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) | Coverage Δ | |
|---|---|---|
| [internal/pipe/git/git.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2358/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9naXQvZ2l0Lmdv) | `86.42% <100.00%> (ø)` | |
| [internal/pipe/snapshot/snapshot.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2358/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9zbmFwc2hvdC9zbmFwc2hvdC5nbw==) | `100.00% <100.00%> (ø)` | |
| [internal/tmpl/tmpl.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2358/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvdG1wbC90bXBsLmdv) | `95.76% <100.00%> (+0.43%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2358?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2358?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Last update [bf19dc1...1a330d6](https://codecov.io/gh/goreleaser/goreleaser/pull/2358?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser).


Patch:
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 173a7cd8..20de36f0 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -4,6 +4,8 @@ before:
   hooks:
     - go mod tidy
     - ./scripts/completions.sh
+snapshot:
+  name_template: '{{ incpatch .Tag }}-next'
 gomod:
   proxy: true
 builds:
diff --git a/internal/pipe/git/git.go b/internal/pipe/git/git.go
index 083014a2..46b63d32 100644
--- a/internal/pipe/git/git.go
+++ b/internal/pipe/git/git.go
@@ -32,7 +32,7 @@ func (Pipe) Run(ctx *context.Context) error {
 		return err
 	}
 	ctx.Git = info
-	log.Infof("releasing %s, commit %s", info.CurrentTag, info.Commit)
+	log.WithField("commit", info.Commit).WithField("latest tag", info.CurrentTag).Info("building...")
 	ctx.Version = strings.TrimPrefix(ctx.Git.CurrentTag, "v")
 	return validate(ctx)
 }
diff --git a/internal/pipe/snapshot/snapshot.go b/internal/pipe/snapshot/snapshot.go
index 5e9f7929..8b9daa33 100644
--- a/internal/pipe/snapshot/snapshot.go
+++ b/internal/pipe/snapshot/snapshot.go
@@ -4,6 +4,7 @@ package snapshot
 import (
 	"fmt"
 
+	"github.com/apex/log"
 	"github.com/goreleaser/goreleaser/internal/pipe"
 	"github.com/goreleaser/goreleaser/internal/tmpl"
 	"github.com/goreleaser/goreleaser/pkg/context"
@@ -36,5 +37,6 @@ func (Pipe) Run(ctx *context.Context) error {
 		return fmt.Errorf("empty snapshot name")
 	}
 	ctx.Version = name
+	log.WithField("version", ctx.Version).Infof("building snapshot...")
 	return nil
 }
diff --git a/internal/static/config.go b/internal/static/config.go
index fe29d80b..5098f5c8 100644
--- a/internal/static/config.go
+++ b/internal/static/config.go
@@ -29,7 +29,7 @@ archives:
 checksum:
   name_template: 'checksums.txt'
 snapshot:
-  name_template: "{{ .Tag }}-next"
+  name_template: "{{ incpatch .Tag }}-next"
 changelog:
   sort: asc
   filters:
diff --git a/internal/tmpl/tmpl.go b/internal/tmpl/tmpl.go
index de56e760..321751ae 100644
--- a/internal/tmpl/tmpl.go
+++ b/internal/tmpl/tmpl.go
@@ -10,6 +10,7 @@ import (
 	"text/template"
 	"time"
 
+	"github.com/Masterminds/semver/v3"
 	"github.com/goreleaser/goreleaser/internal/artifact"
 	"github.com/goreleaser/goreleaser/pkg/build"
 	"github.com/goreleaser/goreleaser/pkg/context"
@@ -166,6 +167,9 @@ func (t *Template) Apply(s string) (string, error) {
 			"trimprefix": strings.TrimPrefix,
 			"dir":        filepath.Dir,
 			"abs":        filepath.Abs,
+			"incmajor":   incMajor,
+			"incminor":   incMinor,
+			"incpatch":   incPatch,
 		}).
 		Parse(s)
 	if err != nil {
@@ -218,3 +222,22 @@ func replace(replacements map[string]string, original string) string {
 	}
 	return result
 }
+
+func incMajor(v string) string {
+	return prefix(v) + semver.MustParse(v).IncMajor().String()
+}
+
+func incMinor(v string) string {
+	return prefix(v) + semver.MustParse(v).IncMinor().String()
+}
+
+func incPatch(v string) string {
+	return prefix(v) + semver.MustParse(v).IncPatch().String()
+}
+
+func prefix(v string) string {
+	if v != "" && v[0] == 'v' {
+		return "v"
+	}
+	return ""
+}
diff --git a/www/docs/customization/snapshots.md b/www/docs/customization/snapshots.md
index 75a3bdf6..f15a1d3e 100644
--- a/www/docs/customization/snapshots.md
+++ b/www/docs/customization/snapshots.md
@@ -17,7 +17,7 @@ snapshot:
   # for example).
   #
   # Default is `{{ .Tag }}-SNAPSHOT-{{.ShortCommit}}`.
-  name_template: 1.2.3-SNAPSHOT-{{.Commit}}
+  name_template: '{{ incpatch .Tag }}-devel'
 ```
 
 ## How it works
diff --git a/www/docs/customization/templates.md b/www/docs/customization/templates.md
index 9bf2b462..a84cd038 100644
--- a/www/docs/customization/templates.md
+++ b/www/docs/customization/templates.md
@@ -10,29 +10,32 @@ support templating.
 
 On fields that support templating, these fields are always available:
 
-| Key                | Description                                                                                                                  |
-|--------------------|------------------------------------------------------------------------------------------------------------------------------|
-| `.ProjectName`     | the project name                                                                                                             |
-| `.Version`         | the version being released (`v` prefix stripped),<br>or `{{ .Tag }}-SNAPSHOT-{{ .ShortCommit }}` in case of snapshot release |
-| `.Branch`          | the current git branch                                                                                                       |
-| `.PrefixedTag`     | the current git tag prefixed with the monorepo config tag prefix (if any)                                                    |
-| `.Tag`             | the current git tag                                                                                                          |
-| `.ShortCommit`     | the git commit short hash                                                                                                    |
-| `.FullCommit`      | the git commit full hash                                                                                                     |
-| `.Commit`          | the git commit hash (deprecated)                                                                                             |
-| `.CommitDate`      | the UTC commit date in RFC 3339 format                                                                                       |
-| `.CommitTimestamp` | the UTC commit date in Unix format                                                                                           |
-| `.GitURL`          | the git remote url                                                                                                           |
-| `.Major`           | the major part of the version (assuming `Tag` is a valid semver, else `0`)                                                   |
-| `.Minor`           | the minor part of the version (assuming `Tag` is a valid semver, else `0`)                                                   |
-| `.Patch`           | the patch part of the version (assuming `Tag` is a valid semver, else `0`)                                                   |
-| `.Prerelease`      | the prerelease part of the version, e.g. `beta` (assuming `Tag` is a valid semver)                                           |
-| `.RawVersion`      | Major.Minor.Patch (assuming `Tag` is a valid semver, else `0.0.0`)                                                           |
-| `.IsSnapshot`      | `true` if a snapshot is being released, `false` otherwise                                                                    |
-| `.Env`             | a map with system's environment variables                                                                                    |
-| `.Date`            | current UTC date in RFC 3339 format                                                                                          |
-| `.Timestamp`       | current UTC time in Unix format                                                                                              |
-| `.ModulePath`      | the go module path, as reported by `go list -m`                                                                              |
+| Key                 | Description                                                                                                                  |
+|---------------------|------------------------------------------------------------------------------------------------------------------------------|
+| `.ProjectName`      | the project name                                                                                                             |
+| `.Version`          | the version being released (`v` prefix stripped),<br>or `{{ .Tag }}-SNAPSHOT-{{ .ShortCommit }}` in case of snapshot release |
+| `.Branch`           | the current git branch                                                                                                       |
+| `.PrefixedTag`      | the current git tag prefixed with the monorepo config tag prefix (if any)                                                    |
+| `.Tag`              | the current git tag                                                                                                          |
+| `.ShortCommit`      | the git commit short hash                                                                                                    |
+| `.FullCommit`       | the git commit full hash                                                                                                     |
+| `.Commit`           | the git commit hash (deprecated)                                                                                             |
+| `.CommitDate`       | the UTC commit date in RFC 3339 format                                                                                       |
+| `.CommitTimestamp`  | the UTC commit date in Unix format                                                                                           |
+| `.GitURL`           | the git remote url                                                                                                           |
+| `.Major`            | the major part of the version (assuming `Tag` is a valid semver, else `0`)                                                   |
+| `.Minor`            | the minor part of the version (assuming `Tag` is a valid semver, else `0`)                                                   |
+| `.Patch`            | the patch part of the version (assuming `Tag` is a valid semver, else `0`)                                                   |
+| `.Prerelease`       | the prerelease part of the version, e.g. `beta` (assuming `Tag` is a valid semver)                                           |
+| `.RawVersion`       | Major.Minor.Patch (assuming `Tag` is a valid semver, else `0.0.0`)                                                           |
+| `.IsSnapshot`       | `true` if a snapshot is being released, `false` otherwise                                                                    |
+| `.Env`              | a map with system's environment variables                                                                                    |
+| `.Date`             | current UTC date in RFC 3339 format                                                                                          |
+| `.Timestamp`        | current UTC time in Unix format                                                                                              |
+| `.ModulePath`       | the go module path, as reported by `go list -m`                                                                              |
+| `incpatch "v1.2.4"` | increments the patch of the given version; will panic if not a semantic version                                              |
+| `incminor "v1.2.4"` | increments the minor of the given version; will panic if not a semantic version                                              |
+| `incmajor "v1.2.4"` | increments the major of the given version; will panic if not a semantic version                                              |
 
 On fields that are related to a single artifact (e.g., the binary name), you
 may have some extra fields:
@@ -64,7 +67,7 @@ On all fields, you have these available functions:
 | `tolower "V1.2"`        | makes input string lowercase. See [ToLower](https://golang.org/pkg/strings/#ToLower)                                           |
 | `toupper "v1.2"`        | makes input string uppercase. See [ToUpper](https://golang.org/pkg/strings/#ToUpper)                                           |
 | `trim " v1.2  "`        | removes all leading and trailing white space. See [TrimSpace](https://golang.org/pkg/strings/#TrimSpace)                       |
-| `trimprefix "v1.2" "v"` | removes provided leading prefix string, if present. See [TrimPrefix](https://golang.org/pkg/strings/#TrimPrefix)                |
+| `trimprefix "v1.2" "v"` | removes provided leading prefix string, if present. See [TrimPrefix](https://golang.org/pkg/strings/#TrimPrefix)               |
 | `dir .Path`             | returns all but the last element of path, typically the path's directory. See [Dir](https://golang.org/pkg/path/filepath/#Dir) |
 | `abs .ArtifactPath`     | returns an absolute representation of path. See [Abs](https://golang.org/pkg/path/filepath/#Abs)                               |
 


Test patch:
diff --git a/cmd/testdata/good.yml b/cmd/testdata/good.yml
index 3eb5d53c..33fe3353 100644
--- a/cmd/testdata/good.yml
+++ b/cmd/testdata/good.yml
@@ -19,7 +19,7 @@ archives:
 checksum:
   name_template: 'checksums.txt'
 snapshot:
-  name_template: "{{ .Tag }}-next"
+  name_template: "{{ incpatch .Tag }}-next"
 changelog:
   sort: asc
   filters:
diff --git a/internal/tmpl/tmpl_test.go b/internal/tmpl/tmpl_test.go
index 75e77b26..954cc21c 100644
--- a/internal/tmpl/tmpl_test.go
+++ b/internal/tmpl/tmpl_test.go
@@ -48,6 +48,12 @@ func TestWithArtifact(t *testing.T) {
 		"binary":                           "{{.Binary}}",
 		"proj":                             "{{.ProjectName}}",
 		"github.com/goreleaser/goreleaser": "{{ .ModulePath }}",
+		"v2.0.0":                           "{{.Tag | incmajor }}",
+		"2.0.0":                            "{{.Version | incmajor }}",
+		"v1.3.0":                           "{{.Tag | incminor }}",
+		"1.3.0":                            "{{.Version | incminor }}",
+		"v1.2.4":                           "{{.Tag | incpatch }}",
+		"1.2.4":                            "{{.Version | incpatch }}",
 	} {
 		tmpl := tmpl
 		expect := expect