Repository: goreleaser/goreleaser
Pull request #1936 (2020-12-07 16:53:51)

feat: add Os and Arch template options to the Binary name field
If applied, this PR will add the capability to use `{{ .Os }}` and `{{.Arch}}` in the binary name. 

I'm implementing this change because I was expecting these to work at this point of the build process. I'm using this specific configuration for the binary name, to make it easier for [Packer](https://github.com/hashicorp/packer) when managing plugin's binaries for different os/arch without needing to add the information to the binary name itself afterward.

Here a failing release example for a repository I'm using for testing:
https://github.com/sylviamoss/packer-provisioner-comment/runs/1511229354?check_suite_focus=true#step:6:58

I tested the changes locally and here's the output line with the binary name: 
```
   • building binaries
      • building                  binary=/Users/sylviamoss/Packer/plugins-example/packer-provisioner-comment/dist/packer-provisioner-comment_freebsd_amd64/packer-provisioner-comment_v0.2.2-SNAPSHOT-a1d03f2_freebsd_amd64
```


vercel[bot]: @sylviamoss is attempting to deploy a commit to the **Goreleaser** Team on [Vercel](https://vercel.com).

A member of the Team first needs to [authorize it](https://vercel.com/git/authorize?team=Goreleaser&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%22be861f9fa2cc67dce2031a6035405d4c6d11bcdc%22%7D%2C%22id%22%3A%22Qmaiqo7YxXBYjhZdMU1vKZ51AoBxZXRKxiCSAds1F9ASAS%22%2C%22org%22%3A%22goreleaser%22%2C%22prId%22%3A1936%2C%22repo%22%3A%22goreleaser%22%7D).


caarlos0: I think you would want to do that by using `archive.format: binary` and setting the templates there... does that make sense?
sylviamoss: I'm creating zip archives. The way our tool works is by keeping the binaries in a folder to use them at the runtime. One can have binaries for different os/arch for the same plugin and that's why we differentiate them by their names. We are parsing the binary name and adding these information to it, it would be nice if they were created already with them. 
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/70luaba53](https://vercel.com/goreleaser/goreleaser/70luaba53)  
✅ Preview: *In Progress*


codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/1936?src=pr&el=h1) Report
> Merging [#1936](https://codecov.io/gh/goreleaser/goreleaser/pull/1936?src=pr&el=desc) (3205829) into [master](https://codecov.io/gh/goreleaser/goreleaser/commit/f934314be3ff62225e33c737112fb6e67f7c28ee?el=desc) (f934314) will **not change** coverage.
> The diff coverage is `100.00%`.

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

```diff
@@           Coverage Diff           @@
##           master    #1936   +/-   ##
=======================================
  Coverage   82.84%   82.84%           
=======================================
  Files          74       74           
  Lines        3982     3982           
=======================================
  Hits         3299     3299           
  Misses        556      556           
  Partials      127      127           
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/1936?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/pipe/build/build.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1936/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGlwZS9idWlsZC9idWlsZC5nbw==) | `98.34% <100.00%> (ø)` | |

------

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

kodiakhq[bot]: 
This PR currently has a merge conflict. Please resolve this and then re-add the `automerge` label.

sylviamoss: @caarlos0 I updated the documentation but rebased with master and messed everything up. 🤦🏼 
caarlos0: LGTM, thanks!

Patch:
diff --git a/internal/pipe/build/build.go b/internal/pipe/build/build.go
index 775734af..232cd799 100644
--- a/internal/pipe/build/build.go
+++ b/internal/pipe/build/build.go
@@ -164,8 +164,22 @@ func doBuild(ctx *context.Context, build config.Build, opts builders.Options) er
 
 func buildOptionsForTarget(ctx *context.Context, build config.Build, target string) (*builders.Options, error) {
 	var ext = extFor(target, build.Flags)
+	var goos string
+	var goarch string
+
+	if strings.Contains(target, "_") {
+		goos = strings.Split(target, "_")[0]
+		goarch = strings.Split(target, "_")[1]
+	}
+
+	buildOpts := builders.Options{
+		Target: target,
+		Ext:    ext,
+		Os:     goos,
+		Arch:   goarch,
+	}
 
-	binary, err := tmpl.New(ctx).Apply(build.Binary)
+	binary, err := tmpl.New(ctx).WithBuildOptions(buildOpts).Apply(build.Binary)
 	if err != nil {
 		return nil, err
 	}
@@ -183,23 +197,10 @@ func buildOptionsForTarget(ctx *context.Context, build config.Build, target stri
 		return nil, err
 	}
 
-	var goos string
-	var goarch string
-
-	if strings.Contains(target, "_") {
-		goos = strings.Split(target, "_")[0]
-		goarch = strings.Split(target, "_")[1]
-	}
-
 	log.WithField("binary", path).Info("building")
-	return &builders.Options{
-		Target: target,
-		Name:   name,
-		Path:   path,
-		Ext:    ext,
-		Os:     goos,
-		Arch:   goarch,
-	}, nil
+	buildOpts.Name = name
+	buildOpts.Path = path
+	return &buildOpts, nil
 }
 
 func extFor(target string, flags config.FlagArray) string {
diff --git a/www/docs/customization/build.md b/www/docs/customization/build.md
index 64061f09..9757da97 100644
--- a/www/docs/customization/build.md
+++ b/www/docs/customization/build.md
@@ -159,6 +159,16 @@ builds:
       - windows
 ```
 
+The binary name field supports [templating](/customization/templates/). The following build details are exposed:
+
+| Key     | Description                      |
+|---------|----------------------------------|
+| .Os     | `GOOS`                           |
+| .Arch   | `GOARCH`                         |
+| .Arm    | `GOARM`                          |
+| .Ext    | Extension, e.g. `.exe`           |
+| .Target | Build target, e.g. `darwin_amd64`|
+
 ## Passing environment variables to ldflags
 
 You can do that by using `{{ .Env.VARIABLE_NAME }}` in the template, for


Test patch:
diff --git a/internal/pipe/build/build_test.go b/internal/pipe/build/build_test.go
index 35e4d6b2..b268bfe7 100644
--- a/internal/pipe/build/build_test.go
+++ b/internal/pipe/build/build_test.go
@@ -706,28 +706,58 @@ func TestPipeOnBuild_invalidBinaryTpl(t *testing.T) {
 func TestBuildOptionsForTarget(t *testing.T) {
 	var tmpDir = testlib.Mktmp(t)
 
-	build := config.Build{
-		ID:     "testid",
-		Binary: "testbinary",
-		Targets: []string{
-			"linux_amd64",
-			"darwin_amd64",
-			"windows_amd64",
+	testCases := []struct {
+		name  string
+		build config.Build
+		expectedOpts *api.Options
+	}{
+		{
+			name: "simple options for target",
+			build: config.Build{
+				ID:     "testid",
+				Binary: "testbinary",
+				Targets: []string{
+					"linux_amd64",
+				},
+			},
+			expectedOpts: &api.Options{
+				Name:   "testbinary",
+				Path:   filepath.Join(tmpDir, "testid_linux_amd64", "testbinary"),
+				Target: "linux_amd64",
+				Os:     "linux",
+				Arch:   "amd64",
+			},
+		},
+		{
+			name: "binary name with Os and Arch template variables",
+			build: config.Build{
+				ID:     "testid",
+				Binary: "testbinary_{{.Os}}_{{.Arch}}",
+				Targets: []string{
+					"linux_amd64",
+				},
+			},
+			expectedOpts: &api.Options{
+				Name:   "testbinary_linux_amd64",
+				Path:   filepath.Join(tmpDir, "testid_linux_amd64", "testbinary_linux_amd64"),
+				Target: "linux_amd64",
+				Os:     "linux",
+				Arch:   "amd64",
+			},
 		},
 	}
-	ctx := context.New(config.Project{
-		Dist:   tmpDir,
-		Builds: []config.Build{build},
-	})
-	opts, err := buildOptionsForTarget(ctx, build, "linux_amd64")
-	require.NoError(t, err)
-	require.Equal(t, &api.Options{
-		Name:   "testbinary",
-		Path:   filepath.Join(tmpDir, "testid_linux_amd64", "testbinary"),
-		Target: "linux_amd64",
-		Os:     "linux",
-		Arch:   "amd64",
-	}, opts)
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.New(config.Project{
+				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)
+		})
+	}
 }
 
 func TestHookComplex(t *testing.T) {