Repository: goreleaser/goreleaser
Pull request #2280 (2021-06-03 01:52:02)

feat: builds. no_unique_dist_dir
still not happy about the name, impl looks a bit nicer though.

closes  #2253

This might include a breaking change: before you could set a path on the binary name, e.g., `foo/mybin`, which would cause snap and nfpm packages to try to put the binary at `/usr/local/bin/foo/mybin`. EDIT: snap would actually fail, just nfpm would do this.

After this, the paths are correct.

---

it seems this is specially useful for terraform providers...

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/goreleaser/25NNN32XJAQWBWgP7UZG1aLuMvt1](https://vercel.com/goreleaser/goreleaser/25NNN32XJAQWBWgP7UZG1aLuMvt1)  
✅ Preview: [https://goreleaser-git-outputdir-goreleaser.vercel.app](https://goreleaser-git-outputdir-goreleaser.vercel.app)


codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2280?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) Report
> Merging [#2280](https://codecov.io/gh/goreleaser/goreleaser/pull/2280?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (bf520d3) into [master](https://codecov.io/gh/goreleaser/goreleaser/commit/752d8653ffc98efec25145e1b4b802b4feed4bf1?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (752d865) will **decrease** coverage by `0.04%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/goreleaser/goreleaser/pull/2280/graphs/tree.svg?width=650&height=150&src=pr&token=3bJvv6b7hN&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)](https://codecov.io/gh/goreleaser/goreleaser/pull/2280?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)

```diff
@@            Coverage Diff             @@
##           master    #2280      +/-   ##
==========================================
- Coverage   82.67%   82.62%   -0.05%     
==========================================
  Files          79       79              
  Lines        4277     4289      +12     
==========================================
+ Hits         3536     3544       +8     
- Misses        601      605       +4     
  Partials      140      140              
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/2280?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) | Coverage Δ | |
|---|---|---|
| [pkg/config/config.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2280/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-cGtnL2NvbmZpZy9jb25maWcuZ28=) | `93.75% <ø> (ø)` | |
| [internal/pipe/build/build.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2280/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9idWlsZC9idWlsZC5nbw==) | `98.41% <100.00%> (+0.06%)` | :arrow_up: |
| [internal/exec/exec\_mock.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2280/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvZXhlYy9leGVjX21vY2suZ28=) | `9.09% <0.00%> (-0.91%)` | :arrow_down: |
| [internal/exec/exec.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2280/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvZXhlYy9leGVjLmdv) | `85.71% <0.00%> (+0.52%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2280?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2280?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Last update [752d865...bf520d3](https://codecov.io/gh/goreleaser/goreleaser/pull/2280?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser).

tmeckel: @caarlos0 another good option to have the use case of a fully configurable output path implemented 👍🏼 But this does not solve your objection about having the ability to specify path templates at two locations. One here with the `dist_path` settings and then with the `binary` setting. 

My initial idea (before i changed my mind and went with the `output_path`) to get rid of the automatically created "target path" (`fmt.Sprintf("%s_%s", build.ID, target)`) was to introduce a setting `no_architecture_dir` which is a `boolean` and if set to true prevents the execution of the `fmt.Sprintf` function call.

![image](https://user-images.githubusercontent.com/14177833/120765407-786a0380-c519-11eb-8e28-8f162d0fa60d.png)

caarlos0: yeah, I thought it would make sense, but having a bool that disables the unique dir is nicer IMHO.

Just pushed, can you test this revision in your pipeline just to be sure it works as we expect?
caarlos0: also cc/ @radeksimko since it involves terraform providers...
tmeckel: @caarlos0 I can say this works as good as my PR #2253 So from my point of view I could retract (close) my PR.

https://github.com/goreleaser/goreleaser/pull/2253#issuecomment-853124533

![image](https://user-images.githubusercontent.com/14177833/120891446-07097e00-c609-11eb-812c-d16824a98c9c.png)

![image](https://user-images.githubusercontent.com/14177833/120891462-15579a00-c609-11eb-8785-bd2f0a6c5e84.png)

caarlos0: nice, I built from yours so your name will still be in the commit, basically changed names/docs a bit and also add that fix for nfpm/snap packages...

Thanks again for the pr/efforts testing et all, really appreciate 🙏
tmeckel: @caarlos0 

> 
> 
> nice, I built from yours so your name will still be in the commit, basically changed names/docs a bit and also add that fix for nfpm/snap packages...

Awesome 👍🏼 

> 
> Thanks again for the pr/efforts testing et all, really appreciate 🙏

I think it's an example how open source development should work: finding together the best solution. It was really nice to have worked with you on this 👍🏼 
caarlos0: > I think it's an example how open source development should work: finding together the best solution. It was really nice to have worked with you on this 👍🏼

thanks, likewise 🖤

Patch:
diff --git a/internal/pipe/build/build.go b/internal/pipe/build/build.go
index 5f6f8441..eb7d87aa 100644
--- a/internal/pipe/build/build.go
+++ b/internal/pipe/build/build.go
@@ -186,10 +186,14 @@ func buildOptionsForTarget(ctx *context.Context, build config.Build, target stri
 
 	build.Binary = binary
 	name := build.Binary + ext
+	dir := fmt.Sprintf("%s_%s", build.ID, target)
+	if build.NoUniqueDistDir {
+		dir = ""
+	}
 	path, err := filepath.Abs(
 		filepath.Join(
 			ctx.Config.Dist,
-			fmt.Sprintf("%s_%s", build.ID, target),
+			dir,
 			name,
 		),
 	)
@@ -198,7 +202,7 @@ func buildOptionsForTarget(ctx *context.Context, build config.Build, target stri
 	}
 
 	log.WithField("binary", path).Info("building")
-	buildOpts.Name = name
+	buildOpts.Name = filepath.Base(name)
 	buildOpts.Path = path
 	return &buildOpts, nil
 }
diff --git a/pkg/config/config.go b/pkg/config/config.go
index e25f1bb1..93181e01 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -183,27 +183,28 @@ func (a *FlagArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
 
 // Build contains the build configuration section.
 type Build struct {
-	ID           string         `yaml:",omitempty"`
-	Goos         []string       `yaml:",omitempty"`
-	Goarch       []string       `yaml:",omitempty"`
-	Goarm        []string       `yaml:",omitempty"`
-	Gomips       []string       `yaml:",omitempty"`
-	Targets      []string       `yaml:",omitempty"`
-	Ignore       []IgnoredBuild `yaml:",omitempty"`
-	Dir          string         `yaml:",omitempty"`
-	Main         string         `yaml:",omitempty"`
-	Ldflags      StringArray    `yaml:",omitempty"`
-	Tags         FlagArray      `yaml:",omitempty"`
-	Flags        FlagArray      `yaml:",omitempty"`
-	Binary       string         `yaml:",omitempty"`
-	Hooks        HookConfig     `yaml:",omitempty"`
-	Env          []string       `yaml:",omitempty"`
-	Lang         string         `yaml:",omitempty"`
-	Asmflags     StringArray    `yaml:",omitempty"`
-	Gcflags      StringArray    `yaml:",omitempty"`
-	ModTimestamp string         `yaml:"mod_timestamp,omitempty"`
-	Skip         bool           `yaml:",omitempty"`
-	GoBinary     string         `yaml:",omitempty"`
+	ID              string         `yaml:",omitempty"`
+	Goos            []string       `yaml:",omitempty"`
+	Goarch          []string       `yaml:",omitempty"`
+	Goarm           []string       `yaml:",omitempty"`
+	Gomips          []string       `yaml:",omitempty"`
+	Targets         []string       `yaml:",omitempty"`
+	Ignore          []IgnoredBuild `yaml:",omitempty"`
+	Dir             string         `yaml:",omitempty"`
+	Main            string         `yaml:",omitempty"`
+	Ldflags         StringArray    `yaml:",omitempty"`
+	Tags            FlagArray      `yaml:",omitempty"`
+	Flags           FlagArray      `yaml:",omitempty"`
+	Binary          string         `yaml:",omitempty"`
+	Hooks           HookConfig     `yaml:",omitempty"`
+	Env             []string       `yaml:",omitempty"`
+	Lang            string         `yaml:",omitempty"`
+	Asmflags        StringArray    `yaml:",omitempty"`
+	Gcflags         StringArray    `yaml:",omitempty"`
+	ModTimestamp    string         `yaml:"mod_timestamp,omitempty"`
+	Skip            bool           `yaml:",omitempty"`
+	GoBinary        string         `yaml:",omitempty"`
+	NoUniqueDistDir bool           `yaml:"no_unique_dist_dir,omitempty"`
 }
 
 type HookConfig struct {
diff --git a/www/docs/customization/build.md b/www/docs/customization/build.md
index 320efb73..e8b001f5 100644
--- a/www/docs/customization/build.md
+++ b/www/docs/customization/build.md
@@ -134,6 +134,15 @@ builds:
     # Useful for library projects.
     # Default is false
     skip: false
+
+    # By default, GoRelaser will create your binaries inside `dist/${BuildID}_${BuildTarget}`, which is an unique directory per build target in the matrix.
+    # You are able to set subdirs within that folder using the `binary` property.
+    #
+    # However, if for some reason you don't want that unique directory to be created, you can set this property.
+    # If you do, you are responsible of keeping different builds from overriding each other.
+    #
+    # Defaults to `false`.
+    no_unique_dist_dir: true
 ```
 
 !!! tip


Test patch:
diff --git a/internal/pipe/build/build_test.go b/internal/pipe/build/build_test.go
index b27f7f59..d2eb7e89 100644
--- a/internal/pipe/build/build_test.go
+++ b/internal/pipe/build/build_test.go
@@ -140,6 +140,7 @@ func TestRunFullPipe(t *testing.T) {
 	}
 	ctx := context.New(config)
 	ctx.Git.CurrentTag = "2.4.5"
+	require.NoError(t, Pipe{}.Default(ctx))
 	require.NoError(t, Pipe{}.Run(ctx))
 	require.Equal(t, ctx.Artifacts.List(), []*artifact.Artifact{{
 		Name: "testing",
@@ -712,6 +713,7 @@ func TestBuildOptionsForTarget(t *testing.T) {
 		name         string
 		build        config.Build
 		expectedOpts *api.Options
+		expectedErr  string
 	}{
 		{
 			name: "simple options for target",
@@ -747,6 +749,24 @@ func TestBuildOptionsForTarget(t *testing.T) {
 				Arch:   "amd64",
 			},
 		},
+		{
+			name: "overriding dist path",
+			build: config.Build{
+				ID:     "testid",
+				Binary: "distpath/{{.Os}}/{{.Arch}}/testbinary_{{.Os}}_{{.Arch}}",
+				Targets: []string{
+					"linux_amd64",
+				},
+				NoUniqueDistDir: true,
+			},
+			expectedOpts: &api.Options{
+				Name:   "testbinary_linux_amd64",
+				Path:   filepath.Join(tmpDir, "distpath", "linux", "amd64", "testbinary_linux_amd64"),
+				Target: "linux_amd64",
+				Os:     "linux",
+				Arch:   "amd64",
+			},
+		},
 	}
 
 	for _, tc := range testCases {
@@ -755,9 +775,14 @@ func TestBuildOptionsForTarget(t *testing.T) {
 				Dist:   tmpDir,
 				Builds: []config.Build{tc.build},
 			})
-			opts, err := buildOptionsForTarget(ctx, tc.build, tc.build.Targets[0])
-			require.NoError(t, err)
-			require.Equal(t, tc.expectedOpts, opts)
+			require.NoError(t, Pipe{}.Default(ctx))
+			opts, err := buildOptionsForTarget(ctx, ctx.Config.Builds[0], ctx.Config.Builds[0].Targets[0])
+			if tc.expectedErr == "" {
+				require.NoError(t, err)
+				require.Equal(t, tc.expectedOpts, opts)
+			} else {
+				require.EqualError(t, err, tc.expectedErr)
+			}
 		})
 	}
 }