Repository: goreleaser/goreleaser
Pull request #1014 (2019-05-07 09:41:53)

feat: multiple nfpm
part of #705 

adding multiple nfpm support

codecov-io: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/1014?src=pr&el=h1) Report
> Merging [#1014](https://codecov.io/gh/goreleaser/goreleaser/pull/1014?src=pr&el=desc) into [master](https://codecov.io/gh/goreleaser/goreleaser/commit/76fa5909a2dbf261ec188188db66a294786188c8?src=pr&el=desc) will **increase** coverage by `0.07%`.
> The diff coverage is `97.61%`.

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

```diff
@@            Coverage Diff             @@
##           master    #1014      +/-   ##
==========================================
+ Coverage   88.33%   88.41%   +0.07%     
==========================================
  Files          52       52              
  Lines        2564     2581      +17     
==========================================
+ Hits         2265     2282      +17     
  Misses        236      236              
  Partials       63       63
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/1014?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pkg/config/config.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1014/diff?src=pr&el=tree#diff-cGtnL2NvbmZpZy9jb25maWcuZ28=) | `100% <ø> (ø)` | :arrow_up: |
| [internal/pipe/nfpm/nfpm.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1014/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGlwZS9uZnBtL25mcG0uZ28=) | `92.18% <97.61%> (+1.19%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/1014?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/goreleaser/pull/1014?src=pr&el=footer). Last update [76fa590...842e147](https://codecov.io/gh/goreleaser/goreleaser/pull/1014?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.goreleaser.yml b/.goreleaser.yml
index e5bb5e59..793df506 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -89,20 +89,21 @@ scoop:
   homepage:  https://goreleaser.com
   description: Deliver Go binaries as fast and easily as possible
   license: MIT
-nfpm:
-  name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
-  homepage:  https://goreleaser.com
-  description: Deliver Go binaries as fast and easily as possible
-  maintainer: Carlos Alexandro Becker <root@carlosbecker.com>
-  license: MIT
-  vendor: GoReleaser
-  formats:
-  - deb
-  - rpm
-  dependencies:
-  - git
-  recommends:
-  - rpm
+nfpms:
+  -
+    name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
+    homepage:  https://goreleaser.com
+    description: Deliver Go binaries as fast and easily as possible
+    maintainer: Carlos Alexandro Becker <root@carlosbecker.com>
+    license: MIT
+    vendor: GoReleaser
+    formats:
+    - deb
+    - rpm
+    dependencies:
+    - git
+    recommends:
+    - rpm
 snapcraft:
   name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
   summary: Deliver Go binaries as fast and easily as possible
diff --git a/internal/pipe/nfpm/nfpm.go b/internal/pipe/nfpm/nfpm.go
index 0e8a23b1..d2f4c0aa 100644
--- a/internal/pipe/nfpm/nfpm.go
+++ b/internal/pipe/nfpm/nfpm.go
@@ -2,11 +2,14 @@
 package nfpm
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
+	"reflect"
 
 	"github.com/apex/log"
 	"github.com/goreleaser/goreleaser/internal/artifact"
+	"github.com/goreleaser/goreleaser/internal/deprecate"
 	"github.com/goreleaser/goreleaser/internal/linux"
 	"github.com/goreleaser/goreleaser/internal/pipe"
 	"github.com/goreleaser/goreleaser/internal/semerrgroup"
@@ -31,52 +34,74 @@ func (Pipe) String() string {
 
 // Default sets the pipe defaults
 func (Pipe) Default(ctx *context.Context) error {
-	var fpm = &ctx.Config.NFPM
-	if fpm.Bindir == "" {
-		fpm.Bindir = "/usr/local/bin"
-	}
-	if fpm.NameTemplate == "" {
-		fpm.NameTemplate = defaultNameTemplate
+	if len(ctx.Config.NFPMs) == 0 {
+		ctx.Config.NFPMs = append(ctx.Config.NFPMs, ctx.Config.NFPM)
+		if !reflect.DeepEqual(ctx.Config.NFPM, config.NFPM{}) {
+			deprecate.Notice("nfpm")
+		}
 	}
-	if fpm.Files == nil {
-		fpm.Files = map[string]string{}
+	for i := range ctx.Config.NFPMs {
+		var fpm = &ctx.Config.NFPMs[i]
+		if fpm.Bindir == "" {
+			fpm.Bindir = "/usr/local/bin"
+		}
+		if fpm.NameTemplate == "" {
+			fpm.NameTemplate = defaultNameTemplate
+		}
+		if fpm.Files == nil {
+			fpm.Files = map[string]string{}
+		}
+		if len(fpm.Builds) == 0 {
+			for _, b := range ctx.Config.Builds {
+				fpm.Builds = append(fpm.Builds, b.ID)
+			}
+		}
 	}
 	return nil
 }
 
 // Run the pipe
 func (Pipe) Run(ctx *context.Context) error {
-	if len(ctx.Config.NFPM.Formats) == 0 {
-		return pipe.Skip("no output formats configured")
+	for _, nfpm := range ctx.Config.NFPMs {
+		if len(nfpm.Formats) == 0 {
+			return pipe.Skip("no output formats configured")
+		}
+		if err := doRun(ctx, nfpm); err != nil {
+			return err
+		}
 	}
-	return doRun(ctx)
+	return nil
 }
 
-func doRun(ctx *context.Context) error {
+func doRun(ctx *context.Context, fpm config.NFPM) error {
 	var linuxBinaries = ctx.Artifacts.Filter(artifact.And(
 		artifact.ByType(artifact.Binary),
 		artifact.ByGoos("linux"),
+		artifact.ByIDs(fpm.Builds...),
 	)).GroupByPlatform()
+	if len(linuxBinaries) == 0 {
+		return fmt.Errorf("no linux binaries found for builds %v", fpm.Builds)
+	}
 	var g = semerrgroup.New(ctx.Parallelism)
-	for _, format := range ctx.Config.NFPM.Formats {
+	for _, format := range fpm.Formats {
 		for platform, artifacts := range linuxBinaries {
 			format := format
 			arch := linux.Arch(platform)
 			artifacts := artifacts
 			g.Go(func() error {
-				return create(ctx, format, arch, artifacts)
+				return create(ctx, fpm, format, arch, artifacts)
 			})
 		}
 	}
 	return g.Wait()
 }
 
-func mergeOverrides(ctx *context.Context, format string) (*config.NFPMOverridables, error) {
+func mergeOverrides(fpm config.NFPM, format string) (*config.NFPMOverridables, error) {
 	var overrided config.NFPMOverridables
-	if err := mergo.Merge(&overrided, ctx.Config.NFPM.NFPMOverridables); err != nil {
+	if err := mergo.Merge(&overrided, fpm.NFPMOverridables); err != nil {
 		return nil, err
 	}
-	perFormat, ok := ctx.Config.NFPM.Overrides[format]
+	perFormat, ok := fpm.Overrides[format]
 	if ok {
 		err := mergo.Merge(&overrided, perFormat, mergo.WithOverride)
 		if err != nil {
@@ -86,8 +111,8 @@ func mergeOverrides(ctx *context.Context, format string) (*config.NFPMOverridabl
 	return &overrided, nil
 }
 
-func create(ctx *context.Context, format, arch string, binaries []artifact.Artifact) error {
-	overrided, err := mergeOverrides(ctx, format)
+func create(ctx *context.Context, fpm config.NFPM, format, arch string, binaries []artifact.Artifact) error {
+	overrided, err := mergeOverrides(fpm, format)
 	if err != nil {
 		return err
 	}
@@ -104,7 +129,7 @@ func create(ctx *context.Context, format, arch string, binaries []artifact.Artif
 	var log = log.WithField("package", name+"."+format).WithField("arch", arch)
 	for _, binary := range binaries {
 		src := binary.Path
-		dst := filepath.Join(ctx.Config.NFPM.Bindir, binary.Name)
+		dst := filepath.Join(fpm.Bindir, binary.Name)
 		log.WithField("src", src).WithField("dst", dst).Debug("adding binary to package")
 		files[src] = dst
 	}
@@ -117,12 +142,12 @@ func create(ctx *context.Context, format, arch string, binaries []artifact.Artif
 		Version:     ctx.Git.CurrentTag,
 		Section:     "",
 		Priority:    "",
-		Maintainer:  ctx.Config.NFPM.Maintainer,
-		Description: ctx.Config.NFPM.Description,
-		Vendor:      ctx.Config.NFPM.Vendor,
-		Homepage:    ctx.Config.NFPM.Homepage,
-		License:     ctx.Config.NFPM.License,
-		Bindir:      ctx.Config.NFPM.Bindir,
+		Maintainer:  fpm.Maintainer,
+		Description: fpm.Description,
+		Vendor:      fpm.Vendor,
+		Homepage:    fpm.Homepage,
+		License:     fpm.License,
+		Bindir:      fpm.Bindir,
 		Overridables: nfpm.Overridables{
 			Conflicts:    overrided.Conflicts,
 			Depends:      overrided.Dependencies,
@@ -169,6 +194,9 @@ func create(ctx *context.Context, format, arch string, binaries []artifact.Artif
 		Goos:   binaries[0].Goos,
 		Goarch: binaries[0].Goarch,
 		Goarm:  binaries[0].Goarm,
+		Extra: map[string]interface{}{
+			"Builds": binaries,
+		},
 	})
 	return nil
 }
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 6b164035..a2303a0c 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -172,6 +172,7 @@ type NFPM struct {
 	NFPMOverridables `yaml:",inline"`
 	Overrides        map[string]NFPMOverridables `yaml:"overrides,omitempty"`
 
+	Builds      []string `yaml:",omitempty"`
 	Formats     []string `yaml:",omitempty"`
 	Vendor      string   `yaml:",omitempty"`
 	Homepage    string   `yaml:",omitempty"`
@@ -317,7 +318,8 @@ type Project struct {
 	Builds        []Build   `yaml:",omitempty"`
 	Archive       Archive   `yaml:",omitempty"` // TODO: remove this
 	Archives      []Archive `yaml:",omitempty"`
-	NFPM          NFPM      `yaml:",omitempty"`
+	NFPM          NFPM      `yaml:",omitempty"` // TODO: remove this
+	NFPMs         []NFPM    `yaml:"nfpms,omitempty"`
 	Snapcraft     Snapcraft `yaml:",omitempty"`
 	Snapshot      Snapshot  `yaml:",omitempty"`
 	Checksum      Checksum  `yaml:",omitempty"`
diff --git a/www/content/deprecations.md b/www/content/deprecations.md
index a31fc640..df6d55ff 100644
--- a/www/content/deprecations.md
+++ b/www/content/deprecations.md
@@ -33,6 +33,29 @@ to this:
 
 -->
 
+### nfpm
+
+> since 2019-05-07
+
+We now allow multiple NFPM config, so the `nfpm` statement will be removed.
+
+Change this:
+
+```yaml
+nfpm:
+  formats:
+    - deb
+```
+
+to this:
+
+```yaml
+nfpms:
+  -
+    formats:
+      - deb
+```
+
 ### archive
 
 > since 2019-04-16
diff --git a/www/content/nfpm.md b/www/content/nfpm.md
index 43fe0016..0f72d0ca 100644
--- a/www/content/nfpm.md
+++ b/www/content/nfpm.md
@@ -12,124 +12,132 @@ Available options:
 
 ```yml
 # .goreleaser.yml
-nfpm:
-  # You can change the name of the package.
-  # Default: `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}`
-  name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
-
-  # Replacements for GOOS and GOARCH in the package name.
-  # Keys should be valid GOOSs or GOARCHs.
-  # Values are the respective replacements.
-  # Default is empty.
-  replacements:
-    amd64: 64-bit
-    386: 32-bit
-    darwin: macOS
-    linux: Tux
-
-  # Your app's vendor.
-  # Default is empty.
-  vendor: Drum Roll Inc.
-  # Your app's homepage.
-  # Default is empty.
-  homepage: https://example.com/
-
-  # Your app's maintainer (probably you).
-  # Default is empty.
-  maintainer: Drummer <drum-roll@example.com>
-
-  # Your app's description.
-  # Default is empty.
-  description: Software to create fast and easy drum rolls.
-
-  # Your app's license.
-  # Default is empty.
-  license: Apache 2.0
-
-  # Formats to be generated.
-  formats:
-    - deb
-    - rpm
-
-  # Packages your package depends on.
-  dependencies:
-    - git
-    - zsh
-
-  # Packages your package recommends installing.
-  # For RPM packages rpmbuild >= 4.13 is required
-  recommends:
-    - bzr
-    - gtk
-
-  # Packages your package suggests installing.
-  # For RPM packages rpmbuild >= 4.13 is required
-  suggests:
-    - cvs
-    - ksh
-
-  # Packages that conflict with your package.
-  conflicts:
-    - svn
-    - bash
-
-  # Override default /usr/local/bin destination for binaries
-  bindir: /usr/bin
-
-  # Empty folders that should be created and managed by the packager
-  # implementation.
-  # Default is empty.
-  empty_folders:
-  - /var/log/foobar
-
-  # Files or directories to add to your package (beyond the binary).
-  # Keys are source paths/globs to get the files from.
-  # Values are the destination locations of the files in the package.
-  files:
-    "scripts/etc/init.d/": "/etc/init.d"
-    "path/**/glob": "/var/foo/glob"
-
-  # Config files to add to your package. They are about the same as
-  # the files keyword, except package managers treat them differently (while
-  # uninstalling, mostly).
-  # Keys are source paths/globs to get the files from.
-  # Values are the destination locations of the files in the package.
-  config_files:
-    "tmp/app_generated.conf": "/etc/app.conf"
-    "conf/*.conf": "/etc/foo/"
-
-  # Scripts to execute during the installation of the package.
-  # Keys are the possible targets during the installation process
-  # Values are the paths to the scripts which will be executed
-  scripts:
-    preinstall: "scripts/preinstall.sh"
-    postinstall: "scripts/postinstall.sh"
-    preremove: "scripts/preremove.sh"
-    postremove: "scripts/postremove.sh"
-
-  # Some attributes can be overrided per package format.
-  overrides:
-    deb:
-      conflicts:
-        - subversion
-      dependencies:
-        - git
-      suggests:
-        - gitk
-      recommends:
-        - tig
-      empty_folders:
-      - /var/log/bar
-    rpm:
-      replacements:
-        amd64: x86_64
-      name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Arch }}"
-      files:
-        "tmp/man.gz": "/usr/share/man/man8/app.8.gz"
-      config_files:
-        "tmp/app_generated.conf": "/etc/app-rpm.conf"
-      scripts:
-        preinstall: "scripts/preinstall-rpm.sh"
+nfpms:
+  # note that this is an array of nfpm configs
+  -
+    # You can change the name of the package.
+    # Default: `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}`
+    name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
+
+    # Build IDs for the builds you want to create NFPM packages for.
+    # Defaults to all builds.
+    builds:
+    - foo
+    - bar
+
+    # Replacements for GOOS and GOARCH in the package name.
+    # Keys should be valid GOOSs or GOARCHs.
+    # Values are the respective replacements.
+    # Default is empty.
+    replacements:
+      amd64: 64-bit
+      386: 32-bit
+      darwin: macOS
+      linux: Tux
+
+    # Your app's vendor.
+    # Default is empty.
+    vendor: Drum Roll Inc.
+    # Your app's homepage.
+    # Default is empty.
+    homepage: https://example.com/
+
+    # Your app's maintainer (probably you).
+    # Default is empty.
+    maintainer: Drummer <drum-roll@example.com>
+
+    # Your app's description.
+    # Default is empty.
+    description: Software to create fast and easy drum rolls.
+
+    # Your app's license.
+    # Default is empty.
+    license: Apache 2.0
+
+    # Formats to be generated.
+    formats:
+      - deb
+      - rpm
+
+    # Packages your package depends on.
+    dependencies:
+      - git
+      - zsh
+
+    # Packages your package recommends installing.
+    # For RPM packages rpmbuild >= 4.13 is required
+    recommends:
+      - bzr
+      - gtk
+
+    # Packages your package suggests installing.
+    # For RPM packages rpmbuild >= 4.13 is required
+    suggests:
+      - cvs
+      - ksh
+
+    # Packages that conflict with your package.
+    conflicts:
+      - svn
+      - bash
+
+    # Override default /usr/local/bin destination for binaries
+    bindir: /usr/bin
+
+    # Empty folders that should be created and managed by the packager
+    # implementation.
+    # Default is empty.
+    empty_folders:
+    - /var/log/foobar
+
+    # Files or directories to add to your package (beyond the binary).
+    # Keys are source paths/globs to get the files from.
+    # Values are the destination locations of the files in the package.
+    files:
+      "scripts/etc/init.d/": "/etc/init.d"
+      "path/**/glob": "/var/foo/glob"
+
+    # Config files to add to your package. They are about the same as
+    # the files keyword, except package managers treat them differently (while
+    # uninstalling, mostly).
+    # Keys are source paths/globs to get the files from.
+    # Values are the destination locations of the files in the package.
+    config_files:
+      "tmp/app_generated.conf": "/etc/app.conf"
+      "conf/*.conf": "/etc/foo/"
+
+    # Scripts to execute during the installation of the package.
+    # Keys are the possible targets during the installation process
+    # Values are the paths to the scripts which will be executed
+    scripts:
+      preinstall: "scripts/preinstall.sh"
+      postinstall: "scripts/postinstall.sh"
+      preremove: "scripts/preremove.sh"
+      postremove: "scripts/postremove.sh"
+
+    # Some attributes can be overrided per package format.
+    overrides:
+      deb:
+        conflicts:
+          - subversion
+        dependencies:
+          - git
+        suggests:
+          - gitk
+        recommends:
+          - tig
+        empty_folders:
+        - /var/log/bar
+      rpm:
+        replacements:
+          amd64: x86_64
+        name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Arch }}"
+        files:
+          "tmp/man.gz": "/usr/share/man/man8/app.8.gz"
+        config_files:
+          "tmp/app_generated.conf": "/etc/app-rpm.conf"
+        scripts:
+          preinstall: "scripts/preinstall-rpm.sh"
 ```
 
 > Learn more about the [name template engine](/templates).


Test patch:
diff --git a/internal/pipe/nfpm/nfpm_test.go b/internal/pipe/nfpm/nfpm_test.go
index decee615..09deed0d 100644
--- a/internal/pipe/nfpm/nfpm_test.go
+++ b/internal/pipe/nfpm/nfpm_test.go
@@ -11,11 +11,11 @@ import (
 	"github.com/goreleaser/goreleaser/internal/testlib"
 	"github.com/goreleaser/goreleaser/pkg/config"
 	"github.com/goreleaser/goreleaser/pkg/context"
-	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestDescription(t *testing.T) {
-	assert.NotEmpty(t, Pipe{}.String())
+	require.NotEmpty(t, Pipe{}.String())
 }
 
 func TestRunPipeNoFormats(t *testing.T) {
@@ -26,18 +26,22 @@ func TestRunPipeNoFormats(t *testing.T) {
 		Config:      config.Project{},
 		Parallelism: runtime.NumCPU(),
 	}
+	require.NoError(t, Pipe{}.Default(ctx))
 	testlib.AssertSkipped(t, Pipe{}.Run(ctx))
 }
 
 func TestRunPipeInvalidFormat(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "nope",
-		NFPM: config.NFPM{
-			Bindir:  "/usr/bin",
-			Formats: []string{"nope"},
-			NFPMOverridables: config.NFPMOverridables{
-				NameTemplate: defaultNameTemplate,
-				Files:        map[string]string{},
+		NFPMs: []config.NFPM{
+			{
+				Bindir:  "/usr/bin",
+				Formats: []string{"nope"},
+				Builds:  []string{"foo"},
+				NFPMOverridables: config.NFPMOverridables{
+					NameTemplate: defaultNameTemplate,
+					Files:        map[string]string{},
+				},
 			},
 		},
 	})
@@ -52,53 +56,59 @@ func TestRunPipeInvalidFormat(t *testing.T) {
 				Goarch: goarch,
 				Goos:   goos,
 				Type:   artifact.Binary,
+				Extra: map[string]interface{}{
+					"ID": "foo",
+				},
 			})
 		}
 	}
-	assert.Contains(t, Pipe{}.Run(ctx).Error(), `no packager registered for the format nope`)
+	require.Contains(t, Pipe{}.Run(ctx).Error(), `no packager registered for the format nope`)
 }
 
 func TestRunPipe(t *testing.T) {
 	folder, err := ioutil.TempDir("", "archivetest")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	var dist = filepath.Join(folder, "dist")
-	assert.NoError(t, os.Mkdir(dist, 0755))
-	assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
+	require.NoError(t, os.Mkdir(dist, 0755))
+	require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
 	var binPath = filepath.Join(dist, "mybin", "mybin")
 	_, err = os.Create(binPath)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		NFPM: config.NFPM{
-			Bindir:      "/usr/bin",
-			Formats:     []string{"deb", "rpm"},
-			Description: "Some description",
-			License:     "MIT",
-			Maintainer:  "me@me",
-			Vendor:      "asdf",
-			Homepage:    "https://goreleaser.github.io",
-			NFPMOverridables: config.NFPMOverridables{
-				NameTemplate: defaultNameTemplate,
-				Dependencies: []string{"make"},
-				Recommends:   []string{"svn"},
-				Suggests:     []string{"bzr"},
-				Conflicts:    []string{"git"},
-				EmptyFolders: []string{"/var/log/foobar"},
-				Files: map[string]string{
-					"./testdata/testfile.txt": "/usr/share/testfile.txt",
-				},
-				ConfigFiles: map[string]string{
-					"./testdata/testfile.txt": "/etc/nope.conf",
-				},
-				Replacements: map[string]string{
-					"linux": "Tux",
-				},
-			},
-			Overrides: map[string]config.NFPMOverridables{
-				"rpm": {
+		NFPMs: []config.NFPM{
+			{
+				Bindir:      "/usr/bin",
+				Builds:      []string{"default"},
+				Formats:     []string{"deb", "rpm"},
+				Description: "Some description",
+				License:     "MIT",
+				Maintainer:  "me@me",
+				Vendor:      "asdf",
+				Homepage:    "https://goreleaser.github.io",
+				NFPMOverridables: config.NFPMOverridables{
+					NameTemplate: defaultNameTemplate,
+					Dependencies: []string{"make"},
+					Recommends:   []string{"svn"},
+					Suggests:     []string{"bzr"},
+					Conflicts:    []string{"git"},
+					EmptyFolders: []string{"/var/log/foobar"},
+					Files: map[string]string{
+						"./testdata/testfile.txt": "/usr/share/testfile.txt",
+					},
 					ConfigFiles: map[string]string{
-						"./testdata/testfile.txt": "/etc/nope-rpm.conf",
+						"./testdata/testfile.txt": "/etc/nope.conf",
+					},
+					Replacements: map[string]string{
+						"linux": "Tux",
+					},
+				},
+				Overrides: map[string]config.NFPMOverridables{
+					"rpm": {
+						ConfigFiles: map[string]string{
+							"./testdata/testfile.txt": "/etc/nope-rpm.conf",
+						},
 					},
 				},
 			},
@@ -114,16 +124,19 @@ func TestRunPipe(t *testing.T) {
 				Goarch: goarch,
 				Goos:   goos,
 				Type:   artifact.Binary,
+				Extra: map[string]interface{}{
+					"ID": "default",
+				},
 			})
 		}
 	}
-	assert.NoError(t, Pipe{}.Run(ctx))
+	require.NoError(t, Pipe{}.Run(ctx))
 	var packages = ctx.Artifacts.Filter(artifact.ByType(artifact.LinuxPackage)).List()
-	assert.Len(t, packages, 4)
+	require.Len(t, packages, 4)
 	for _, pkg := range packages {
-		assert.Contains(t, pkg.Name, "mybin_1.0.0_Tux_", "linux should have been replaced by Tux")
+		require.Contains(t, pkg.Name, "mybin_1.0.0_Tux_", "linux should have been replaced by Tux")
 	}
-	assert.Len(t, ctx.Config.NFPM.Files, 1, "should not modify the config file list")
+	require.Len(t, ctx.Config.NFPMs[0].Files, 1, "should not modify the config file list")
 }
 
 func TestInvalidNameTemplate(t *testing.T) {
@@ -131,9 +144,37 @@ func TestInvalidNameTemplate(t *testing.T) {
 		Parallelism: runtime.NumCPU(),
 		Artifacts:   artifact.New(),
 		Config: config.Project{
-			NFPM: config.NFPM{
-				NFPMOverridables: config.NFPMOverridables{NameTemplate: "{{.Foo}"},
-				Formats:          []string{"deb"},
+			NFPMs: []config.NFPM{
+				{
+					NFPMOverridables: config.NFPMOverridables{NameTemplate: "{{.Foo}"},
+					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.Contains(t, Pipe{}.Run(ctx).Error(), `template: tmpl:1: unexpected "}" in operand`)
+}
+
+func TestNoBuildsFound(t *testing.T) {
+	var ctx = &context.Context{
+		Parallelism: runtime.NumCPU(),
+		Artifacts:   artifact.New(),
+		Config: config.Project{
+			NFPMs: []config.NFPM{
+				{
+					Formats: []string{"deb"},
+					Builds:  []string{"nope"},
+				},
 			},
 		},
 	}
@@ -142,24 +183,30 @@ func TestInvalidNameTemplate(t *testing.T) {
 		Goos:   "linux",
 		Goarch: "amd64",
 		Type:   artifact.Binary,
+		Extra: map[string]interface{}{
+			"ID": "default",
+		},
 	})
-	assert.Contains(t, Pipe{}.Run(ctx).Error(), `template: tmpl:1: unexpected "}" in operand`)
+	require.EqualError(t, Pipe{}.Run(ctx), `no linux binaries found for builds [nope]`)
 }
 
 func TestCreateFileDoesntExist(t *testing.T) {
 	folder, err := ioutil.TempDir("", "archivetest")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	var dist = filepath.Join(folder, "dist")
-	assert.NoError(t, os.Mkdir(dist, 0755))
-	assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
+	require.NoError(t, os.Mkdir(dist, 0755))
+	require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
 	var ctx = context.New(config.Project{
 		Dist:        dist,
 		ProjectName: "asd",
-		NFPM: config.NFPM{
-			Formats: []string{"deb", "rpm"},
-			NFPMOverridables: config.NFPMOverridables{
-				Files: map[string]string{
-					"testdata/testfile.txt": "/var/lib/test/testfile.txt",
+		NFPMs: []config.NFPM{
+			{
+				Formats: []string{"deb", "rpm"},
+				Builds:  []string{"default"},
+				NFPMOverridables: config.NFPMOverridables{
+					Files: map[string]string{
+						"testdata/testfile.txt": "/var/lib/test/testfile.txt",
+					},
 				},
 			},
 		},
@@ -173,20 +220,26 @@ func TestCreateFileDoesntExist(t *testing.T) {
 		Goos:   "linux",
 		Goarch: "amd64",
 		Type:   artifact.Binary,
+		Extra: map[string]interface{}{
+			"ID": "default",
+		},
 	})
-	assert.Contains(t, Pipe{}.Run(ctx).Error(), `dist/mybin/mybin: file does not exist`)
+	require.Contains(t, Pipe{}.Run(ctx).Error(), `dist/mybin/mybin: file does not exist`)
 }
 
 func TestInvalidConfig(t *testing.T) {
 	folder, err := ioutil.TempDir("", "archivetest")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	var dist = filepath.Join(folder, "dist")
-	assert.NoError(t, os.Mkdir(dist, 0755))
-	assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
+	require.NoError(t, os.Mkdir(dist, 0755))
+	require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
 	var ctx = context.New(config.Project{
 		Dist: dist,
-		NFPM: config.NFPM{
-			Formats: []string{"deb"},
+		NFPMs: []config.NFPM{
+			{
+				Formats: []string{"deb"},
+				Builds:  []string{"default"},
+			},
 		},
 	})
 	ctx.Git.CurrentTag = "v1.2.3"
@@ -197,58 +250,95 @@ func TestInvalidConfig(t *testing.T) {
 		Goos:   "linux",
 		Goarch: "amd64",
 		Type:   artifact.Binary,
+		Extra: map[string]interface{}{
+			"ID": "default",
+		},
 	})
-	assert.Contains(t, Pipe{}.Run(ctx).Error(), `invalid nfpm config: package name cannot be empty`)
+	require.Contains(t, Pipe{}.Run(ctx).Error(), `invalid nfpm config: package name cannot be empty`)
 }
 
 func TestDefault(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
-			NFPM: config.NFPM{},
+			NFPMs: []config.NFPM{},
+			Builds: []config.Build{
+				{ID: "foo"},
+				{ID: "bar"},
+			},
 		},
 	}
-	assert.NoError(t, Pipe{}.Default(ctx))
-	assert.Equal(t, "/usr/local/bin", ctx.Config.NFPM.Bindir)
-	assert.Equal(t, defaultNameTemplate, ctx.Config.NFPM.NameTemplate)
+	require.NoError(t, Pipe{}.Default(ctx))
+	require.Equal(t, "/usr/local/bin", ctx.Config.NFPMs[0].Bindir)
+	require.Equal(t, []string{"foo", "bar"}, ctx.Config.NFPMs[0].Builds)
+	require.Equal(t, defaultNameTemplate, ctx.Config.NFPMs[0].NameTemplate)
 }
 
-func TestDefaultSet(t *testing.T) {
+func TestDefaultDeprecate(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
 			NFPM: config.NFPM{
-				Bindir: "/bin",
-				NFPMOverridables: config.NFPMOverridables{
-					NameTemplate: "foo",
+				Formats: []string{"deb"},
+			},
+			Builds: []config.Build{
+				{ID: "foo"},
+				{ID: "bar"},
+			},
+		},
+	}
+	require.NoError(t, Pipe{}.Default(ctx))
+	require.Equal(t, "/usr/local/bin", ctx.Config.NFPMs[0].Bindir)
+	require.Equal(t, []string{"deb"}, ctx.Config.NFPMs[0].Formats)
+	require.Equal(t, []string{"foo", "bar"}, ctx.Config.NFPMs[0].Builds)
+	require.Equal(t, defaultNameTemplate, ctx.Config.NFPMs[0].NameTemplate)
+}
+
+func TestDefaultSet(t *testing.T) {
+	var ctx = &context.Context{
+		Config: config.Project{
+			Builds: []config.Build{
+				{ID: "foo"},
+				{ID: "bar"},
+			},
+			NFPMs: []config.NFPM{
+				{
+					Builds: []string{"foo"},
+					Bindir: "/bin",
+					NFPMOverridables: config.NFPMOverridables{
+						NameTemplate: "foo",
+					},
 				},
 			},
 		},
 	}
-	assert.NoError(t, Pipe{}.Default(ctx))
-	assert.Equal(t, "/bin", ctx.Config.NFPM.Bindir)
-	assert.Equal(t, "foo", ctx.Config.NFPM.NameTemplate)
+	require.NoError(t, Pipe{}.Default(ctx))
+	require.Equal(t, "/bin", ctx.Config.NFPMs[0].Bindir)
+	require.Equal(t, "foo", ctx.Config.NFPMs[0].NameTemplate)
+	require.Equal(t, []string{"foo"}, ctx.Config.NFPMs[0].Builds)
 }
 
 func TestOverrides(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
-			NFPM: config.NFPM{
-				Bindir: "/bin",
-				NFPMOverridables: config.NFPMOverridables{
-					NameTemplate: "foo",
-				},
-				Overrides: map[string]config.NFPMOverridables{
-					"deb": {
-						NameTemplate: "bar",
+			NFPMs: []config.NFPM{
+				{
+					Bindir: "/bin",
+					NFPMOverridables: config.NFPMOverridables{
+						NameTemplate: "foo",
+					},
+					Overrides: map[string]config.NFPMOverridables{
+						"deb": {
+							NameTemplate: "bar",
+						},
 					},
 				},
 			},
 		},
 	}
-	assert.NoError(t, Pipe{}.Default(ctx))
-	merged, err := mergeOverrides(ctx, "deb")
-	assert.NoError(t, err)
-	assert.Equal(t, "/bin", ctx.Config.NFPM.Bindir)
-	assert.Equal(t, "foo", ctx.Config.NFPM.NameTemplate)
-	assert.Equal(t, "bar", ctx.Config.NFPM.Overrides["deb"].NameTemplate)
-	assert.Equal(t, "bar", merged.NameTemplate)
+	require.NoError(t, Pipe{}.Default(ctx))
+	merged, err := mergeOverrides(ctx.Config.NFPMs[0], "deb")
+	require.NoError(t, err)
+	require.Equal(t, "/bin", ctx.Config.NFPMs[0].Bindir)
+	require.Equal(t, "foo", ctx.Config.NFPMs[0].NameTemplate)
+	require.Equal(t, "bar", ctx.Config.NFPMs[0].Overrides["deb"].NameTemplate)
+	require.Equal(t, "bar", merged.NameTemplate)
 }