From 8db935ff15974be530d440059aeee4b32cc43848 Mon Sep 17 00:00:00 2001
From: Marc Auberer <marc.auberer@chillibits.com>
Date: Thu, 20 May 2021 19:21:48 +0200
Subject: [PATCH] feat: add support for name templates to NFPM contents (#2229)

---
 internal/pipe/nfpm/nfpm.go                    | 21 +++++--
 internal/pipe/nfpm/nfpm_test.go               | 57 ++++++++++++++++++-
 internal/pipe/nfpm/testdata/testfile-386.txt  |  1 +
 .../pipe/nfpm/testdata/testfile-amd64.txt     |  1 +
 www/docs/customization/nfpm.md                |  4 ++
 5 files changed, 79 insertions(+), 5 deletions(-)
 create mode 100644 internal/pipe/nfpm/testdata/testfile-386.txt
 create mode 100644 internal/pipe/nfpm/testdata/testfile-amd64.txt

diff --git a/internal/pipe/nfpm/nfpm.go b/internal/pipe/nfpm/nfpm.go
index 380906bab..baed637d5 100644
--- a/internal/pipe/nfpm/nfpm.go
+++ b/internal/pipe/nfpm/nfpm.go
@@ -174,19 +174,32 @@ func create(ctx *context.Context, fpm config.NFPM, format, arch string, binaries
 	if err != nil {
 		return err
 	}
-	name, err := tmpl.New(ctx).
+	tmpl := tmpl.New(ctx).
 		WithArtifact(binaries[0], overridden.Replacements).
 		WithExtraFields(tmpl.Fields{
 			"Release":     fpm.Release,
 			"Epoch":       fpm.Epoch,
 			"PackageName": fpm.PackageName,
-		}).
-		Apply(overridden.FileNameTemplate)
+		})
+	name, err := tmpl.Apply(overridden.FileNameTemplate)
 	if err != nil {
 		return err
 	}
 
