diff --git a/internal/http/http.go b/internal/http/http.go index 48c7b6daf4a..7c76adb3dff 100644 --- a/internal/http/http.go +++ b/internal/http/http.go @@ -2,11 +2,9 @@ package http import ( - "bytes" "crypto/tls" "crypto/x509" "fmt" - "html/template" "io" h "net/http" "os" @@ -19,6 +17,7 @@ import ( "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/pipe" "github.com/goreleaser/goreleaser/internal/semerrgroup" + "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" ) @@ -351,51 +350,15 @@ func executeHTTPRequest(ctx *context.Context, upload *config.Upload, req *h.Requ return resp, err } -// targetData is used as a template struct for -// Artifactory.Target -type targetData struct { - Version string - Tag string - ProjectName string - ArtifactName string - - // Only supported in mode binary - Os string - Arch string - Arm string -} - // resolveTargetTemplate returns the resolved target template with replaced variables // Those variables can be replaced by the given context, goos, goarch, goarm and more -// TODO: replace this with our internal template pkg func resolveTargetTemplate(ctx *context.Context, upload *config.Upload, artifact *artifact.Artifact) (string, error) { - data := targetData{ - Version: ctx.Version, - Tag: ctx.Git.CurrentTag, - ProjectName: ctx.Config.ProjectName, - ArtifactName: artifact.Name, - } - + var replacements = map[string]string{} if upload.Mode == ModeBinary { // TODO: multiple archives here - 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 - t, err := template.New(ctx.Config.ProjectName).Parse(upload.Target) - if err != nil { - return "", err - } - err = t.Execute(&out, data) - return out.String(), err -} - -func replace(replacements map[string]string, original string) string { - result := replacements[original] - if result == "" { - return original + replacements = ctx.Config.Archives[0].Replacements } - return result + return tmpl.New(ctx). + WithArtifact(artifact, replacements). + Apply(upload.Target) } diff --git a/internal/http/http_test.go b/internal/http/http_test.go index 417db688856..c23c2290ecf 100644 --- a/internal/http/http_test.go +++ b/internal/http/http_test.go @@ -208,7 +208,11 @@ func TestUpload(t *testing.T) { ctx := context.New(config.Project{ ProjectName: "blah", Archives: []config.Archive{ - {}, + { + Replacements: map[string]string{ + "linux": "Linux", + }, + }, }, }) ctx.Env["TEST_A_SECRET"] = "x" @@ -232,9 +236,11 @@ func TestUpload(t *testing.T) { var file = filepath.Join(folder, "a."+a.ext) require.NoError(t, ioutil.WriteFile(file, []byte("lorem ipsum"), 0644)) ctx.Artifacts.Add(&artifact.Artifact{ - Name: "a." + a.ext, - Path: file, - Type: a.typ, + Name: "a." + a.ext, + Goos: "linux", + Goarch: "amd64", + Path: file, + Type: a.typ, Extra: map[string]interface{}{ "ID": "foo", }, @@ -307,6 +313,21 @@ func TestUpload(t *testing.T) { check{"/blah/2.1.0/a.tar", "u1", "x", content, map[string]string{}}, ), }, + {"archive_with_os_tmpl", true, true, false, false, + func(s *httptest.Server) (*context.Context, config.Upload) { + return ctx, config.Upload{ + Mode: ModeArchive, + Name: "a", + Target: s.URL + "/{{.ProjectName}}/{{.Version}}/{{.Os}}/{{.Arch}}", + Username: "u1", + TrustedCerts: cert(s), + } + }, + checks( + check{"/blah/2.1.0/linux/amd64/a.deb", "u1", "x", content, map[string]string{}}, + check{"/blah/2.1.0/linux/amd64/a.tar", "u1", "x", content, map[string]string{}}, + ), + }, {"archive_with_ids", true, true, false, false, func(s *httptest.Server) (*context.Context, config.Upload) { return ctx, config.Upload{ @@ -335,6 +356,18 @@ func TestUpload(t *testing.T) { }, checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content, map[string]string{}}), }, + {"binary_with_os_tmpl", 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}}/{{.Os}}/{{.Arch}}", + Username: "u2", + TrustedCerts: cert(s), + } + }, + checks(check{"/blah/2.1.0/Linux/amd64/a.ubi", "u2", "x", content, map[string]string{}}), + }, {"binary_with_ids", true, true, false, false, func(s *httptest.Server) (*context.Context, config.Upload) { return ctx, config.Upload{ diff --git a/internal/pipe/artifactory/artifactory_test.go b/internal/pipe/artifactory/artifactory_test.go index 0cf28347f3c..551ab41b3a7 100644 --- a/internal/pipe/artifactory/artifactory_test.go +++ b/internal/pipe/artifactory/artifactory_test.go @@ -383,7 +383,7 @@ func TestRunPipe_TargetTemplateError(t *testing.T) { }) assert.NoError(t, Pipe{}.Default(ctx)) - assert.EqualError(t, Pipe{}.Publish(ctx), `artifactory: error while building the target url: template: mybin:1: unexpected "/" in operand`) + assert.EqualError(t, Pipe{}.Publish(ctx), `artifactory: error while building the target url: template: tmpl:1: unexpected "/" in operand`) } func TestRunPipe_BadCredentials(t *testing.T) { diff --git a/internal/pipe/upload/upload_test.go b/internal/pipe/upload/upload_test.go index 865bda976e4..e2edf257e1e 100644 --- a/internal/pipe/upload/upload_test.go +++ b/internal/pipe/upload/upload_test.go @@ -422,7 +422,7 @@ func TestRunPipe_TargetTemplateError(t *testing.T) { }) err = Pipe{}.Publish(ctx) assert.Error(t, err) - assert.Contains(t, err.Error(), `upload: error while building the target url: template: mybin:1: unexpected "/" in operand`) + assert.Contains(t, err.Error(), `upload: error while building the target url: template: tmpl:1: unexpected "/" in operand`) } func TestRunPipe_BadCredentials(t *testing.T) { diff --git a/www/content/upload.md b/www/content/upload.md index 2ca24074ec5..0b43aaf9263 100644 --- a/www/content/upload.md +++ b/www/content/upload.md @@ -30,7 +30,7 @@ Prerequisites: ### Target -The `target` is the URL to upload the artifacts to (_without_ the name of the artifact). +The `target` is the template of the URL to upload the artifacts to (_without_ the name of the artifact). An example configuration for `goreleaser` in upload mode `binary` with the target can look like @@ -66,7 +66,7 @@ The configured name of your HTTP server will be used to build the environment variable name. This way we support auth for multiple instances. This also means that the `name` per configured instance needs to be unique -per goreleaser configuration. +per GoReleaser configuration. The name of the environment variable will be `UPLOAD_NAME_USERNAME`. If your instance is named `production`, you can store the username in the @@ -82,7 +82,7 @@ The password will be stored in a environment variable. The configured name of your HTTP server will be used. This way we support auth for multiple instances. This also means that the `name` per configured instance needs to be unique -per goreleaser configuration. +per GoReleaser configuration. The name of the environment variable will be `UPLOAD_NAME_SECRET`. If your instance is named `production`, you need to store the secret in the @@ -144,7 +144,7 @@ uploads: # Default is `archive`. mode: archive - # URL to be used as target of the HTTP request + # Template of the URL to be used as target of the HTTP request target: https://some.server/some/path/example-repo-local/{{ .ProjectName }}/{{ .Version }}/ # Custom artifact name (defaults to false) @@ -178,3 +178,5 @@ uploads: ``` These settings should allow you to push your artifacts into multiple HTTP servers. + +> Learn more about the [name template engine](/templates).