Repository: goreleaser/nfpm
Pull request #215 (2020-08-19 19:19:18)

feat: Introduce version_metadata for all packagers (deprecate deb.metadata)
I added the global config field `VersionMetadata` for all packagers. I chose `version_metadata` for the YAML field because metadata is such a generic term that most people won't associate it with the version otherwise. The `deb` packager already supported this and the `apk` formatter borrowed it from the `deb`-specific option which was not ideal.

`Deb.VersionMetadata` is now deprecated an a warning will be emitted to stderr if it is still set (same for `BinDir`).


If not explicitly set, the `VersionMetadata` can be populated from `Version` semver metadata.

Also the tests and conventional file names are updated.

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: *In Progress*  
✅ Preview: *In Progress*


codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/nfpm/pull/215?src=pr&el=h1) Report
> Merging [#215](https://codecov.io/gh/goreleaser/nfpm/pull/215?src=pr&el=desc) into [master](https://codecov.io/gh/goreleaser/nfpm/commit/3c4244d700009aef6ae97c7a1eae8a275124d549&el=desc) will **decrease** coverage by `0.98%`.
> The diff coverage is `37.03%`.

[![Impacted file tree graph](https://codecov.io/gh/goreleaser/nfpm/pull/215/graphs/tree.svg?width=650&height=150&src=pr&token=9QKGIxn7wg)](https://codecov.io/gh/goreleaser/nfpm/pull/215?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #215      +/-   ##
==========================================
- Coverage   69.98%   69.00%   -0.99%     
==========================================
  Files           7        7              
  Lines         903      926      +23     
==========================================
+ Hits          632      639       +7     
- Misses        164      178      +14     
- Partials      107      109       +2     
```


| [Impacted Files](https://codecov.io/gh/goreleaser/nfpm/pull/215?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [apk/apk.go](https://codecov.io/gh/goreleaser/nfpm/pull/215/diff?src=pr&el=tree#diff-YXBrL2Fway5nbw==) | `68.66% <0.00%> (-3.08%)` | :arrow_down: |
| [nfpm.go](https://codecov.io/gh/goreleaser/nfpm/pull/215/diff?src=pr&el=tree#diff-bmZwbS5nbw==) | `79.74% <25.00%> (-6.17%)` | :arrow_down: |
| [deb/deb.go](https://codecov.io/gh/goreleaser/nfpm/pull/215/diff?src=pr&el=tree#diff-ZGViL2RlYi5nbw==) | `71.13% <100.00%> (+0.19%)` | :arrow_up: |
| [rpm/rpm.go](https://codecov.io/gh/goreleaser/nfpm/pull/215/diff?src=pr&el=tree#diff-cnBtL3JwbS5nbw==) | `73.39% <100.00%> (+0.37%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goreleaser/nfpm/pull/215?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goreleaser/nfpm/pull/215?src=pr&el=footer). Last update [3c4244d...426a7a5](https://codecov.io/gh/goreleaser/nfpm/pull/215?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/apk/apk.go b/apk/apk.go
index 9f40df2..a0f1aa5 100644
--- a/apk/apk.go
+++ b/apk/apk.go
@@ -77,7 +77,25 @@ type Apk struct{}
 
 func (a *Apk) ConventionalFileName(info *nfpm.Info) string {
 	// TODO: verify this
-	return fmt.Sprintf("%s_%s_%s.apk", info.Name, info.Version, info.Arch)
+	arch, ok := archToAlpine[info.Arch]
+	if !ok {
+		arch = info.Arch
+	}
+
+	version := info.Version
+	if info.Release != "" {
+		version += "-" + info.Release
+	}
+
+	if info.Prerelease != "" {
+		version += "~" + info.Prerelease
+	}
+
+	if info.VersionMetadata != "" {
+		version += "+" + info.VersionMetadata
+	}
+
+	return fmt.Sprintf("%s_%s_%s.apk", info.Name, version, arch)
 }
 
 // Package writes a new apk package to the given writer using the given info.
@@ -453,7 +471,7 @@ pkgname = {{.Info.Name}}
 pkgver = {{ if .Info.Epoch}}{{ .Info.Epoch }}:{{ end }}{{.Info.Version}}
          {{- if .Info.Release}}-{{ .Info.Release }}{{- end }}
          {{- if .Info.Prerelease}}~{{ .Info.Prerelease }}{{- end }}
-         {{- if .Info.Deb.VersionMetadata}}+{{ .Info.Deb.VersionMetadata }}{{- end }}
+         {{- if .Info.VersionMetadata}}+{{ .Info.VersionMetadata }}{{- end }}
 arch = {{.Info.Arch}}
 size = {{.InstalledSize}}
 pkgdesc = {{multiline .Info.Description}}
diff --git a/deb/deb.go b/deb/deb.go
index 77661da..b77bd3f 100644
--- a/deb/deb.go
+++ b/deb/deb.go
@@ -61,10 +61,15 @@ func (*Deb) ConventionalFileName(info *nfpm.Info) string {
 	if info.Release != "" {
 		version += "-" + info.Release
 	}
+
 	if info.Prerelease != "" {
 		version += "~" + info.Prerelease
 	}
 
+	if info.VersionMetadata != "" {
+		version += "+" + info.VersionMetadata
+	}
+
 	// package_version_architecture.package-type
 	return fmt.Sprintf("%s_%s_%s.deb", info.Name, version, arch)
 }
@@ -502,7 +507,7 @@ Package: {{.Info.Name}}
 Version: {{ if .Info.Epoch}}{{ .Info.Epoch }}:{{ end }}{{.Info.Version}}
          {{- if .Info.Release}}-{{ .Info.Release }}{{- end }}
          {{- if .Info.Prerelease}}~{{ .Info.Prerelease }}{{- end }}
-         {{- if .Info.Deb.VersionMetadata}}+{{ .Info.Deb.VersionMetadata }}{{- end }}
+         {{- if .Info.VersionMetadata}}+{{ .Info.VersionMetadata }}{{- end }}
 Section: {{.Info.Section}}
 Priority: {{.Info.Priority}}
 Architecture: {{.Info.Arch}}
diff --git a/nfpm.go b/nfpm.go
index 3b2a964..53ff5a4 100644
--- a/nfpm.go
+++ b/nfpm.go
@@ -116,24 +116,25 @@ func (c *Config) Validate() error {
 
 // Info contains information about a single package.
 type Info struct {
-	Overridables `yaml:",inline"`
-	Name         string `yaml:"name,omitempty"`
-	Arch         string `yaml:"arch,omitempty"`
-	Platform     string `yaml:"platform,omitempty"`
-	Epoch        string `yaml:"epoch,omitempty"`
-	Version      string `yaml:"version,omitempty"`
-	Release      string `yaml:"release,omitempty"`
-	Prerelease   string `yaml:"prerelease,omitempty"`
-	Section      string `yaml:"section,omitempty"`
-	Priority     string `yaml:"priority,omitempty"`
-	Maintainer   string `yaml:"maintainer,omitempty"`
-	Description  string `yaml:"description,omitempty"`
-	Vendor       string `yaml:"vendor,omitempty"`
-	Homepage     string `yaml:"homepage,omitempty"`
-	License      string `yaml:"license,omitempty"`
-	Bindir       string `yaml:"bindir,omitempty"` // Deprecated: this does nothing. TODO: remove.
-	Changelog    string `yaml:"changelog,omitempty"`
-	Target       string `yaml:"-"`
+	Overridables    `yaml:",inline"`
+	Name            string `yaml:"name,omitempty"`
+	Arch            string `yaml:"arch,omitempty"`
+	Platform        string `yaml:"platform,omitempty"`
+	Epoch           string `yaml:"epoch,omitempty"`
+	Version         string `yaml:"version,omitempty"`
+	Release         string `yaml:"release,omitempty"`
+	Prerelease      string `yaml:"prerelease,omitempty"`
+	VersionMetadata string `yaml:"version_metadata,omitempty"`
+	Section         string `yaml:"section,omitempty"`
+	Priority        string `yaml:"priority,omitempty"`
+	Maintainer      string `yaml:"maintainer,omitempty"`
+	Description     string `yaml:"description,omitempty"`
+	Vendor          string `yaml:"vendor,omitempty"`
+	Homepage        string `yaml:"homepage,omitempty"`
+	License         string `yaml:"license,omitempty"`
+	Bindir          string `yaml:"bindir,omitempty"` // Deprecated: this does nothing. TODO: remove.
+	Changelog       string `yaml:"changelog,omitempty"`
+	Target          string `yaml:"-"`
 }
 
 // Overridables contain the field which are overridable in a package.
@@ -165,7 +166,7 @@ type RPM struct {
 type Deb struct {
 	Scripts         DebScripts  `yaml:"scripts,omitempty"`
 	Triggers        DebTriggers `yaml:"triggers,omitempty"`
-	VersionMetadata string      `yaml:"metadata,omitempty"`
+	VersionMetadata string      `yaml:"metadata,omitempty"` // Deprecated: Moved to Info
 }
 
 // DebTriggers contains triggers only available for deb packages.
@@ -213,6 +214,18 @@ func Validate(info *Info) error {
 	if info.Version == "" {
 		return ErrFieldEmpty{"version"}
 	}
+
+	// deprecation warnings
+	if info.Deb.VersionMetadata != "" {
+		fmt.Fprintln(os.Stderr,
+			"Warning: deb.metadata is deprecated and will be removed in a future version "+
+				"(moved to version_metadata)")
+	}
+
+	if info.Bindir != "" {
+		fmt.Fprintln(os.Stderr, "Warning: bindir is deprecated and will be removed in a future version")
+	}
+
 	return nil
 }
 
@@ -232,6 +245,10 @@ func WithDefaults(info *Info) *Info {
 		if info.Prerelease == "" {
 			info.Prerelease = v.Prerelease()
 		}
+
+		if info.VersionMetadata == "" {
+			info.VersionMetadata = v.Metadata()
+		}
 	}
 
 	return info
diff --git a/rpm/rpm.go b/rpm/rpm.go
index 4852a26..eaa1dea 100644
--- a/rpm/rpm.go
+++ b/rpm/rpm.go
@@ -228,11 +228,17 @@ func buildRPMMeta(info *nfpm.Info) (*rpmpack.RPMMetaData, error) {
 }
 
 func formatVersion(info *nfpm.Info) string {
-	if info.Prerelease == "" {
-		return info.Version
+	version := info.Version
+
+	if info.Prerelease != "" {
+		version += "~" + info.Prerelease
+	}
+
+	if info.VersionMetadata != "" {
+		version += "+" + info.VersionMetadata
 	}
 
-	return info.Version + "~" + info.Prerelease
+	return version
 }
 
 func defaultTo(in, def string) string {
diff --git a/www/docs/configuration.md b/www/docs/configuration.md
index e0b2d2a..a402c01 100644
--- a/www/docs/configuration.md
+++ b/www/docs/configuration.md
@@ -22,14 +22,18 @@ version: v1.2.3
 # Default is extracted from `version` if it is semver compatible.
 epoch: 2
 
-# Version Release.
-# Default is extracted from `version` if it is semver compatible.
-release: 1
-
 # Version Prerelease.
 # Default is extracted from `version` if it is semver compatible.
 prerelease: beta1
 
+# Version Metadata (previously deb.metadata).
+# Default is extracted from `version` if it is semver compatible.
+# Setting metadata might interfere with version comparisons depending on the packager.
+version_metadata: git
+
+# Version Release.
+release: 1
+
 # Section.
 section: default
 
@@ -141,10 +145,6 @@ rpm:
 
 # Custon configuration applied only to the Deb packager.
 deb:
-  # Custom version metadata.
-  # Setting metadata might interfere with version comparisons.
-  metadata: xyz2
-
   # Custom deb rules script.
   scripts:
     rules: foo.sh


Test patch:
diff --git a/acceptance/testdata/apk.env-var-version.dockerfile b/acceptance/testdata/apk.env-var-version.dockerfile
index 6f05a9e..6ee0f36 100644
--- a/acceptance/testdata/apk.env-var-version.dockerfile
+++ b/acceptance/testdata/apk.env-var-version.dockerfile
@@ -2,7 +2,7 @@ FROM alpine
 ARG package
 COPY ${package} /tmp/foo.apk
 RUN apk add --allow-untrusted /tmp/foo.apk
-ENV EXPECTVER="foo-1.0.0~0.1.b1 description:"
+ENV EXPECTVER="foo-1.0.0~0.1.b1+git.abcdefgh description:"
 RUN apk info foo | grep "foo-" | grep " description:" > found
 RUN export FOUND_VER="$(cat found)" && \
     echo "Expected: '${EXPECTVER}' :: Found: '${FOUND_VER}'" && \
diff --git a/acceptance/testdata/deb.env-var-version.dockerfile b/acceptance/testdata/deb.env-var-version.dockerfile
index 43c850a..117f28c 100644
--- a/acceptance/testdata/deb.env-var-version.dockerfile
+++ b/acceptance/testdata/deb.env-var-version.dockerfile
@@ -1,7 +1,7 @@
 FROM ubuntu
 ARG package
 COPY ${package} /tmp/foo.deb
-ENV EXPECTVER=" Version: 1.0.0~0.1.b1"
+ENV EXPECTVER=" Version: 1.0.0~0.1.b1+git.abcdefgh"
 RUN dpkg --info /tmp/foo.deb | grep "Version" > found
 RUN export FOUND_VER="$(cat found)" && \
     echo "Expected: '${EXPECTVER}' :: Found: '${FOUND_VER}'" && \
diff --git a/acceptance/testdata/rpm.env-var-version.dockerfile b/acceptance/testdata/rpm.env-var-version.dockerfile
index 2922d1f..165587d 100644
--- a/acceptance/testdata/rpm.env-var-version.dockerfile
+++ b/acceptance/testdata/rpm.env-var-version.dockerfile
@@ -1,7 +1,7 @@
 FROM fedora
 ARG package
 COPY ${package} /tmp/foo.rpm
-ENV EXPECTVER="Version : 1.0.0~0.1.b1" \
+ENV EXPECTVER="Version : 1.0.0~0.1.b1+git.abcdefgh" \
     EXPECTREL="Release : 1"
 RUN rpm -qpi /tmp/foo.rpm | sed -e 's/ \+/ /g' | grep "Version" > found.ver
 RUN rpm -qpi /tmp/foo.rpm | sed -e 's/ \+/ /g' | grep "Release" > found.rel
diff --git a/deb/deb_test.go b/deb/deb_test.go
index fa6d40a..e791ba1 100644
--- a/deb/deb_test.go
+++ b/deb/deb_test.go
@@ -121,28 +121,61 @@ func TestDebVersionWithRelease(t *testing.T) {
 }
 
 func TestDebVersionWithPrerelease(t *testing.T) {
+	var buf bytes.Buffer
+
 	info := exampleInfo()
 	info.Version = "1.0.0" //nolint:golint,goconst
 	info.Prerelease = "1"
-	var buf bytes.Buffer
-	var err = writeControl(&buf, controlData{info, 0})
+	err := writeControl(&buf, controlData{info, 0})
 	require.NoError(t, err)
-	var v = extractDebVersion(&buf)
+	v := extractDebVersion(&buf)
 	assert.Equal(t, "1.0.0~1", v)
 }
 
 func TestDebVersionWithReleaseAndPrerelease(t *testing.T) {
+	var buf bytes.Buffer
+
 	info := exampleInfo()
 	info.Version = "1.0.0" //nolint:golint,goconst
 	info.Release = "2"
-	info.Prerelease = "rc1"
-	var buf bytes.Buffer
-	var err = writeControl(&buf, controlData{info, 0})
+	info.Prerelease = "rc1" //nolint:golint,goconst
+	err := writeControl(&buf, controlData{info, 0})
 	require.NoError(t, err)
-	var v = extractDebVersion(&buf)
+	v := extractDebVersion(&buf)
 	assert.Equal(t, "1.0.0-2~rc1", v)
 }
 
+func TestDebVersionWithVersionMetadata(t *testing.T) {
+	var buf bytes.Buffer
+
+	info := exampleInfo()
+	info.Version = "1.0.0+meta" //nolint:golint,goconst
+	info.VersionMetadata = ""
+	err := writeControl(&buf, controlData{info, 0})
+	require.NoError(t, err)
+	v := extractDebVersion(&buf)
+	assert.Equal(t, "1.0.0+meta", v)
+
+	buf.Reset()
+
+	info.Version = "1.0.0" //nolint:golint,goconst
+	info.VersionMetadata = "meta"
+	err = writeControl(&buf, controlData{info, 0})
+	require.NoError(t, err)
+	v = extractDebVersion(&buf)
+	assert.Equal(t, "1.0.0+meta", v)
+
+	buf.Reset()
+
+	info.Version = "1.0.0+foo" //nolint:golint,goconst
+	info.Prerelease = "alpha"
+	info.VersionMetadata = "meta"
+	err = writeControl(&buf, controlData{nfpm.WithDefaults(info), 0})
+	require.NoError(t, err)
+	v = extractDebVersion(&buf)
+	assert.Equal(t, "1.0.0~alpha+meta", v)
+}
+
 func TestControl(t *testing.T) {
 	var w bytes.Buffer
 	assert.NoError(t, writeControl(&w, controlData{
@@ -399,21 +432,25 @@ func TestDEBConventionalFileName(t *testing.T) {
 		Release    string
 		Prerelease string
 		Expected   string
+		Metadata   string
 	}{
-		{Version: "1.2.3", Release: "", Prerelease: "",
+		{Version: "1.2.3", Release: "", Prerelease: "", Metadata: "",
 			Expected: fmt.Sprintf("%s_1.2.3_%s.deb", info.Name, info.Arch)},
-		{Version: "1.2.3", Release: "4", Prerelease: "",
+		{Version: "1.2.3", Release: "4", Prerelease: "", Metadata: "",
 			Expected: fmt.Sprintf("%s_1.2.3-4_%s.deb", info.Name, info.Arch)},
-		{Version: "1.2.3", Release: "4", Prerelease: "5",
+		{Version: "1.2.3", Release: "4", Prerelease: "5", Metadata: "",
 			Expected: fmt.Sprintf("%s_1.2.3-4~5_%s.deb", info.Name, info.Arch)},
-		{Version: "1.2.3", Release: "", Prerelease: "5",
+		{Version: "1.2.3", Release: "", Prerelease: "5", Metadata: "",
 			Expected: fmt.Sprintf("%s_1.2.3~5_%s.deb", info.Name, info.Arch)},
+		{Version: "1.2.3", Release: "1", Prerelease: "5", Metadata: "git",
+			Expected: fmt.Sprintf("%s_1.2.3-1~5+git_%s.deb", info.Name, info.Arch)},
 	}
 
 	for _, testCase := range testCases {
 		info.Version = testCase.Version
 		info.Release = testCase.Release
 		info.Prerelease = testCase.Prerelease
+		info.VersionMetadata = testCase.Metadata
 
 		assert.Equal(t, testCase.Expected, Default.ConventionalFileName(info))
 	}
diff --git a/rpm/rpm_test.go b/rpm/rpm_test.go
index 6c673b4..c6b1bf8 100644
--- a/rpm/rpm_test.go
+++ b/rpm/rpm_test.go
@@ -249,6 +249,23 @@ func TestRPMVersionWithReleaseAndPrerelease(t *testing.T) {
 	assert.Equal(t, "0.2", meta.Release)
 }
 
+func TestRPMVersionWithVersionMetadata(t *testing.T) {
+	// https://fedoraproject.org/wiki/Package_Versioning_Examples#Complex_versioning_examples
+	info := exampleInfo()
+
+	info.Version = "1.0.0+meta"
+	info.VersionMetadata = ""
+	meta, err := buildRPMMeta(nfpm.WithDefaults(info))
+	require.NoError(t, err)
+	assert.Equal(t, "1.0.0+meta", meta.Version)
+
+	info.Version = "1.0.0"
+	info.VersionMetadata = "meta"
+	meta, err = buildRPMMeta(nfpm.WithDefaults(info))
+	require.NoError(t, err)
+	assert.Equal(t, "1.0.0+meta", meta.Version)
+}
+
 func TestWithInvalidEpoch(t *testing.T) {
 	f, err := ioutil.TempFile("", "test.rpm")
 	defer func() {
@@ -378,21 +395,25 @@ func TestRPMConventionalFileName(t *testing.T) {
 		Release    string
 		Prerelease string
 		Expected   string
+		Metadata   string
 	}{
-		{Version: "1.2.3", Release: "", Prerelease: "",
+		{Version: "1.2.3", Release: "", Prerelease: "", Metadata: "",
 			Expected: fmt.Sprintf("%s-1.2.3.%s.rpm", info.Name, info.Arch)},
-		{Version: "1.2.3", Release: "4", Prerelease: "",
+		{Version: "1.2.3", Release: "4", Prerelease: "", Metadata: "",
 			Expected: fmt.Sprintf("%s-1.2.3-4.%s.rpm", info.Name, info.Arch)},
-		{Version: "1.2.3", Release: "4", Prerelease: "5",
+		{Version: "1.2.3", Release: "4", Prerelease: "5", Metadata: "",
 			Expected: fmt.Sprintf("%s-1.2.3~5-4.%s.rpm", info.Name, info.Arch)},
-		{Version: "1.2.3", Release: "", Prerelease: "5",
+		{Version: "1.2.3", Release: "", Prerelease: "5", Metadata: "",
 			Expected: fmt.Sprintf("%s-1.2.3~5.%s.rpm", info.Name, info.Arch)},
+		{Version: "1.2.3", Release: "1", Prerelease: "5", Metadata: "git",
+			Expected: fmt.Sprintf("%s-1.2.3~5+git-1.%s.rpm", info.Name, info.Arch)},
 	}
 
 	for _, testCase := range testCases {
 		info.Version = testCase.Version
 		info.Release = testCase.Release
 		info.Prerelease = testCase.Prerelease
+		info.VersionMetadata = testCase.Metadata
 
 		assert.Equal(t, testCase.Expected, Default.ConventionalFileName(info))
 	}