Repository: goreleaser/goreleaser
Pull request #1273 (2019-12-17 11:26:34)

feat: specify (sub)directory with go code when building
This will enable using GoReleaser in multi-language projects where Go code (and `go.mod`/`go.sum`) is not in project's root directory.

An example of such project is [Oasis Core](https://github.com/oasislabs/oasis-core) (where we are planning on using GoReleaser to do releases).

codecov-io: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/1273?src=pr&el=h1) Report
> Merging [#1273](https://codecov.io/gh/goreleaser/goreleaser/pull/1273?src=pr&el=desc) into [master](https://codecov.io/gh/goreleaser/goreleaser/commit/6a759c1b95870c487e2940b16f7b7a870f6015da?src=pr&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `86.66%`.

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

```diff
@@            Coverage Diff             @@
##           master    #1273      +/-   ##
==========================================
- Coverage   83.75%   83.73%   -0.02%     
==========================================
  Files          58       58              
  Lines        3342     3351       +9     
==========================================
+ Hits         2799     2806       +7     
- Misses        461      462       +1     
- Partials       82       83       +1
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/1273?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pkg/config/config.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1273/diff?src=pr&el=tree#diff-cGtnL2NvbmZpZy9jb25maWcuZ28=) | `100% <ø> (ø)` | :arrow_up: |
| [internal/builders/golang/build.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1273/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYnVpbGRlcnMvZ29sYW5nL2J1aWxkLmdv) | `98.48% <100%> (+0.05%)` | :arrow_up: |
| [internal/pipe/build/build.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1273/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGlwZS9idWlsZC9idWlsZC5nbw==) | `95.74% <75%> (-2.04%)` | :arrow_down: |

------

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

caarlos0: in this case, I think it would probably be simpler to just `cd folder && goreleaser`, or, we could add a `--cwd` flag to the `goreleaser` binary...

any thoughts?
tjanez: > in this case, I think it would probably be simpler to just `cd folder && goreleaser`

I also though about this approach but I think GoReleaser could actually be quite useful for a whole (multi-language) project.

One could use it to build code in other languages (see #962 for an example) and/or upload custom project's artifacts to GitHub releases (see #1251).

Also, I would rather have `.goreleaser.yml` in project's top-level directory since it creates a GitHub release of the whole project and not just the Go part.

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 02f9cacb..64995bb1 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -12,3 +12,5 @@ linters:
     - lll
     - funlen
     - godox
+    - gomnd
+    - gocognit
diff --git a/go.mod b/go.mod
index f112fd04..7bd0c912 100644
--- a/go.mod
+++ b/go.mod
@@ -7,19 +7,18 @@ require (
 	github.com/Masterminds/semver/v3 v3.0.3
 	github.com/apex/log v1.1.1
 	github.com/aws/aws-sdk-go v1.25.11
-	github.com/caarlos0/cmdstream v0.1.0
 	github.com/caarlos0/ctrlc v1.0.0
 	github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e
-	github.com/fatih/color v1.7.0
+	github.com/fatih/color v1.9.0
 	github.com/golang/protobuf v1.3.2 // indirect
 	github.com/google/go-github/v28 v28.1.1
 	github.com/goreleaser/nfpm v1.1.9
 	github.com/imdario/mergo v0.3.8
 	github.com/jarcoal/httpmock v1.0.4
-	github.com/kamilsk/retry/v4 v4.3.1
+	github.com/kamilsk/retry/v4 v4.4.1
 	github.com/mattn/go-zglob v0.0.1
 	github.com/mitchellh/go-homedir v1.1.0
-	github.com/pkg/errors v0.8.1
+	github.com/pkg/errors v0.9.0
 	github.com/stretchr/testify v1.4.0
 	github.com/xanzy/go-gitlab v0.22.2
 	gocloud.dev v0.18.0
diff --git a/go.sum b/go.sum
index 6496adbc..07b598ff 100644
--- a/go.sum
+++ b/go.sum
@@ -44,8 +44,6 @@ github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
 github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
-github.com/caarlos0/cmdstream v0.1.0 h1:Y9EOUEp0e7Lh09vaVXq0psc6gqUYXnLzfD+WAciRW50=
-github.com/caarlos0/cmdstream v0.1.0/go.mod h1:JV0/yP9NA7jR+rYk7gDk0K43FxcxaduXpGhYEe8olNc=
 github.com/caarlos0/ctrlc v1.0.0 h1:2DtF8GSIcajgffDFJzyG15vO+1PuBWOMUdFut7NnXhw=
 github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw=
 github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e h1:V9a67dfYqPLAvzk5hMQOXYJlZ4SLIXgyKIE+ZiHzgGQ=
@@ -65,6 +63,8 @@ github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TR
 github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
 github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -130,8 +130,8 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht
 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/kamilsk/retry/v4 v4.3.1 h1:hNQmK1xAgybAVsadNAGvCNutFLS2h+Ycpw317u4d+i0=
-github.com/kamilsk/retry/v4 v4.3.1/go.mod h1:VaCDWufu3zVv7Ktt+Z7Dcslli2te4QEZoqt4QR7xgQg=
+github.com/kamilsk/retry/v4 v4.4.1 h1:nbcZ8axT87PrEg+7za3tBU9x5+iWeiiBc/jRxaq9DpE=
+github.com/kamilsk/retry/v4 v4.4.1/go.mod h1:pIQtBtycHTXScrJmpu3N2SSBT07s07Uruq2Au1aRLks=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -142,11 +142,15 @@ github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149 h1:HfxbT6/JcvIljmERptWhwa8XzP7H3T+Z2N26gTsaDaA=
 github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY=
 github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
@@ -160,6 +164,8 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWEr
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.0 h1:J8lpUdobwIeCI7OiSxHqEwJUKvJwicL5+3v1oe2Yb4k=
+github.com/pkg/errors v0.9.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -246,6 +252,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190620070143-6f217b454f45 h1:Dl2hc890lrizvUppGbRWhnIh2f8jOTCQpY5IKWRS0oM=
 golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
diff --git a/internal/builders/golang/build.go b/internal/builders/golang/build.go
index e522b337..1f4a248a 100644
--- a/internal/builders/golang/build.go
+++ b/internal/builders/golang/build.go
@@ -7,6 +7,7 @@ import (
 	"go/token"
 	"os"
 	"os/exec"
+	"path/filepath"
 	"strings"
 
 	"github.com/apex/log"
@@ -32,6 +33,9 @@ type Builder struct{}
 
 // WithDefaults sets the defaults for a golang build and returns it
 func (*Builder) WithDefaults(build config.Build) config.Build {
+	if build.Dir == "" {
+		build.Dir = "."
+	}
 	if build.Main == "" {
 		build.Main = "."
 	}
@@ -111,7 +115,7 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti
 	cmd = append(cmd, processedLdFlags)
 
 	cmd = append(cmd, "-o", options.Path, build.Main)
-	if err := run(ctx, cmd, env); err != nil {
+	if err := run(ctx, cmd, env, build.Dir); err != nil {
 		return errors.Wrapf(err, "failed to build for %s", options.Target)
 	}
 	ctx.Artifacts.Add(artifact)
@@ -138,11 +142,12 @@ func joinLdFlags(flags []string) string {
 	return ldflagString.String()
 }
 
-func run(ctx *context.Context, command, env []string) error {
+func run(ctx *context.Context, command, env []string, dir string) error {
 	/* #nosec */
 	var cmd = exec.CommandContext(ctx, command[0], command[1:]...)
 	var log = log.WithField("env", env).WithField("cmd", command)
 	cmd.Env = env
+	cmd.Dir = dir
 	log.Debug("running")
 	if out, err := cmd.CombinedOutput(); err != nil {
 		log.WithError(err).Debug("failed")
@@ -182,6 +187,9 @@ func checkMain(build config.Build) error {
 	if main == "" {
 		main = "."
 	}
+	if build.Dir != "" {
+		main = filepath.Join(build.Dir, main)
+	}
 	stat, ferr := os.Stat(main)
 	if ferr != nil {
 		return ferr
diff --git a/internal/http/http.go b/internal/http/http.go
index 63cad0a0..6540ccb6 100644
--- a/internal/http/http.go
+++ b/internal/http/http.go
@@ -372,9 +372,9 @@ func resolveTargetTemplate(ctx *context.Context, upload *config.Upload, artifact
 
 	if upload.Mode == ModeBinary {
 		// TODO: multiple archives here
-		data.Os = replace(ctx.Config.Archive.Replacements, artifact.Goos)
-		data.Arch = replace(ctx.Config.Archive.Replacements, artifact.Goarch)
-		data.Arm = replace(ctx.Config.Archive.Replacements, artifact.Goarm)
+		data.Os = replace(ctx.Config.Archives[0].Replacements, artifact.Goos)
+		data.Arch = replace(ctx.Config.Archives[0].Replacements, artifact.Goarch)
+		data.Arm = replace(ctx.Config.Archives[0].Replacements, artifact.Goarm)
 	}
 
 	var out bytes.Buffer
diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go
index 5f82721b..987c7024 100644
--- a/internal/pipe/archive/archive.go
+++ b/internal/pipe/archive/archive.go
@@ -7,14 +7,14 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"reflect"
 	"strings"
 	"sync"
 
 	"github.com/apex/log"
 	"github.com/campoy/unique"
+	"github.com/mattn/go-zglob"
+
 	"github.com/goreleaser/goreleaser/internal/artifact"
-	"github.com/goreleaser/goreleaser/internal/deprecate"
 	"github.com/goreleaser/goreleaser/internal/ids"
 	"github.com/goreleaser/goreleaser/internal/semerrgroup"
 	"github.com/goreleaser/goreleaser/internal/tmpl"
@@ -22,7 +22,6 @@ import (
 	archivelib "github.com/goreleaser/goreleaser/pkg/archive"
 	"github.com/goreleaser/goreleaser/pkg/config"
 	"github.com/goreleaser/goreleaser/pkg/context"
-	zglob "github.com/mattn/go-zglob"
 )
 
 const (
@@ -44,10 +43,7 @@ func (Pipe) String() string {
 func (Pipe) Default(ctx *context.Context) error {
 	var ids = ids.New("archives")
 	if len(ctx.Config.Archives) == 0 {
-		ctx.Config.Archives = append(ctx.Config.Archives, ctx.Config.Archive)
-		if !reflect.DeepEqual(ctx.Config.Archive, config.Archive{}) {
-			deprecate.Notice("archive")
-		}
+		ctx.Config.Archives = append(ctx.Config.Archives, config.Archive{})
 	}
 	for i := range ctx.Config.Archives {
 		var archive = &ctx.Config.Archives[i]
diff --git a/internal/pipe/build/build.go b/internal/pipe/build/build.go
index 3f8163c2..22914818 100644
--- a/internal/pipe/build/build.go
+++ b/internal/pipe/build/build.go
@@ -113,11 +113,16 @@ func doBuild(ctx *context.Context, build config.Build, target string) error {
 
 	build.Binary = binary
 	var name = build.Binary + ext
-	var path = filepath.Join(
-		ctx.Config.Dist,
-		fmt.Sprintf("%s_%s", build.ID, target),
-		name,
+	path, err := filepath.Abs(
+		filepath.Join(
+			ctx.Config.Dist,
+			fmt.Sprintf("%s_%s", build.ID, target),
+			name,
+		),
 	)
+	if err != nil {
+		return err
+	}
 	log.WithField("binary", path).Info("building")
 	return builders.For(build.Lang).Build(ctx, build, builders.Options{
 		Target: target,
diff --git a/internal/pipe/docker/docker.go b/internal/pipe/docker/docker.go
index 1ff46eed..dc4d818d 100644
--- a/internal/pipe/docker/docker.go
+++ b/internal/pipe/docker/docker.go
@@ -55,11 +55,6 @@ func (Pipe) Default(ctx *context.Context) error {
 			ctx.Config.Builds[0].Binary,
 		}
 	}
-	if len(ctx.Config.Dockers[0].Builds) == 0 {
-		ctx.Config.Dockers[0].Builds = []string{
-			ctx.Config.Builds[0].ID,
-		}
-	}
 	if ctx.Config.Dockers[0].Dockerfile == "" {
 		ctx.Config.Dockers[0].Dockerfile = "Dockerfile"
 	}
diff --git a/internal/pipe/scoop/scoop.go b/internal/pipe/scoop/scoop.go
index 494b15e0..a512627e 100644
--- a/internal/pipe/scoop/scoop.go
+++ b/internal/pipe/scoop/scoop.go
@@ -61,7 +61,8 @@ func doRun(ctx *context.Context, client client.Client) error {
 	// if ctx.Config.Release.Disable {
 	// }
 
-	if ctx.Config.Archive.Format == "binary" {
+	// TODO: multiple archives
+	if ctx.Config.Archives[0].Format == "binary" {
 		return pipe.Skip("archive format is binary")
 	}
 
diff --git a/internal/pipe/sign/sign.go b/internal/pipe/sign/sign.go
index d948a597..fa9afb58 100644
--- a/internal/pipe/sign/sign.go
+++ b/internal/pipe/sign/sign.go
@@ -8,7 +8,6 @@ import (
 	"reflect"
 
 	"github.com/apex/log"
-	"github.com/caarlos0/cmdstream"
 
 	"github.com/goreleaser/goreleaser/internal/artifact"
 	"github.com/goreleaser/goreleaser/internal/deprecate"
@@ -117,10 +116,10 @@ func signone(ctx *context.Context, cfg config.Sign, a *artifact.Artifact) (*arti
 	// tells the scanner to ignore this.
 	// #nosec
 	cmd := exec.CommandContext(ctx, cfg.Cmd, args...)
+	cmd.Stderr = logext.NewWriter(log.WithField("cmd", cfg.Cmd))
+	cmd.Stdout = cmd.Stderr
 	log.WithField("cmd", cmd.Args).Info("signing")
-	if err := cmdstream.Stream(
-		cmd, logext.NewWriter(log.WithField("cmd", cfg.Cmd)),
-	); err != nil {
+	if err := cmd.Run(); err != nil {
 		return nil, fmt.Errorf("sign: %s failed", cfg.Cmd)
 	}
 
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 79fd4116..c8505ace 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -144,6 +144,7 @@ type Build struct {
 	Goarm    []string       `yaml:",omitempty"`
 	Targets  []string       `yaml:",omitempty"`
 	Ignore   []IgnoredBuild `yaml:",omitempty"`
+	Dir      string         `yaml:",omitempty"`
 	Main     string         `yaml:",omitempty"`
 	Ldflags  StringArray    `yaml:",omitempty"`
 	Flags    FlagArray      `yaml:",omitempty"`
@@ -358,7 +359,6 @@ type Project struct {
 	Brews         []Homebrew  `yaml:",omitempty"`
 	Scoop         Scoop       `yaml:",omitempty"`
 	Builds        []Build     `yaml:",omitempty"`
-	Archive       Archive     `yaml:",omitempty"` // TODO: remove this
 	Archives      []Archive   `yaml:",omitempty"`
 	NFPMs         []NFPM      `yaml:"nfpms,omitempty"`
 	Snapcrafts    []Snapcraft `yaml:",omitempty"`
diff --git a/www/content/build.md b/www/content/build.md
index f92987e8..862f9eeb 100644
--- a/www/content/build.md
+++ b/www/content/build.md
@@ -21,6 +21,11 @@ builds:
     # Defaults to the project name.
     id: "my-build"
 
+    # Path to project's (sub)directory containing Go code.
+    # This is the working directory for the Go build command(s).
+    # Default is `.`.
+    dir: go
+
     # Path to main.go file or main package.
     # Default is `.`.
     main: ./cmd/main.go
diff --git a/www/content/ci.md b/www/content/ci.md
index 5edba5b0..38622fc1 100644
--- a/www/content/ci.md
+++ b/www/content/ci.md
@@ -161,13 +161,13 @@ pipeline:
 
 ## Google CloudBuild
 
-CloudBuild works off a different clone than your github repo: it seems that
+CloudBuild works off a different clone than your GitHub repo: it seems that
 your changes are pulled to a repo like
-source.developers.google.com/p/YourProjectId/r/github-YourGithubUser-YourGithubRepo, and that's what
-you're building off.
+`source.developers.google.com/p/YourProjectId/r/github-YourGithubUser-YourGithubRepo`,
+and that's what you're building off.
 
-This repo has the wrong name, so to prevent Goreleaser from publishing to
-the wrong github repo, add to your `.goreleaser.yml` file's release section:
+This repo has the wrong name, so to prevent GoReleaser from publishing to
+the wrong GitHub repo, add to your `.goreleaser.yml` file's release section:
 
 ```yml
 release:
@@ -178,23 +178,25 @@ release:
 
 Create two build triggers:
 
-- a "push to any branch" trigger for your regular CI (doesn't invoke goreleaser)
-- a "push to tag" trigger which invokes goreleaser
+- a "push to any branch" trigger for your regular CI (doesn't invoke GoReleaser)
+- a "push to tag" trigger which invokes GoReleaser
 
-The push to any branch trigger could use a Dockerfile or a cloudbuild.yaml,
+The push to any branch trigger could use a `Dockerfile` or a `cloudbuild.yaml`,
 whichever you prefer.
 
-You should have a dedicated cloudbuild.release.yaml that is only used by the "push to
-tag" trigger.
+You should have a dedicated `cloudbuild.release.yaml` that is only used by the
+"push to tag" trigger.
 
 In this example we're creating a new release every time a new tag is pushed.
-See [Using Encrypted Resources](https://cloud.google.com/cloud-build/docs/securing-builds/use-encrypted-secrets-credentials) for how to encrypt and base64-encode your github token.
-
-The clone that the build uses [has no
-tags](https://issuetracker.google.com/u/1/issues/113668706), which is why we
-must explicitly run git tag $TAG_NAME (note that $TAG_NAME is only set when
-your build is triggered by a "push to tag".) This will allow goreleaser to
-create a release with that version, but it won't be able to build a proper
+See [Using Encrypted Resources](https://cloud.google.com/cloud-build/docs/securing-builds/use-encrypted-secrets-credentials)
+for how to encrypt and base64-encode your github token.
+
+The clone that the build uses
+[has no tags](https://issuetracker.google.com/u/1/issues/113668706),
+which is why we must explicitly run `git tag $TAG_NAME` (note that `$TAG_NAME`
+is only set when your build is triggered by a "push to tag".)
+This will allow GoReleaser to create a release with that version,
+but it won't be able to build a proper
 changelog containing just the messages from the commits since the prior tag.
 Note that the build performs a shallow clone of git repositories and will
 only contain tags that reference the latest commit.
@@ -227,7 +229,7 @@ steps:
 ## Semaphore
 
 In [Sempahore 2.0](https://semaphoreci.com) each project starts with the
-default pipeline specified in .semaphore/semaphore.yml.
+default pipeline specified in `.semaphore/semaphore.yml`.
 
 ```yml
 # .semaphore/semaphore.yml.
@@ -266,7 +268,7 @@ promotions:
              - "^refs/tags/v*"
 ```
 
-Pipeline file in .semaphore/goreleaser.yml:
+Pipeline file in `.semaphore/goreleaser.yml`:
 
 ```yml
 version: "v1.0"
@@ -292,8 +294,8 @@ blocks:
           - curl -sL https://git.io/goreleaser | bash
 ```
 
-The following YAML file, `createSecret.yml` creates a new secret item that is called goreleaser
-with one environment variable, named GITHUB_TOKEN:
+The following YAML file, `createSecret.yml` creates a new secret item that is
+called GoReleaser with one environment variable, named `GITHUB_TOKEN`:
 
 ```yml
 apiVersion: v1alpha
@@ -306,12 +308,13 @@ data:
       value: "4afk4388304hfhei34950dg43245"
 ```
 
-Check [Managing Secrets](https://docs.semaphoreci.com/article/51-secrets-yaml-reference) for
-more detailed documentation.
+Check [Managing Secrets](https://docs.semaphoreci.com/article/51-secrets-yaml-reference)
+for more detailed documentation.
 
 ## GitLab CI
 
-To push releases to both GitHub and the **official** Docker registry, add a file `.gitlab-ci.yml` in the Go project directory:
+To push releases to both GitHub and the **official** Docker registry, add a
+file `.gitlab-ci.yml` in the Go project directory:
 
 ```yaml
 image: docker:stable
@@ -332,10 +335,13 @@ build:
     - docker run --rm --privileged -v $PWD:/go/src/github.com/YourGithubUser/YourGithubRepo -v /var/run/docker.sock:/var/run/docker.sock -w /go/src/github.com/YourGithubUser/YourGithubRepo -e GITHUB_TOKEN -e DOCKER_USERNAME -e DOCKER_PASSWORD -e DOCKER_REGISTRY $GORELEASER_IMAGE release --rm-dist
 ```
 
-Next, in the GitLab sidebar add the variables `DOCKER_USERNAME`, `DOCKER_PASSWORD` and `GITHUB_TOKEN` through Project --> Settings --> CI / CD --> Variables.\
+Next, in the GitLab sidebar add the variables `DOCKER_USERNAME`,
+`DOCKER_PASSWORD` and `GITHUB_TOKEN` through
+Project --> Settings --> CI / CD --> Variables.
 Make sure they are set to *Masked* (*Protection* is not needed).
 
-To push to some other Docker registry (e.g. to a GitLab registry), set different variables in the file above:
+To push to some other Docker registry (e.g. to a GitLab registry), set
+different variables in the file above:
 
 ```txt
 CI_REGISTRY: gitlab.example.com:4567
@@ -344,7 +350,8 @@ DOCKER_USERNAME: gitlab-ci-token
 DOCKER_PASSWORD: $CI_JOB_TOKEN
 ```
 
-Make sure the `image_templates` in the file `.goreleaser.yml` reflect that custom registry!
+Make sure the `image_templates` in the file `.goreleaser.yml` reflect that
+custom registry!
 
 Example:
 
@@ -362,9 +369,12 @@ dockers:
 
 ## Codefresh
 
-Codefresh uses Docker based pipelines where all steps must be Docker containers. Using Goreleaser is very easy via the [existing Docker image](https://hub.docker.com/r/goreleaser/goreleaser/).
+Codefresh uses Docker based pipelines where all steps must be Docker containers.
+Using GoReleaser is very easy via the
+[existing Docker image](https://hub.docker.com/r/goreleaser/goreleaser/).
 
-Here is an example pipeline that builds a Go application and then uses Goreleaser.
+Here is an example pipeline that builds a Go application and then uses
+GoReleaser.
 
 ```yaml
 version: '1.0'
@@ -393,7 +403,12 @@ steps:
       - goreleaser --rm-dist
 ```
 
-You need to pass the variable `GITHUB_TOKEN` in the Codefresh UI that contains credentials to your Github account or load it from [shared configuration](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/shared-configuration/).
-You should also restrict this pipeline to run only on tags when you add [git triggers](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/triggers/git-triggers/) on it.
+You need to pass the variable `GITHUB_TOKEN` in the Codefresh UI that
+contains credentials to your Github account or load it from
+[shared configuration](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/shared-configuration/).
+You should also restrict this pipeline to run only on tags when you add
+[git triggers](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/triggers/git-triggers/)
+on it.
 
-More details can be found in the [goreleaser example page](https://codefresh.io/docs/docs/learn-by-example/golang/goreleaser/).
+More details can be found in the
+[GoReleaser example page](https://codefresh.io/docs/docs/learn-by-example/golang/goreleaser/).
diff --git a/www/content/deprecations.md b/www/content/deprecations.md
index d9442d4a..d44f8c9b 100644
--- a/www/content/deprecations.md
+++ b/www/content/deprecations.md
@@ -175,9 +175,13 @@ blobs:
   # etc
 ```
 
+## Expired deprecation notices
+
+The following options were deprecated for ~6 months and are now fully removed.
+
 ### archive
 
-> since 2019-04-16
+> since 2019-04-16, removed 2019-12-27
 
 We now allow multiple archives, so the `archive` statement will be removed.
 
@@ -196,11 +200,6 @@ archives:
     format: zip
 ```
 
-## Expired deprecation notices
-
-The following options were deprecated for ~6 months and are now fully removed.
-
-
 ### snapcraft
 
 > since 2019-05-27, removed 2019-12-27
diff --git a/www/content/install.md b/www/content/install.md
index c512dd30..702f6bae 100644
--- a/www/content/install.md
+++ b/www/content/install.md
@@ -43,7 +43,7 @@ install with `dpkg -i` and `rpm -i` respectively.
 
 **Shell script**:
 
-```
+```sh
 $ curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | sh
 ```
 


Test patch:
diff --git a/internal/builders/golang/build_test.go b/internal/builders/golang/build_test.go
index 3841cd02..d6ed2af0 100644
--- a/internal/builders/golang/build_test.go
+++ b/internal/builders/golang/build_test.go
@@ -3,6 +3,7 @@ package golang
 import (
 	"fmt"
 	"io/ioutil"
+	"os"
 	"path/filepath"
 	"runtime"
 	"strings"
@@ -184,6 +185,38 @@ func TestBuild(t *testing.T) {
 	})
 }
 
+func TestBuildCodeInSubdir(t *testing.T) {
+	folder, back := testlib.Mktmp(t)
+	defer back()
+	subdir := filepath.Join(folder, "bar")
+	err := os.Mkdir(subdir, 0755)
+	assert.NoError(t, err)
+	writeGoodMain(t, subdir)
+	var config = config.Project{
+		Builds: []config.Build{
+			{
+				ID:     "foo",
+				Env:    []string{"GO111MODULE=off"},
+				Dir:    "bar",
+				Binary: "foo",
+				Targets: []string{
+					runtimeTarget,
+				},
+			},
+		},
+	}
+	var ctx = context.New(config)
+	ctx.Git.CurrentTag = "5.6.7"
+	var build = ctx.Config.Builds[0]
+	err = Default.Build(ctx, build, api.Options{
+		Target: runtimeTarget,
+		Name:   build.Binary,
+		Path:   filepath.Join(folder, "dist", runtimeTarget, build.Binary),
+		Ext:    "",
+	})
+	assert.NoError(t, err)
+}
+
 func TestBuildFailed(t *testing.T) {
 	folder, back := testlib.Mktmp(t)
 	defer back()
diff --git a/internal/http/http_test.go b/internal/http/http_test.go
index e99e6653..417db688 100644
--- a/internal/http/http_test.go
+++ b/internal/http/http_test.go
@@ -205,7 +205,12 @@ func TestUpload(t *testing.T) {
 		}
 		return errors.Errorf("unexpected http status code: %v", r.StatusCode)
 	}
-	ctx := context.New(config.Project{ProjectName: "blah"})
+	ctx := context.New(config.Project{
+		ProjectName: "blah",
+		Archives: []config.Archive{
+			{},
+		},
+	})
 	ctx.Env["TEST_A_SECRET"] = "x"
 	ctx.Env["TEST_A_USERNAME"] = "u2"
 	ctx.Version = "2.1.0"
diff --git a/internal/pipe/artifactory/artifactory_test.go b/internal/pipe/artifactory/artifactory_test.go
index e1b50108..6694370d 100644
--- a/internal/pipe/artifactory/artifactory_test.go
+++ b/internal/pipe/artifactory/artifactory_test.go
@@ -188,6 +188,9 @@ func TestRunPipe_ModeBinary(t *testing.T) {
 				Username: "productionuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION-US_SECRET": "deployuser-secret",
@@ -229,6 +232,9 @@ func TestRunPipe_ModeArchive(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
@@ -361,6 +367,9 @@ func TestRunPipe_TargetTemplateError(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
@@ -418,6 +427,9 @@ func TestRunPipe_BadCredentials(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
@@ -476,6 +488,9 @@ func TestRunPipe_UnparsableErrorResponse(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
@@ -531,6 +546,9 @@ func TestRunPipe_UnparsableResponse(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
@@ -559,6 +577,9 @@ func TestRunPipe_FileNotFound(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
@@ -597,6 +618,9 @@ func TestRunPipe_UnparsableTarget(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
@@ -623,6 +647,9 @@ func TestRunPipe_SkipWhenPublishFalse(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
@@ -654,6 +681,9 @@ func TestRunPipe_DirUpload(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
diff --git a/internal/pipe/build/build_test.go b/internal/pipe/build/build_test.go
index dd985bf7..165a66f6 100644
--- a/internal/pipe/build/build_test.go
+++ b/internal/pipe/build/build_test.go
@@ -231,6 +231,7 @@ func TestDefaultEmptyBuild(t *testing.T) {
 	var build = ctx.Config.Builds[0]
 	assert.Equal(t, ctx.Config.ProjectName, build.ID)
 	assert.Equal(t, ctx.Config.ProjectName, build.Binary)
+	assert.Equal(t, ".", build.Dir)
 	assert.Equal(t, ".", build.Main)
 	assert.Equal(t, []string{"linux", "darwin"}, build.Goos)
 	assert.Equal(t, []string{"amd64", "386"}, build.Goarch)
@@ -289,6 +290,7 @@ func TestDefaultPartialBuilds(t *testing.T) {
 				{
 					ID:      "build2",
 					Binary:  "foo",
+					Dir:     "baz",
 					Ldflags: []string{"-s -w"},
 					Goarch:  []string{"386"},
 				},
@@ -299,6 +301,7 @@ func TestDefaultPartialBuilds(t *testing.T) {
 	t.Run("build0", func(t *testing.T) {
 		var build = ctx.Config.Builds[0]
 		assert.Equal(t, "bar", build.Binary)
+		assert.Equal(t, ".", build.Dir)
 		assert.Equal(t, "./cmd/main.go", build.Main)
 		assert.Equal(t, []string{"linux"}, build.Goos)
 		assert.Equal(t, []string{"amd64", "386"}, build.Goarch)
@@ -310,6 +313,7 @@ func TestDefaultPartialBuilds(t *testing.T) {
 		var build = ctx.Config.Builds[1]
 		assert.Equal(t, "foo", build.Binary)
 		assert.Equal(t, ".", build.Main)
+		assert.Equal(t, "baz", build.Dir)
 		assert.Equal(t, []string{"linux", "darwin"}, build.Goos)
 		assert.Equal(t, []string{"386"}, build.Goarch)
 		assert.Equal(t, []string{"6"}, build.Goarm)
diff --git a/internal/pipe/defaults/defaults_test.go b/internal/pipe/defaults/defaults_test.go
index 269d713d..a3d96fc3 100644
--- a/internal/pipe/defaults/defaults_test.go
+++ b/internal/pipe/defaults/defaults_test.go
@@ -63,9 +63,11 @@ func TestFillPartial(t *testing.T) {
 					Name:  "test",
 				},
 			},
-			Archive: config.Archive{
-				Files: []string{
-					"glob/*",
+			Archives: []config.Archive{
+				{
+					Files: []string{
+						"glob/*",
+					},
 				},
 			},
 			Builds: []config.Build{
@@ -90,7 +92,7 @@ func TestFillPartial(t *testing.T) {
 		},
 	}
 	assert.NoError(t, Pipe{}.Run(ctx))
-	assert.Len(t, ctx.Config.Archive.Files, 1)
+	assert.Len(t, ctx.Config.Archives[0].Files, 1)
 	assert.Equal(t, `bin.install "testreleaser"`, ctx.Config.Brews[0].Install)
 	assert.NotEmpty(t, ctx.Config.Dockers[0].Binaries)
 	assert.NotEmpty(t, ctx.Config.Dockers[0].Goos)
diff --git a/internal/pipe/docker/docker_test.go b/internal/pipe/docker/docker_test.go
index 29621779..85869913 100644
--- a/internal/pipe/docker/docker_test.go
+++ b/internal/pipe/docker/docker_test.go
@@ -723,6 +723,7 @@ func TestDefault(t *testing.T) {
 	assert.Equal(t, "linux", docker.Goos)
 	assert.Equal(t, "amd64", docker.Goarch)
 	assert.Equal(t, []string{ctx.Config.Builds[0].Binary}, docker.Binaries)
+	assert.Empty(t, docker.Builds)
 }
 
 func TestDefaultBinaries(t *testing.T) {
diff --git a/internal/pipe/scoop/scoop_test.go b/internal/pipe/scoop/scoop_test.go
index ecba4d04..f6b31f6f 100644
--- a/internal/pipe/scoop/scoop_test.go
+++ b/internal/pipe/scoop/scoop_test.go
@@ -101,8 +101,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							GitHub: config.Repo{
@@ -145,8 +145,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							GitHub: config.Repo{
@@ -188,8 +188,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							GitLab: config.Repo{
@@ -248,8 +248,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							GitHub: config.Repo{
@@ -306,8 +306,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							GitHub: config.Repo{
@@ -349,8 +349,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							GitHub: config.Repo{
@@ -392,8 +392,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							GitHub: config.Repo{
@@ -427,8 +427,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							GitHub: config.Repo{
@@ -471,8 +471,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							Draft: true,
@@ -511,8 +511,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "tar.gz",
+						Archives: []config.Archive{
+							{Format: "tar.gz"},
 						},
 						Release: config.Release{
 							Disable: true,
@@ -551,8 +551,8 @@ func Test_doRun(t *testing.T) {
 						},
 						Dist:        ".",
 						ProjectName: "run-pipe",
-						Archive: config.Archive{
-							Format: "binary",
+						Archives: []config.Archive{
+							{Format: "binary"},
 						},
 						Release: config.Release{
 							Draft: true,
@@ -613,8 +613,8 @@ func Test_buildManifest(t *testing.T) {
 					},
 					Dist:        ".",
 					ProjectName: "run-pipe",
-					Archive: config.Archive{
-						Format: "tar.gz",
+					Archives: []config.Archive{
+						{Format: "tar.gz"},
 					},
 					Release: config.Release{
 						GitHub: config.Repo{
@@ -652,8 +652,8 @@ func Test_buildManifest(t *testing.T) {
 					},
 					Dist:        ".",
 					ProjectName: "run-pipe",
-					Archive: config.Archive{
-						Format: "tar.gz",
+					Archives: []config.Archive{
+						{Format: "tar.gz"},
 					},
 					Release: config.Release{
 						GitHub: config.Repo{
@@ -692,8 +692,8 @@ func Test_buildManifest(t *testing.T) {
 					},
 					Dist:        ".",
 					ProjectName: "run-pipe",
-					Archive: config.Archive{
-						Format: "tar.gz",
+					Archives: []config.Archive{
+						{Format: "tar.gz"},
 					},
 					Release: config.Release{
 						GitHub: config.Repo{
diff --git a/internal/pipe/upload/upload_test.go b/internal/pipe/upload/upload_test.go
index b7ce8708..1092e32c 100644
--- a/internal/pipe/upload/upload_test.go
+++ b/internal/pipe/upload/upload_test.go
@@ -123,6 +123,9 @@ func TestRunPipe_ModeBinary(t *testing.T) {
 				Username: "productionuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"UPLOAD_PRODUCTION-US_SECRET": "deployuser-secret",
@@ -164,6 +167,9 @@ func TestRunPipe_ModeArchive(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
@@ -261,6 +267,9 @@ func TestRunPipe_TargetTemplateError(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
@@ -313,6 +322,9 @@ func TestRunPipe_BadCredentials(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
@@ -343,6 +355,9 @@ func TestRunPipe_FileNotFound(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
@@ -381,6 +396,9 @@ func TestRunPipe_UnparsableTarget(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
@@ -406,6 +424,9 @@ func TestRunPipe_SkipWhenPublishFalse(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
@@ -437,6 +458,9 @@ func TestRunPipe_DirUpload(t *testing.T) {
 				Username: "deployuser",
 			},
 		},
+		Archives: []config.Archive{
+			{},
+		},
 	})
 	ctx.Env = map[string]string{
 		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",