Repository: goreleaser/nfpm
Pull request #248 (2020-11-09 19:00:23)

feat: Add option to disable globbing
This also fixes the bug that detection of whether or not the file destination is a directory was wrong for certain patterns.

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/nfpm/cx7sfwdc8](https://vercel.com/goreleaser/nfpm/cx7sfwdc8)  
✅ Preview: *In Progress*


codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/nfpm/pull/248?src=pr&el=h1) Report
> Merging [#248](https://codecov.io/gh/goreleaser/nfpm/pull/248?src=pr&el=desc) (d074823) into [master](https://codecov.io/gh/goreleaser/nfpm/commit/8c19c6a283856b8d7659a34579180d925643ba83?el=desc) (8c19c6a) will **increase** coverage by `0.19%`.
> The diff coverage is `100.00%`.

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

```diff
@@            Coverage Diff             @@
##           master     #248      +/-   ##
==========================================
+ Coverage   66.73%   66.92%   +0.19%     
==========================================
  Files           9        9              
  Lines        1031     1031              
==========================================
+ Hits          688      690       +2     
+ Misses        206      205       -1     
+ Partials      137      136       -1     
```


| [Impacted Files](https://codecov.io/gh/goreleaser/nfpm/pull/248?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [nfpm.go](https://codecov.io/gh/goreleaser/nfpm/pull/248/diff?src=pr&el=tree#diff-bmZwbS5nbw==) | `78.04% <ø> (ø)` | |
| [apk/apk.go](https://codecov.io/gh/goreleaser/nfpm/pull/248/diff?src=pr&el=tree#diff-YXBrL2Fway5nbw==) | `66.52% <100.00%> (ø)` | |
| [deb/deb.go](https://codecov.io/gh/goreleaser/nfpm/pull/248/diff?src=pr&el=tree#diff-ZGViL2RlYi5nbw==) | `68.56% <100.00%> (ø)` | |
| [internal/files/files.go](https://codecov.io/gh/goreleaser/nfpm/pull/248/diff?src=pr&el=tree#diff-aW50ZXJuYWwvZmlsZXMvZmlsZXMuZ28=) | `92.30% <100.00%> (+1.39%)` | :arrow_up: |
| [internal/glob/glob.go](https://codecov.io/gh/goreleaser/nfpm/pull/248/diff?src=pr&el=tree#diff-aW50ZXJuYWwvZ2xvYi9nbG9iLmdv) | `81.81% <100.00%> (+1.17%)` | :arrow_up: |
| [rpm/rpm.go](https://codecov.io/gh/goreleaser/nfpm/pull/248/diff?src=pr&el=tree#diff-cnBtL3JwbS5nbw==) | `73.39% <100.00%> (+0.45%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goreleaser/nfpm/pull/248?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/248?src=pr&el=footer). Last update [8c19c6a...d074823](https://codecov.io/gh/goreleaser/nfpm/pull/248?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 a68ee39..6216410 100644
--- a/apk/apk.go
+++ b/apk/apk.go
@@ -396,12 +396,12 @@ func createBuilderData(info *nfpm.Info, sizep *int64) func(tw *tar.Writer) error
 }
 
 func createFilesInsideTarGz(info *nfpm.Info, tw *tar.Writer, created map[string]bool, sizep *int64) error {
-	filesToCopy, err := files.Expand(info.Files)
+	filesToCopy, err := files.Expand(info.Files, info.DisableGlobbing)
 	if err != nil {
 		return err
 	}
 
-	configFilesToCopy, err := files.Expand(info.ConfigFiles)
+	configFilesToCopy, err := files.Expand(info.ConfigFiles, info.DisableGlobbing)
 	if err != nil {
 		return err
 	}
diff --git a/deb/deb.go b/deb/deb.go
index 33d9cb5..bf42bc7 100644
--- a/deb/deb.go
+++ b/deb/deb.go
@@ -218,12 +218,12 @@ func createFilesInsideTarGz(info *nfpm.Info, out *tar.Writer, created map[string
 	var md5buf bytes.Buffer
 	var instSize int64
 
-	filesToCopy, err := files.Expand(info.Files)
+	filesToCopy, err := files.Expand(info.Files, info.DisableGlobbing)
 	if err != nil {
 		return md5buf, 0, err
 	}
 
-	configFiles, err := files.Expand(info.ConfigFiles)
+	configFiles, err := files.Expand(info.ConfigFiles, info.DisableGlobbing)
 	if err != nil {
 		return md5buf, 0, err
 	}
diff --git a/internal/files/files.go b/internal/files/files.go
index 17f24e0..083013d 100644
--- a/internal/files/files.go
+++ b/internal/files/files.go
@@ -3,6 +3,8 @@ package files
 import (
 	"sort"
 
+	"github.com/goreleaser/fileglob"
+
 	"github.com/goreleaser/nfpm/internal/glob"
 )
 
@@ -14,13 +16,17 @@ type FileToCopy struct {
 }
 
 // Expand gathers all of the real files to be copied into the package.
-func Expand(filesSrcDstMap map[string]string) ([]FileToCopy, error) {
+func Expand(filesSrcDstMap map[string]string, disableGlobbing bool) ([]FileToCopy, error) {
 	var files []FileToCopy
 
-	for srcglob, dstroot := range filesSrcDstMap {
-		globbed, err := glob.Glob(srcglob, dstroot)
+	for srcGlob, dstRoot := range filesSrcDstMap {
+		if disableGlobbing {
+			srcGlob = fileglob.QuoteMeta(srcGlob)
+		}
+
+		globbed, err := glob.Glob(srcGlob, dstRoot)
 		if err != nil {
-			return nil, err
+			return nil, err // nolint:wrapcheck
 		}
 		for src, dst := range globbed {
 			files = append(files, FileToCopy{src, dst})
diff --git a/internal/glob/glob.go b/internal/glob/glob.go
index f107e0e..a32c446 100644
--- a/internal/glob/glob.go
+++ b/internal/glob/glob.go
@@ -2,10 +2,10 @@
 package glob
 
 import (
+	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
-	"strings"
 
 	"github.com/goreleaser/fileglob"
 )
@@ -52,6 +52,9 @@ func (e ErrGlobNoMatch) Error() string {
 // for each globbed file is then dst joined with src with the lcp trimmed off.
 func Glob(pattern, dst string) (map[string]string, error) {
 	matches, err := fileglob.Glob(pattern)
+	if errors.Is(err, os.ErrNotExist) {
+		return nil, err
+	}
 	if err != nil {
 		return nil, fmt.Errorf("glob failed: %s: %w", pattern, err)
 	}
@@ -62,7 +65,7 @@ func Glob(pattern, dst string) (map[string]string, error) {
 	files := make(map[string]string)
 	prefix := longestCommonPrefix(matches)
 	// the prefix may not be a complete path or may use glob patterns, in that case use the parent directory
-	if _, err := os.Stat(prefix); os.IsNotExist(err) || strings.ContainsAny(pattern, "*") {
+	if _, err := os.Stat(prefix); os.IsNotExist(err) || fileglob.ContainsMatchers(pattern) {
 		prefix = filepath.Dir(prefix)
 	}
 	for _, src := range matches {
diff --git a/nfpm.go b/nfpm.go
index 05bbb43..e11b494 100644
--- a/nfpm.go
+++ b/nfpm.go
@@ -153,6 +153,7 @@ type Info struct {
 	License         string `yaml:"license,omitempty"`
 	Bindir          string `yaml:"bindir,omitempty"` // Deprecated: this does nothing. TODO: remove.
 	Changelog       string `yaml:"changelog,omitempty"`
+	DisableGlobbing bool   `yaml:"disable_globbing"`
 	Target          string `yaml:"-"`
 }
 
diff --git a/rpm/rpm.go b/rpm/rpm.go
index c920f13..16f8fe4 100644
--- a/rpm/rpm.go
+++ b/rpm/rpm.go
@@ -314,7 +314,7 @@ func addEmptyDirsRPM(info *nfpm.Info, rpm *rpmpack.RPM) {
 }
 
 func createFilesInsideRPM(info *nfpm.Info, rpm *rpmpack.RPM) error {
-	regularFiles, err := files.Expand(info.Files)
+	regularFiles, err := files.Expand(info.Files, info.DisableGlobbing)
 	if err != nil {
 		return err
 	}
@@ -326,7 +326,7 @@ func createFilesInsideRPM(info *nfpm.Info, rpm *rpmpack.RPM) error {
 		}
 	}
 
-	configFiles, err := files.Expand(info.ConfigFiles)
+	configFiles, err := files.Expand(info.ConfigFiles, info.DisableGlobbing)
 	if err != nil {
 		return err
 	}
@@ -338,7 +338,7 @@ func createFilesInsideRPM(info *nfpm.Info, rpm *rpmpack.RPM) error {
 		}
 	}
 
-	configNoReplaceFiles, err := files.Expand(info.RPM.ConfigNoReplaceFiles)
+	configNoReplaceFiles, err := files.Expand(info.RPM.ConfigNoReplaceFiles, info.DisableGlobbing)
 	if err != nil {
 		return err
 	}
diff --git a/www/docs/configuration.md b/www/docs/configuration.md
index 69dcaa1..e9238da 100644
--- a/www/docs/configuration.md
+++ b/www/docs/configuration.md
@@ -59,6 +59,9 @@ license: MIT
 # Changelog YAML file, see: https://github.com/goreleaser/chglog
 changelog: "changelog.yaml"
 
+# Disables globbing for files, config_files, etc.
+disable_globbing: false
+
 # Packages it replaces. (overridable)
 replaces:
   - foobar


Test patch:
diff --git a/apk/apk_test.go b/apk/apk_test.go
index 4f9f557..829c2e2 100644
--- a/apk/apk_test.go
+++ b/apk/apk_test.go
@@ -3,6 +3,7 @@ package apk
 import (
 	"archive/tar"
 	"bytes"
+	"compress/gzip"
 	"crypto/sha1" // nolint:gosec
 	"crypto/sha256"
 	"errors"
@@ -304,6 +305,29 @@ func TestSignatureError(t *testing.T) {
 	assert.True(t, ok)
 }
 
+func TestDisableGlobbing(t *testing.T) {
+	info := exampleInfo()
+	info.DisableGlobbing = true
+	info.Files = map[string]string{
+		"../testdata/{file}*": "/test/{file}*",
+	}
+
+	size := int64(0)
+	var dataTarGz bytes.Buffer
+	_, err := createData(&dataTarGz, info, &size)
+	require.NoError(t, err)
+
+	gzr, err := gzip.NewReader(&dataTarGz)
+	require.NoError(t, err)
+	dataTar, err := ioutil.ReadAll(gzr)
+	require.NoError(t, err)
+
+	extractedContent := extractFromTar(t, dataTar, "test/{file}*")
+	actualContent, err := ioutil.ReadFile("../testdata/{file}*")
+	require.NoError(t, err)
+	require.Equal(t, actualContent, extractedContent)
+}
+
 func extractFromTar(t *testing.T, tarFile []byte, fileName string) []byte {
 	t.Helper()
 
diff --git a/deb/deb_test.go b/deb/deb_test.go
index d1e2c72..5968b4d 100644
--- a/deb/deb_test.go
+++ b/deb/deb_test.go
@@ -776,6 +776,25 @@ func TestDebsigsSignatureError(t *testing.T) {
 	assert.True(t, ok)
 }
 
+func TestDisableGlobbing(t *testing.T) {
+	info := exampleInfo()
+	info.DisableGlobbing = true
+	info.Files = map[string]string{
+		"../testdata/{file}*": "/test/{file}*",
+	}
+
+	dataTarGz, _, _, err := createDataTarGz(info)
+	require.NoError(t, err)
+
+	expectedContent, err := ioutil.ReadFile("../testdata/{file}*")
+	require.NoError(t, err)
+
+	actualContent, err := extractFileFromTarGz(dataTarGz, "/test/{file}*")
+	require.NoError(t, err)
+
+	assert.Equal(t, expectedContent, actualContent)
+}
+
 func extractFileFromTarGz(tarGzFile []byte, filename string) ([]byte, error) {
 	tarFile, err := gzipInflate(tarGzFile)
 	if err != nil {
diff --git a/internal/files/files_test.go b/internal/files/files_test.go
index 4161d40..9644d64 100644
--- a/internal/files/files_test.go
+++ b/internal/files/files_test.go
@@ -3,6 +3,7 @@ package files
 import (
 	"testing"
 
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
 	"github.com/goreleaser/nfpm"
@@ -20,10 +21,10 @@ func TestListFilesToCopy(t *testing.T) {
 		},
 	}
 
-	regularFiles, err := Expand(info.Files)
+	regularFiles, err := Expand(info.Files, info.DisableGlobbing)
 	require.NoError(t, err)
 
-	configFiles, err := Expand(info.ConfigFiles)
+	configFiles, err := Expand(info.ConfigFiles, info.DisableGlobbing)
 	require.NoError(t, err)
 
 	// all the input files described in the config in sorted order by source path
@@ -38,3 +39,18 @@ func TestListFilesToCopy(t *testing.T) {
 		{"../../testdata/whatever.conf", "/whatever"},
 	}, configFiles)
 }
+
+func TestListFilesToCopyWithAndWithoutGlobbing(t *testing.T) {
+	_, err := Expand(map[string]string{
+		"../../testdata/{file}*": "/test/{file}*",
+	}, false)
+	assert.EqualError(t, err, "glob failed: ../../testdata/{file}*: no matching files")
+
+	files, err := Expand(map[string]string{
+		"../../testdata/{file}*": "/test/{file}*",
+	}, true)
+	require.NoError(t, err)
+	assert.Equal(t, []FileToCopy{
+		{"../../testdata/{file}*", "/test/{file}*"},
+	}, files)
+}
diff --git a/rpm/rpm_test.go b/rpm/rpm_test.go
index 597bd2e..75af57c 100644
--- a/rpm/rpm_test.go
+++ b/rpm/rpm_test.go
@@ -643,6 +643,26 @@ func TestRPMGhostFiles(t *testing.T) {
 	assert.Equal(t, "", string(packagedFile))
 }
 
+func TestDisableGlobbing(t *testing.T) {
+	info := exampleInfo()
+	info.DisableGlobbing = true
+	info.Files = map[string]string{
+		"../testdata/{file}*": "/test/{file}*",
+	}
+
+	var rpmFileBuffer bytes.Buffer
+	err := Default.Package(info, &rpmFileBuffer)
+	require.NoError(t, err)
+
+	expectedContent, err := ioutil.ReadFile("../testdata/{file}*")
+	require.NoError(t, err)
+
+	actualContent, err := extractFileFromRpm(rpmFileBuffer.Bytes(), "/test/{file}*")
+	require.NoError(t, err)
+
+	assert.Equal(t, expectedContent, actualContent)
+}
+
 func extractFileFromRpm(rpm []byte, filename string) ([]byte, error) {
 	rpmFile, err := rpmutils.ReadRpm(bytes.NewReader(rpm))
 	if err != nil {
diff --git a/testdata/{file}* b/testdata/{file}*
new file mode 100644
index 0000000..ff39838
--- /dev/null
+++ b/testdata/{file}*
@@ -0,0 +1,1 @@
+echo test
\ No newline at end of file