Repository: goreleaser/goreleaser
Pull request #2002 (2021-01-07 12:36:23)

feat(http): support custom headers in http upload
<!--

Hi, thanks for contributing!

Please make sure you read our CONTRIBUTING guide.

Please fill the fields above:

-->

<!-- If applied, this commit will... -->

This commit will add an additional field to elements under `uploads:`, allowing the user to specify custom headers for the `PUT` (or other mode) request.

<!-- Why is this change being made? -->

GitLab has a Generic Packages Repository which allows uploads using various auth methods, one being the CI job token. However, it must be specified not as basic authentication but rather as a custom header, namely `JOB-TOKEN: $CI_JOB_TOKEN`. This change supports specifying that header as well as any other headers required by various upload destinations.

<!-- # Provide links to any relevant tickets, URLs or other resources -->

See also: https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#publish-a-generic-package-by-using-cicd

vercel[bot]: @sirlatrom 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%2239b622ef5d8a3a9d4bcd13fee83aef5afb2130fd%22%7D%2C%22id%22%3A%22QmXXFxpzRXFGNEqUzJVUu2e8TtHfm2iV4EAxvKauQt8uvh%22%2C%22org%22%3A%22goreleaser%22%2C%22prId%22%3A2002%2C%22repo%22%3A%22goreleaser%22%7D).


codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2002?src=pr&el=h1) Report
> Merging [#2002](https://codecov.io/gh/goreleaser/goreleaser/pull/2002?src=pr&el=desc) (39b622e) into [master](https://codecov.io/gh/goreleaser/goreleaser/commit/873f35a2c2d01242bb22cb72fe5dbf39ad2cbe3c?el=desc) (873f35a) will **decrease** coverage by `0.04%`.
> The diff coverage is `0.00%`.

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

```diff
@@            Coverage Diff             @@
##           master    #2002      +/-   ##
==========================================
- Coverage   82.83%   82.79%   -0.05%     
==========================================
  Files          74       74              
  Lines        3980     3982       +2     
==========================================
  Hits         3297     3297              
- Misses        556      557       +1     
- Partials      127      128       +1     
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/2002?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/http/http.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2002/diff?src=pr&el=tree#diff-aW50ZXJuYWwvaHR0cC9odHRwLmdv) | `88.06% <0.00%> (-1.02%)` | :arrow_down: |
| [pkg/config/config.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2002/diff?src=pr&el=tree#diff-cGtnL2NvbmZpZy9jb25maWcuZ28=) | `93.75% <ø> (ø)` | |

------

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

sirlatrom: > 
> 
> LGTM, can you add a test case as well?

Yes, I've added it now, along with an example in the docs: https://github.com/goreleaser/goreleaser/pull/2002/files#diff-8be32e869556dd405c589f4df19e68aef2c4cab8ab8a1867224460cc4bd6cc2eR472-R486
sirlatrom: I failed to check the new test locally, turns out I can't write valid Go code 😅 Fixed now.

Patch:
diff --git a/internal/http/http.go b/internal/http/http.go
index af627255..15e7b949 100644
--- a/internal/http/http.go
+++ b/internal/http/http.go
@@ -240,6 +240,11 @@ func uploadAsset(ctx *context.Context, upload *config.Upload, artifact *artifact
 	log.Debugf("generated target url: %s", targetURL)
 
 	var headers = map[string]string{}
+	if upload.CustomHeaders != nil {
+		for name, value := range upload.CustomHeaders {
+			headers[name] = value
+		}
+	}
 	if upload.ChecksumHeader != "" {
 		sum, err := artifact.Checksum("sha256")
 		if err != nil {
diff --git a/pkg/config/config.go b/pkg/config/config.go
index f9cd30cf..438d9bd3 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -543,17 +543,18 @@ type Blob struct {
 
 // Upload configuration.
 type Upload struct {
-	Name               string   `yaml:",omitempty"`
-	IDs                []string `yaml:"ids,omitempty"`
-	Target             string   `yaml:",omitempty"`
-	Username           string   `yaml:",omitempty"`
-	Mode               string   `yaml:",omitempty"`
-	Method             string   `yaml:",omitempty"`
-	ChecksumHeader     string   `yaml:"checksum_header,omitempty"`
-	TrustedCerts       string   `yaml:"trusted_certificates,omitempty"`
-	Checksum           bool     `yaml:",omitempty"`
-	Signature          bool     `yaml:",omitempty"`
-	CustomArtifactName bool     `yaml:"custom_artifact_name,omitempty"`
+	Name               string            `yaml:",omitempty"`
+	IDs                []string          `yaml:"ids,omitempty"`
+	Target             string            `yaml:",omitempty"`
+	Username           string            `yaml:",omitempty"`
+	Mode               string            `yaml:",omitempty"`
+	Method             string            `yaml:",omitempty"`
+	ChecksumHeader     string            `yaml:"checksum_header,omitempty"`
+	TrustedCerts       string            `yaml:"trusted_certificates,omitempty"`
+	Checksum           bool              `yaml:",omitempty"`
+	Signature          bool              `yaml:",omitempty"`
+	CustomArtifactName bool              `yaml:"custom_artifact_name,omitempty"`
+	CustomHeaders      map[string]string `yaml:"custom_headers,omitempty"`
 }
 
 // Publisher configuration.
diff --git a/www/docs/customization/upload.md b/www/docs/customization/upload.md
index 7fddba76..46cc0557 100644
--- a/www/docs/customization/upload.md
+++ b/www/docs/customization/upload.md
@@ -161,6 +161,11 @@ uploads:
     # Default is empty.
     checksum_header: -X-SHA256-Sum
 
+    # A map of custom headers e.g. to support required content types or auth schemes.
+    # Default is empty.
+    custom_headers:
+      JOB-TOKEN: {{ .Env.CI_JOB_TOKEN }}
+
     # Upload checksums (defaults to false)
     checksum: true
 


Test patch:
diff --git a/internal/http/http_test.go b/internal/http/http_test.go
index b0a75a1c..87a3b20a 100644
--- a/internal/http/http_test.go
+++ b/internal/http/http_test.go
@@ -469,6 +469,21 @@ func TestUpload(t *testing.T) {
 			},
 			checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content, map[string]string{"-x-sha256": "5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269"}}),
 		},
+		{"custom-headers", true, true, false, false,
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
+					Mode:     ModeBinary,
+					Name:     "a",
+					Target:   s.URL + "/{{.ProjectName}}/{{.Version}}/",
+					Username: "u2",
+					CustomHeaders: map[string]string{
+						"x-custom-header-name": "custom-header-value",
+					},
+					TrustedCerts: cert(s),
+				}
+			},
+			checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content, map[string]string{"x-custom-header-name": "custom-header-value"}}),
+		},
 	}
 
 	uploadAndCheck := func(t *testing.T, setup func(*httptest.Server) (*context.Context, config.Upload), wantErrPlain, wantErrTLS bool, check func(r []*h.Request) error, srv *httptest.Server) {