-	contents := append(files.Contents{}, overridden.Contents...)
+	contents := files.Contents{}
+	for _, content := range overridden.Contents {
+		src, err := tmpl.Apply(content.Source)
+		if err != nil {
+			return err
+		}
+		contents = append(contents, &files.Content{
+			Source:      src,
+			Destination: content.Destination,
+			Type:        content.Type,
+			Packager:    content.Packager,
+			FileInfo:    content.FileInfo,
+		})
+	}
 
 	// FPM meta package should not contain binaries at all
 	if !fpm.Meta {
diff --git a/internal/pipe/nfpm/nfpm_test.go b/internal/pipe/nfpm/nfpm_test.go
index eaee27f84..ae3cde1a1 100644
--- a/internal/pipe/nfpm/nfpm_test.go
+++ b/internal/pipe/nfpm/nfpm_test.go
@@ -129,6 +129,10 @@ func TestRunPipe(t *testing.T) {
 							Destination: "/etc/nope2.conf",
 							Type:        "symlink",
 						},
+						{
+							Source:      "./testdata/testfile-{{ .Arch }}.txt",
+							Destination: "/etc/nope3.conf",
+						},
 					},
 					Replacements: map[string]string{
 						"linux": "Tux",
@@ -161,15 +165,24 @@ func TestRunPipe(t *testing.T) {
 		require.NotEmpty(t, format)
 		require.Equal(t, pkg.Name, "foo_1.0.0_Tux_"+pkg.Goarch+"-10-20."+format)
 		require.Equal(t, pkg.ExtraOr("ID", ""), "someid")
+		require.ElementsMatch(t, []string{
+			"./testdata/testfile.txt",
+			"./testdata/testfile.txt",
+			"./testdata/testfile.txt",
+			"/etc/nope.conf",
+			"./testdata/testfile-" + pkg.Goarch + ".txt",
+			binPath,
+		}, sources(pkg.ExtraOr("Files", files.Contents{}).(files.Contents)))
 		require.ElementsMatch(t, []string{
 			"/usr/share/testfile.txt",
 			"/etc/nope.conf",
 			"/etc/nope-rpm.conf",
 			"/etc/nope2.conf",
+			"/etc/nope3.conf",
 			"/usr/bin/mybin",
 		}, destinations(pkg.ExtraOr("Files", files.Contents{}).(files.Contents)))
 	}
-	require.Len(t, ctx.Config.NFPMs[0].Contents, 4, "should not modify the config file list")
+	require.Len(t, ctx.Config.NFPMs[0].Contents, 5, "should not modify the config file list")
 }
 
 func TestInvalidNameTemplate(t *testing.T) {
@@ -201,6 +214,40 @@ func TestInvalidNameTemplate(t *testing.T) {
 	require.Contains(t, Pipe{}.Run(ctx).Error(), `template: tmpl:1: unexpected "}" in operand`)
 }
 
+func TestRunPipeInvalidContentsSourceTemplate(t *testing.T) {
+	ctx := &context.Context{
+		Parallelism: runtime.NumCPU(),
+		Artifacts:   artifact.New(),
+		Config: config.Project{
+			NFPMs: []config.NFPM{
+				{
+					NFPMOverridables: config.NFPMOverridables{
+						PackageName: "foo",
+						Contents: []*files.Content{
+							{
+								Source:      "{{.asdsd}",
+								Destination: "testfile",
+							},
+						},
+					},
+					Formats: []string{"deb"},
+					Builds:  []string{"default"},
+				},
+			},
+		},
+	}
+	ctx.Artifacts.Add(&artifact.Artifact{
+		Name:   "mybin",
+		Goos:   "linux",
+		Goarch: "amd64",
+		Type:   artifact.Binary,
+		Extra: map[string]interface{}{
+			"ID": "default",
+		},
+	})
+	require.EqualError(t, Pipe{}.Run(ctx), `template: tmpl:1: unexpected "}" in operand`)
+}
+
 func TestNoBuildsFound(t *testing.T) {
 	ctx := &context.Context{
 		Parallelism: runtime.NumCPU(),
@@ -902,3 +949,11 @@ func TestSkipSign(t *testing.T) {
 		require.NoError(t, Pipe{}.Run(ctx))
 	})
 }
+
+func sources(contents files.Contents) []string {
+	result := make([]string, 0, len(contents))
+	for _, f := range contents {
+		result = append(result, f.Source)
+	}
+	return result
+}
diff --git a/internal/pipe/nfpm/testdata/testfile-386.txt b/internal/pipe/nfpm/testdata/testfile-386.txt
new file mode 100644
index 000000000..68a4528a6
--- /dev/null
+++ b/internal/pipe/nfpm/testdata/testfile-386.txt
@@ -0,0 +1 @@
+this is a test file
\ No newline at end of file
diff --git a/internal/pipe/nfpm/testdata/testfile-amd64.txt b/internal/pipe/nfpm/testdata/testfile-amd64.txt
new file mode 100644
index 000000000..68a4528a6
--- /dev/null
+++ b/internal/pipe/nfpm/testdata/testfile-amd64.txt
@@ -0,0 +1 @@
+this is a test file
\ No newline at end of file
diff --git a/www/docs/customization/nfpm.md b/www/docs/customization/nfpm.md
index d1531111c..1ff4a12ae 100644
--- a/www/docs/customization/nfpm.md
+++ b/www/docs/customization/nfpm.md
@@ -148,6 +148,10 @@ nfpms:
         dst: /etc/bar.conf
         type: "config|noreplace"
 
+      # The src attribute also supports name templates
+      - src: path/{{ .Os }}-{{ .Arch }}/bar.conf
+        dst: /etc/foo/bar.conf
+
       # These files are not actually present in the package, but the file names
       # are added to the package header. From the RPM directives documentation:
       #