Repository: goreleaser/goreleaser
Pull request #1246 (2019-11-15 19:20:58)

feat: http POST 
This would allow to send HTTP POSTs as well instead of only PUTs.

refs https://github.com/goreleaser/goreleaser/pull/1204

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

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

```diff
@@            Coverage Diff             @@
##           master    #1246      +/-   ##
==========================================
- Coverage   83.76%   83.66%   -0.11%     
==========================================
  Files          58       58              
  Lines        3320     3342      +22     
==========================================
+ Hits         2781     2796      +15     
- Misses        459      463       +4     
- Partials       80       83       +3
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/1246?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pkg/config/config.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1246/diff?src=pr&el=tree#diff-cGtnL2NvbmZpZy9jb25maWcuZ28=) | `100% <ø> (ø)` | :arrow_up: |
| [internal/pipe/publish/publish.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1246/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGlwZS9wdWJsaXNoL3B1Ymxpc2guZ28=) | `84.61% <ø> (ø)` | :arrow_up: |
| [internal/pipe/artifactory/artifactory.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1246/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGlwZS9hcnRpZmFjdG9yeS9hcnRpZmFjdG9yeS5nbw==) | `100% <100%> (ø)` | :arrow_up: |
| [internal/pipe/upload/upload.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1246/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGlwZS91cGxvYWQvdXBsb2FkLmdv) | `83.33% <70%> (ø)` | |
| [internal/http/http.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1246/diff?src=pr&el=tree#diff-aW50ZXJuYWwvaHR0cC9odHRwLmdv) | `90.24% <94.36%> (-1.2%)` | :arrow_down: |

------

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


Patch:
diff --git a/go.sum b/go.sum
index cd6b5451..728fb666 100644
--- a/go.sum
+++ b/go.sum
@@ -168,6 +168,7 @@ github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUr
 github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
 github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
diff --git a/internal/http/http.go b/internal/http/http.go
index 9b75c0cb..63cad0a0 100644
--- a/internal/http/http.go
+++ b/internal/http/http.go
@@ -68,47 +68,74 @@ func assetOpenDefault(kind string, a *artifact.Artifact) (*asset, error) {
 	}, nil
 }
 
-// Defaults sets default configuration options on Put structs
-func Defaults(puts []config.Put) error {
-	for i := range puts {
-		defaults(&puts[i])
+// Defaults sets default configuration options on upload structs
+func Defaults(uploads []config.Upload) error {
+	for i := range uploads {
+		defaults(&uploads[i])
 	}
 	return nil
 }
 
-func defaults(put *config.Put) {
-	if put.Mode == "" {
-		put.Mode = ModeArchive
+func defaults(upload *config.Upload) {
+	if upload.Mode == "" {
+		upload.Mode = ModeArchive
+	}
+	if upload.Method == "" {
+		upload.Method = h.MethodPut
 	}
 }
 
-// CheckConfig validates a Put configuration returning a descriptive error when appropriate
-func CheckConfig(ctx *context.Context, put *config.Put, kind string) error {
-	if put.Target == "" {
-		return misconfigured(kind, put, "missing target")
+// CheckConfig validates an upload configuration returning a descriptive error when appropriate
+func CheckConfig(ctx *context.Context, upload *config.Upload, kind string) error {
+	if upload.Target == "" {
+		return misconfigured(kind, upload, "missing target")
 	}
 
-	if put.Name == "" {
-		return misconfigured(kind, put, "missing name")
+	if upload.Name == "" {
+		return misconfigured(kind, upload, "missing name")
 	}
 
-	if put.Mode != ModeArchive && put.Mode != ModeBinary {
-		return misconfigured(kind, put, "mode must be 'binary' or 'archive'")
+	if upload.Mode != ModeArchive && upload.Mode != ModeBinary {
+		return misconfigured(kind, upload, "mode must be 'binary' or 'archive'")
 	}
 
-	envName := fmt.Sprintf("%s_%s_SECRET", strings.ToUpper(kind), strings.ToUpper(put.Name))
-	if _, ok := ctx.Env[envName]; !ok {
-		return misconfigured(kind, put, fmt.Sprintf("missing %s environment variable", envName))
+	if _, err := getUsername(ctx, upload, kind); err != nil {
+		return err
+	}
+
+	if _, err := getPassword(ctx, upload, kind); err != nil {
+		return err
 	}
 
-	if put.TrustedCerts != "" && !x509.NewCertPool().AppendCertsFromPEM([]byte(put.TrustedCerts)) {
-		return misconfigured(kind, put, "no certificate could be added from the specified trusted_certificates configuration")
+	if upload.TrustedCerts != "" && !x509.NewCertPool().AppendCertsFromPEM([]byte(upload.TrustedCerts)) {
+		return misconfigured(kind, upload, "no certificate could be added from the specified trusted_certificates configuration")
 	}
 
 	return nil
 }
 
-func misconfigured(kind string, upload *config.Put, reason string) error {
+func getUsername(ctx *context.Context, upload *config.Upload, kind string) (string, error) {
+	if upload.Username != "" {
+		return upload.Username, nil
+	}
+	var key = fmt.Sprintf("%s_%s_USERNAME", strings.ToUpper(kind), strings.ToUpper(upload.Name))
+	user, ok := ctx.Env[key]
+	if !ok {
+		return "", misconfigured(kind, upload, fmt.Sprintf("missing username or %s environment variable", key))
+	}
+	return user, nil
+}
+
+func getPassword(ctx *context.Context, upload *config.Upload, kind string) (string, error) {
+	var key = fmt.Sprintf("%s_%s_SECRET", strings.ToUpper(kind), strings.ToUpper(upload.Name))
+	pwd, ok := ctx.Env[key]
+	if !ok {
+		return "", misconfigured(kind, upload, fmt.Sprintf("missing %s environment variable", key))
+	}
+	return pwd, nil
+}
+
+func misconfigured(kind string, upload *config.Upload, reason string) error {
 	return pipe.Skip(fmt.Sprintf("%s section '%s' is not configured properly (%s)", kind, upload.Name, reason))
 }
 
@@ -117,25 +144,25 @@ func misconfigured(kind string, upload *config.Put, reason string) error {
 type ResponseChecker func(*h.Response) error
 
 // Upload does the actual uploading work
-func Upload(ctx *context.Context, puts []config.Put, kind string, check ResponseChecker) error {
+func Upload(ctx *context.Context, uploads []config.Upload, kind string, check ResponseChecker) error {
 	if ctx.SkipPublish {
 		return pipe.ErrSkipPublishEnabled
 	}
 
-	// Handle every configured put
-	for _, put := range puts {
-		put := put
+	// Handle every configured upload
+	for _, upload := range uploads {
+		upload := upload
 		filters := []artifact.Filter{}
-		if put.Checksum {
+		if upload.Checksum {
 			filters = append(filters, artifact.ByType(artifact.Checksum))
 		}
-		if put.Signature {
+		if upload.Signature {
 			filters = append(filters, artifact.ByType(artifact.Signature))
 		}
 		// We support two different modes
 		//	- "archive": Upload all artifacts
 		//	- "binary": Upload only the raw binaries
-		switch v := strings.ToLower(put.Mode); v {
+		switch v := strings.ToLower(upload.Mode); v {
 		case ModeArchive:
 			filters = append(filters,
 				artifact.ByType(artifact.UploadableArchive),
@@ -146,17 +173,17 @@ func Upload(ctx *context.Context, puts []config.Put, kind string, check Response
 		default:
 			err := fmt.Errorf("%s: mode \"%s\" not supported", kind, v)
 			log.WithFields(log.Fields{
-				kind:   put.Name,
+				kind:   upload.Name,
 				"mode": v,
 			}).Error(err.Error())
 			return err
 		}
 
 		var filter = artifact.Or(filters...)
-		if len(put.IDs) > 0 {
-			filter = artifact.And(filter, artifact.ByIDs(put.IDs...))
+		if len(upload.IDs) > 0 {
+			filter = artifact.And(filter, artifact.ByIDs(upload.IDs...))
 		}
-		if err := uploadWithFilter(ctx, &put, filter, kind, check); err != nil {
+		if err := uploadWithFilter(ctx, &upload, filter, kind, check); err != nil {
 			return err
 		}
 	}
@@ -164,34 +191,36 @@ func Upload(ctx *context.Context, puts []config.Put, kind string, check Response
 	return nil
 }
 
-func uploadWithFilter(ctx *context.Context, put *config.Put, filter artifact.Filter, kind string, check ResponseChecker) error {
+func uploadWithFilter(ctx *context.Context, upload *config.Upload, filter artifact.Filter, kind string, check ResponseChecker) error {
 	var artifacts = ctx.Artifacts.Filter(filter).List()
 	log.Debugf("will upload %d artifacts", len(artifacts))
 	var g = semerrgroup.New(ctx.Parallelism)
 	for _, artifact := range artifacts {
 		artifact := artifact
 		g.Go(func() error {
-			return uploadAsset(ctx, put, artifact, kind, check)
+			return uploadAsset(ctx, upload, artifact, kind, check)
 		})
 	}
 	return g.Wait()
 }
 
 // uploadAsset uploads file to target and logs all actions
-func uploadAsset(ctx *context.Context, put *config.Put, artifact *artifact.Artifact, kind string, check ResponseChecker) error {
-	envBase := fmt.Sprintf("%s_%s_", strings.ToUpper(kind), strings.ToUpper(put.Name))
-	username := put.Username
-	if username == "" {
-		// username not configured: using env
-		username = ctx.Env[envBase+"USERNAME"]
+func uploadAsset(ctx *context.Context, upload *config.Upload, artifact *artifact.Artifact, kind string, check ResponseChecker) error {
+	username, err := getUsername(ctx, upload, kind)
+	if err != nil {
+		return err
+	}
+
+	secret, err := getPassword(ctx, upload, kind)
+	if err != nil {
+		return err
 	}
-	secret := ctx.Env[envBase+"SECRET"]
 
 	// Generate the target url
-	targetURL, err := resolveTargetTemplate(ctx, put, artifact)
+	targetURL, err := resolveTargetTemplate(ctx, upload, artifact)
 	if err != nil {
 		msg := fmt.Sprintf("%s: error while building the target url", kind)
-		log.WithField("instance", put.Name).WithError(err).Error(msg)
+		log.WithField("instance", upload.Name).WithError(err).Error(msg)
 		return errors.Wrap(err, msg)
 	}
 
@@ -209,19 +238,19 @@ func uploadAsset(ctx *context.Context, put *config.Put, artifact *artifact.Artif
 	targetURL += artifact.Name
 
 	var headers = map[string]string{}
-	if put.ChecksumHeader != "" {
+	if upload.ChecksumHeader != "" {
 		sum, err := artifact.Checksum("sha256")
 		if err != nil {
 			return err
 		}
-		headers[put.ChecksumHeader] = sum
+		headers[upload.ChecksumHeader] = sum
 	}
 
-	res, err := uploadAssetToServer(ctx, put, targetURL, username, secret, headers, asset, check)
+	res, err := uploadAssetToServer(ctx, upload, targetURL, username, secret, headers, asset, check)
 	if err != nil {
 		msg := fmt.Sprintf("%s: upload failed", kind)
 		log.WithError(err).WithFields(log.Fields{
-			"instance": put.Name,
+			"instance": upload.Name,
 			"username": username,
 		}).Error(msg)
 		return errors.Wrap(err, msg)
@@ -231,26 +260,26 @@ func uploadAsset(ctx *context.Context, put *config.Put, artifact *artifact.Artif
 	}
 
 	log.WithFields(log.Fields{
-		"instance": put.Name,
-		"mode":     put.Mode,
+		"instance": upload.Name,
+		"mode":     upload.Mode,
 	}).Info("uploaded successful")
 
 	return nil
 }
 
 // uploadAssetToServer uploads the asset file to target
-func uploadAssetToServer(ctx *context.Context, put *config.Put, target, username, secret string, headers map[string]string, a *asset, check ResponseChecker) (*h.Response, error) {
-	req, err := newUploadRequest(target, username, secret, headers, a)
+func uploadAssetToServer(ctx *context.Context, upload *config.Upload, target, username, secret string, headers map[string]string, a *asset, check ResponseChecker) (*h.Response, error) {
+	req, err := newUploadRequest(upload.Method, target, username, secret, headers, a)
 	if err != nil {
 		return nil, err
 	}
 
-	return executeHTTPRequest(ctx, put, req, check)
+	return executeHTTPRequest(ctx, upload, req, check)
 }
 
 // newUploadRequest creates a new h.Request for uploading
-func newUploadRequest(target, username, secret string, headers map[string]string, a *asset) (*h.Request, error) {
-	req, err := h.NewRequest(h.MethodPut, target, a.ReadCloser)
+func newUploadRequest(method, target, username, secret string, headers map[string]string, a *asset) (*h.Request, error) {
+	req, err := h.NewRequest(method, target, a.ReadCloser)
 	if err != nil {
 		return nil, err
 	}
@@ -264,8 +293,8 @@ func newUploadRequest(target, username, secret string, headers map[string]string
 	return req, err
 }
 
-func getHTTPClient(put *config.Put) (*h.Client, error) {
-	if put.TrustedCerts == "" {
+func getHTTPClient(upload *config.Upload) (*h.Client, error) {
+	if upload.TrustedCerts == "" {
 		return h.DefaultClient, nil
 	}
 	pool, err := x509.SystemCertPool()
@@ -277,7 +306,7 @@ func getHTTPClient(put *config.Put) (*h.Client, error) {
 			return nil, err
 		}
 	}
-	pool.AppendCertsFromPEM([]byte(put.TrustedCerts)) // already validated certs checked by CheckConfig
+	pool.AppendCertsFromPEM([]byte(upload.TrustedCerts)) // already validated certs checked by CheckConfig
 	return &h.Client{
 		Transport: &h.Transport{
 			TLSClientConfig: &tls.Config{
@@ -288,8 +317,8 @@ func getHTTPClient(put *config.Put) (*h.Client, error) {
 }
 
 // executeHTTPRequest processes the http call with respect of context ctx
-func executeHTTPRequest(ctx *context.Context, put *config.Put, req *h.Request, check ResponseChecker) (*h.Response, error) {
-	client, err := getHTTPClient(put)
+func executeHTTPRequest(ctx *context.Context, upload *config.Upload, req *h.Request, check ResponseChecker) (*h.Response, error) {
+	client, err := getHTTPClient(upload)
 	if err != nil {
 		return nil, err
 	}
@@ -334,14 +363,14 @@ type targetData struct {
 // 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, put *config.Put, artifact *artifact.Artifact) (string, error) {
+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,
 	}
 
-	if put.Mode == ModeBinary {
+	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)
@@ -349,7 +378,7 @@ func resolveTargetTemplate(ctx *context.Context, put *config.Put, artifact *arti
 	}
 
 	var out bytes.Buffer
-	t, err := template.New(ctx.Config.ProjectName).Parse(put.Target)
+	t, err := template.New(ctx.Config.ProjectName).Parse(upload.Target)
 	if err != nil {
 		return "", err
 	}
diff --git a/internal/pipe/artifactory/artifactory.go b/internal/pipe/artifactory/artifactory.go
index 622add4d..a50e0844 100644
--- a/internal/pipe/artifactory/artifactory.go
+++ b/internal/pipe/artifactory/artifactory.go
@@ -47,6 +47,7 @@ func (Pipe) String() string {
 func (Pipe) Default(ctx *context.Context) error {
 	for i := range ctx.Config.Artifactories {
 		ctx.Config.Artifactories[i].ChecksumHeader = "X-Checksum-SHA256"
+		ctx.Config.Artifactories[i].Method = h.MethodPut
 	}
 	return http.Defaults(ctx.Config.Artifactories)
 }
diff --git a/internal/pipe/publish/publish.go b/internal/pipe/publish/publish.go
index 0d1ee0e4..c1e1266d 100644
--- a/internal/pipe/publish/publish.go
+++ b/internal/pipe/publish/publish.go
@@ -10,11 +10,11 @@ import (
 	"github.com/goreleaser/goreleaser/internal/pipe/blob"
 	"github.com/goreleaser/goreleaser/internal/pipe/brew"
 	"github.com/goreleaser/goreleaser/internal/pipe/docker"
-	"github.com/goreleaser/goreleaser/internal/pipe/put"
 	"github.com/goreleaser/goreleaser/internal/pipe/release"
 	"github.com/goreleaser/goreleaser/internal/pipe/s3"
 	"github.com/goreleaser/goreleaser/internal/pipe/scoop"
 	"github.com/goreleaser/goreleaser/internal/pipe/snapcraft"
+	"github.com/goreleaser/goreleaser/internal/pipe/upload"
 	"github.com/goreleaser/goreleaser/pkg/context"
 	"github.com/pkg/errors"
 )
@@ -38,7 +38,7 @@ type Publisher interface {
 var publishers = []Publisher{
 	s3.Pipe{},
 	blob.Pipe{},
-	put.Pipe{},
+	upload.Pipe{},
 	artifactory.Pipe{},
 	docker.Pipe{},
 	snapcraft.Pipe{},
diff --git a/internal/pipe/put/put.go b/internal/pipe/upload/upload.go
similarity index 56%
rename from internal/pipe/put/put.go
rename to internal/pipe/upload/upload.go
index b24e3edf..cdfb0d4f 100644
--- a/internal/pipe/put/put.go
+++ b/internal/pipe/upload/upload.go
@@ -1,9 +1,11 @@
-// Package put provides a Pipe that push using HTTP PUT
-package put
+// Package upload provides a Pipe that push using HTTP
+package upload
 
 import (
 	h "net/http"
 
+	"github.com/goreleaser/goreleaser/internal/deprecate"
+
 	"github.com/goreleaser/goreleaser/internal/http"
 	"github.com/goreleaser/goreleaser/internal/pipe"
 	"github.com/goreleaser/goreleaser/pkg/context"
@@ -15,30 +17,34 @@ type Pipe struct{}
 
 // String returns the description of the pipe
 func (Pipe) String() string {
-	return "HTTP PUT"
+	return "HTTP Upload"
 }
 
 // Default sets the pipe defaults
 func (Pipe) Default(ctx *context.Context) error {
-	return http.Defaults(ctx.Config.Puts)
+	if len(ctx.Config.Puts) > 0 {
+		deprecate.Notice("puts")
+		ctx.Config.Uploads = append(ctx.Config.Uploads, ctx.Config.Puts...)
+	}
+	return http.Defaults(ctx.Config.Uploads)
 }
 
 // Publish artifacts
 func (Pipe) Publish(ctx *context.Context) error {
-	if len(ctx.Config.Puts) == 0 {
-		return pipe.Skip("put section is not configured")
+	if len(ctx.Config.Uploads) == 0 {
+		return pipe.Skip("uploads section is not configured")
 	}
 
 	// Check requirements for every instance we have configured.
 	// If not fulfilled, we can skip this pipeline
-	for _, instance := range ctx.Config.Puts {
+	for _, instance := range ctx.Config.Uploads {
 		instance := instance
-		if skip := http.CheckConfig(ctx, &instance, "put"); skip != nil {
+		if skip := http.CheckConfig(ctx, &instance, "upload"); skip != nil {
 			return pipe.Skip(skip.Error())
 		}
 	}
 
-	return http.Upload(ctx, ctx.Config.Puts, "put", func(res *h.Response) error {
+	return http.Upload(ctx, ctx.Config.Uploads, "upload", func(res *h.Response) error {
 		if c := res.StatusCode; c < 200 || 299 < c {
 			return errors.Errorf("unexpected http response status: %s", res.Status)
 		}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 210f814f..25a21914 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -331,13 +331,14 @@ type Blob struct {
 	IDs      []string `yaml:"ids,omitempty"`
 }
 
-// Put HTTP upload configuration
-type Put 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"`
@@ -362,8 +363,9 @@ type Project struct {
 	Snapshot      Snapshot    `yaml:",omitempty"`
 	Checksum      Checksum    `yaml:",omitempty"`
 	Dockers       []Docker    `yaml:",omitempty"`
-	Artifactories []Put       `yaml:",omitempty"`
-	Puts          []Put       `yaml:",omitempty"`
+	Artifactories []Upload    `yaml:",omitempty"`
+	Uploads       []Upload    `yaml:",omitempty"`
+	Puts          []Upload    `yaml:",omitempty"` // TODO: remove this
 	S3            []S3        `yaml:"s3,omitempty"`
 	Blob          []Blob      `yaml:"blob,omitempty"` // TODO: remove this
 	Blobs         []Blob      `yaml:"blobs,omitempty"`
diff --git a/www/content/deprecations.md b/www/content/deprecations.md
index 3a2be66e..3d0b027e 100644
--- a/www/content/deprecations.md
+++ b/www/content/deprecations.md
@@ -39,6 +39,31 @@ to this:
 
 -->
 
+
+### puts
+
+> since 2019-11-15
+
+The HTTP upload support was extended to also accept `POST` as a method,
+so the name `puts` kind of lost its meaning.
+
+Change this:
+
+```yaml
+puts:
+- ...
+```
+
+to this:
+
+```yaml
+uploads:
+- ...
+```
+
+Also note that secrets environment variable name prefixes have changed from
+`PUT_` to `UPLOAD_`.
+
 ### nfpms.name_template
 
 > since 2019-11-15
diff --git a/www/content/put.md b/www/content/upload.md
similarity index 78%
rename from www/content/put.md
rename to www/content/upload.md
index 548d6aed..5ae392a2 100644
--- a/www/content/put.md
+++ b/www/content/upload.md
@@ -1,22 +1,23 @@
 ---
-title: HTTP Put
+title: HTTP Upload
 series: customization
 hideFromIndex: true
 weight: 120
 ---
 
-GoReleaser supports building and pushing artifacts to HTTP servers using simple HTTP PUT requests.
+GoReleaser supports building and pushing artifacts to HTTP servers using simple
+HTTP requests.
 
 ## How it works
 
-You can declare multiple Put instances.
-All binaries generated by your `builds` section will be pushed to each configured Put.
+You can declare multiple `uploads` instances. All binaries generated by your
+`builds` section will be pushed to each configured upload.
 
-If you have only one Put instance, the configuration is as easy as adding the
-upload target and a username to your `.goreleaser.yml` file:
+If you have only one `uploads` instance, the configuration is as easy as adding
+the upload target and a username to your `.goreleaser.yml` file:
 
 ```yaml
-puts:
+uploads:
   - name: production
     target: http://some.server/some/path/example-repo-local/{{ .ProjectName }}/{{ .Version }}/
     username: goreleaser
@@ -24,8 +25,8 @@ puts:
 
 Prerequisites:
 
-- An HTTP server accepting PUT requests
-- A user + password with grants to upload an artifact using PUT requests (if the server requires it)
+- An HTTP server accepting HTTP requests
+- A user + password with grants to upload an artifact using HTTP requests (if the server requires it)
 
 ### Target
 
@@ -63,9 +64,9 @@ This way we support auth for multiple instances.
 This also means that the `name` per configured instance needs to be unique
 per goreleaser configuration.
 
-The name of the environment variable will be `PUT_NAME_USERNAME`.
+The name of the environment variable will be `UPLOAD_NAME_USERNAME`.
 If your instance is named `production`, you can store the username in the
-environment variable `PUT_PRODUCTION_USERNAME`.
+environment variable `UPLOAD_PRODUCTION_USERNAME`.
 The name will be transformed to uppercase.
 
 If a configured username is found in the configuration file, then the
@@ -79,24 +80,24 @@ This way we support auth for multiple instances.
 This also means that the `name` per configured instance needs to be unique
 per goreleaser configuration.
 
-The name of the environment variable will be `PUT_NAME_SECRET`.
+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
-environment variable `PUT_PRODUCTION_SECRET`.
+environment variable `UPLOAD_PRODUCTION_SECRET`.
 The name will be transformed to uppercase.
 
 ### Server authentication
 
 You can authenticate your TLS server adding a trusted X.509 certificate chain
-in your put configuration.
+in your upload configuration.
 
 The trusted certificate chain will be used to validate the server certificates.
 
 You can set the trusted certificate chain using the `trusted_certificates`
-setting the put section with PEM encoded certificates on a YAML literal block
+setting the upload section with PEM encoded certificates on a YAML literal block
 like this:
 
 ```yaml
-puts:
+uploads:
   - name: "some HTTP/TLS server"
     #...(other settings)...
     trusted_certificates: |
@@ -118,13 +119,17 @@ Of course, you can customize a lot of things:
 
 ```yaml
 # .goreleaser.yml
-puts:
-  # You can have multiple Put instances.
+uploads:
+  # You can have multiple upload instances.
   -
-    # Unique name of your Put instance. Used to identify the instance.
+    # Unique name of your upload instance. Used to identify the instance.
     name: production
 
-    # IDs of the artifacts you want to PUT.
+    # HTTP method to use.
+    # Default: PUT
+    method: POST
+
+    # IDs of the artifacts you want to upload.
     ids:
     - foo
     - bar
@@ -135,7 +140,7 @@ puts:
     # Default is `archive`.
     mode: archive
 
-    # URL to be used as target of the HTTP PUT request
+    # URL to be used as target of the HTTP request
     target: https://some.server/some/path/example-repo-local/{{ .ProjectName }}/{{ .Version }}/
 
     # User that will be used for the deployment


Test patch:
diff --git a/internal/http/http_test.go b/internal/http/http_test.go
index 8f35f498..e99e6653 100644
--- a/internal/http/http_test.go
+++ b/internal/http/http_test.go
@@ -58,7 +58,7 @@ func TestAssetOpenDefault(t *testing.T) {
 
 func TestDefaults(t *testing.T) {
 	type args struct {
-		puts []config.Put
+		uploads []config.Upload
 	}
 	tests := []struct {
 		name     string
@@ -66,16 +66,16 @@ func TestDefaults(t *testing.T) {
 		wantErr  bool
 		wantMode string
 	}{
-		{"set default", args{[]config.Put{{Name: "a", Target: "http://"}}}, false, ModeArchive},
-		{"keep value", args{[]config.Put{{Name: "a", Target: "http://...", Mode: ModeBinary}}}, false, ModeBinary},
+		{"set default", args{[]config.Upload{{Name: "a", Target: "http://"}}}, false, ModeArchive},
+		{"keep value", args{[]config.Upload{{Name: "a", Target: "http://...", Mode: ModeBinary}}}, false, ModeBinary},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			if err := Defaults(tt.args.puts); (err != nil) != tt.wantErr {
+			if err := Defaults(tt.args.uploads); (err != nil) != tt.wantErr {
 				t.Errorf("Defaults() error = %v, wantErr %v", err, tt.wantErr)
 			}
-			if tt.wantMode != tt.args.puts[0].Mode {
-				t.Errorf("Incorrect Defaults() mode %q , wanted %q", tt.args.puts[0].Mode, tt.wantMode)
+			if tt.wantMode != tt.args.uploads[0].Mode {
+				t.Errorf("Incorrect Defaults() mode %q , wanted %q", tt.args.uploads[0].Mode, tt.wantMode)
 			}
 		})
 	}
@@ -86,7 +86,7 @@ func TestCheckConfig(t *testing.T) {
 	ctx.Env["TEST_A_SECRET"] = "x"
 	type args struct {
 		ctx    *context.Context
-		upload *config.Put
+		upload *config.Upload
 		kind   string
 	}
 	tests := []struct {
@@ -94,13 +94,13 @@ func TestCheckConfig(t *testing.T) {
 		args    args
 		wantErr bool
 	}{
-		{"ok", args{ctx, &config.Put{Name: "a", Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, false},
-		{"secret missing", args{ctx, &config.Put{Name: "b", Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, true},
-		{"target missing", args{ctx, &config.Put{Name: "a", Username: "pepe", Mode: ModeArchive}, "test"}, true},
-		{"name missing", args{ctx, &config.Put{Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, true},
-		{"mode missing", args{ctx, &config.Put{Name: "a", Target: "http://blabla", Username: "pepe"}, "test"}, true},
-		{"mode invalid", args{ctx, &config.Put{Name: "a", Target: "http://blabla", Username: "pepe", Mode: "blabla"}, "test"}, true},
-		{"cert invalid", args{ctx, &config.Put{Name: "a", Target: "http://blabla", Username: "pepe", Mode: ModeBinary, TrustedCerts: "bad cert!"}, "test"}, true},
+		{"ok", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, false},
+		{"secret missing", args{ctx, &config.Upload{Name: "b", Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, true},
+		{"target missing", args{ctx, &config.Upload{Name: "a", Username: "pepe", Mode: ModeArchive}, "test"}, true},
+		{"name missing", args{ctx, &config.Upload{Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, true},
+		{"mode missing", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe"}, "test"}, true},
+		{"mode invalid", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe", Mode: "blabla"}, "test"}, true},
+		{"cert invalid", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe", Mode: ModeBinary, TrustedCerts: "bad cert!"}, "test"}, true},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -242,12 +242,12 @@ func TestUpload(t *testing.T) {
 		tryTLS       bool
 		wantErrPlain bool
 		wantErrTLS   bool
-		setup        func(*httptest.Server) (*context.Context, config.Put)
+		setup        func(*httptest.Server) (*context.Context, config.Upload)
 		check        func(r []*h.Request) error
 	}{
 		{"wrong-mode", true, true, true, true,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         "wrong-mode",
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -258,8 +258,8 @@ func TestUpload(t *testing.T) {
 			checks(),
 		},
 		{"username-from-env", true, true, false, false,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeArchive,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -271,9 +271,25 @@ func TestUpload(t *testing.T) {
 				check{"/blah/2.1.0/a.tar", "u2", "x", content, map[string]string{}},
 			),
 		},
+		{"post", true, true, false, false,
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
+					Method:       h.MethodPost,
+					Mode:         ModeArchive,
+					Name:         "a",
+					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
+					Username:     "u1",
+					TrustedCerts: cert(s),
+				}
+			},
+			checks(
+				check{"/blah/2.1.0/a.deb", "u1", "x", content, map[string]string{}},
+				check{"/blah/2.1.0/a.tar", "u1", "x", content, map[string]string{}},
+			),
+		},
 		{"archive", true, true, false, false,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeArchive,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -287,8 +303,8 @@ func TestUpload(t *testing.T) {
 			),
 		},
 		{"archive_with_ids", true, true, false, false,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeArchive,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -303,8 +319,8 @@ func TestUpload(t *testing.T) {
 			),
 		},
 		{"binary", true, true, false, false,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeBinary,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -315,8 +331,8 @@ func TestUpload(t *testing.T) {
 			checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content, map[string]string{}}),
 		},
 		{"binary_with_ids", true, true, false, false,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeBinary,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -328,8 +344,8 @@ func TestUpload(t *testing.T) {
 			checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content, map[string]string{}}),
 		},
 		{"binary-add-ending-bar", true, true, false, false,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeBinary,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}",
@@ -340,8 +356,8 @@ func TestUpload(t *testing.T) {
 			checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content, map[string]string{}}),
 		},
 		{"archive-with-checksum-and-signature", true, true, false, false,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeArchive,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -359,8 +375,8 @@ func TestUpload(t *testing.T) {
 			),
 		},
 		{"bad-template", true, true, true, true,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeBinary,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectNameXXX}}/{{.VersionXXX}}/",
@@ -373,8 +389,8 @@ func TestUpload(t *testing.T) {
 			checks(),
 		},
 		{"failed-request", true, true, true, true,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeBinary,
 					Name:         "a",
 					Target:       s.URL[0:strings.LastIndex(s.URL, ":")] + "/{{.ProjectName}}/{{.Version}}/",
@@ -387,8 +403,8 @@ func TestUpload(t *testing.T) {
 			checks(),
 		},
 		{"broken-cert", false, true, false, true,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:         ModeBinary,
 					Name:         "a",
 					Target:       s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -401,16 +417,16 @@ func TestUpload(t *testing.T) {
 			checks(),
 		},
 		{"skip-publishing", true, true, true, true,
-			func(s *httptest.Server) (*context.Context, config.Put) {
+			func(s *httptest.Server) (*context.Context, config.Upload) {
 				c := *ctx
 				c.SkipPublish = true
-				return &c, config.Put{}
+				return &c, config.Upload{}
 			},
 			checks(),
 		},
 		{"checksumheader", true, true, false, false,
-			func(s *httptest.Server) (*context.Context, config.Put) {
-				return ctx, config.Put{
+			func(s *httptest.Server) (*context.Context, config.Upload) {
+				return ctx, config.Upload{
 					Mode:           ModeBinary,
 					Name:           "a",
 					Target:         s.URL + "/{{.ProjectName}}/{{.Version}}/",
@@ -423,14 +439,14 @@ func TestUpload(t *testing.T) {
 		},
 	}
 
-	uploadAndCheck := func(t *testing.T, setup func(*httptest.Server) (*context.Context, config.Put), wantErrPlain, wantErrTLS bool, check func(r []*h.Request) error, srv *httptest.Server) {
+	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) {
 		requests = nil
-		ctx, put := setup(srv)
+		ctx, upload := setup(srv)
 		wantErr := wantErrPlain
 		if srv.Certificate() != nil {
 			wantErr = wantErrTLS
 		}
-		if err := Upload(ctx, []config.Put{put}, "test", is2xx); (err != nil) != wantErr {
+		if err := Upload(ctx, []config.Upload{upload}, "test", is2xx); (err != nil) != wantErr {
 			t.Errorf("Upload() error = %v, wantErr %v", err, wantErr)
 		}
 		if err := check(requests); err != nil {
diff --git a/internal/pipe/artifactory/artifactory_test.go b/internal/pipe/artifactory/artifactory_test.go
index 1ece7d32..e1b50108 100644
--- a/internal/pipe/artifactory/artifactory_test.go
+++ b/internal/pipe/artifactory/artifactory_test.go
@@ -174,7 +174,7 @@ func TestRunPipe_ModeBinary(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production-us",
 				Mode:     "binary",
@@ -203,6 +203,7 @@ func TestRunPipe_ModeBinary(t *testing.T) {
 		})
 	}
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.NoError(t, Pipe{}.Publish(ctx))
 }
 
@@ -220,7 +221,7 @@ func TestRunPipe_ModeArchive(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "goreleaser",
 		Dist:        folder,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "archive",
@@ -300,6 +301,7 @@ func TestRunPipe_ModeArchive(t *testing.T) {
 		uploads.Store("deb", true)
 	})
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.NoError(t, Pipe{}.Publish(ctx))
 	_, ok := uploads.Load("targz")
 	assert.True(t, ok, "tar.gz file was not uploaded")
@@ -316,7 +318,7 @@ func TestRunPipe_ArtifactoryDown(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "goreleaser",
 		Dist:        folder,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "archive",
@@ -334,6 +336,8 @@ func TestRunPipe_ArtifactoryDown(t *testing.T) {
 		Name: "bin.tar.gz",
 		Path: tarfile.Name(),
 	})
+
+	assert.NoError(t, Pipe{}.Default(ctx))
 	err = Pipe{}.Publish(ctx)
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "connection refused")
@@ -348,7 +352,7 @@ func TestRunPipe_TargetTemplateError(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name: "production",
 				Mode: "binary",
@@ -369,6 +373,7 @@ func TestRunPipe_TargetTemplateError(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
+	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`)
 }
 
@@ -405,7 +410,7 @@ func TestRunPipe_BadCredentials(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "binary",
@@ -425,6 +430,7 @@ func TestRunPipe_BadCredentials(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	err = Pipe{}.Publish(ctx)
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "Bad credentials")
@@ -462,7 +468,7 @@ func TestRunPipe_UnparsableErrorResponse(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "binary",
@@ -482,6 +488,7 @@ func TestRunPipe_UnparsableErrorResponse(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.EqualError(t, Pipe{}.Publish(ctx), `artifactory: upload failed: invalid character '.' looking for beginning of value`)
 }
 
@@ -516,7 +523,7 @@ func TestRunPipe_UnparsableResponse(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "binary",
@@ -536,6 +543,7 @@ func TestRunPipe_UnparsableResponse(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.EqualError(t, Pipe{}.Publish(ctx), `artifactory: upload failed: invalid character 'i' looking for beginning of value`)
 }
 
@@ -543,7 +551,7 @@ func TestRunPipe_FileNotFound(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        "archivetest/dist",
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "binary",
@@ -563,6 +571,7 @@ func TestRunPipe_FileNotFound(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.EqualError(t, Pipe{}.Publish(ctx), `open archivetest/dist/mybin/mybin: no such file or directory`)
 }
 
@@ -580,7 +589,7 @@ func TestRunPipe_UnparsableTarget(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "binary",
@@ -600,12 +609,13 @@ func TestRunPipe_UnparsableTarget(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.EqualError(t, Pipe{}.Publish(ctx), `artifactory: upload failed: parse ://artifacts.company.com/example-repo-local/mybin/darwin/amd64/mybin: missing protocol scheme`)
 }
 
 func TestRunPipe_SkipWhenPublishFalse(t *testing.T) {
 	var ctx = context.New(config.Project{
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "binary",
@@ -619,6 +629,7 @@ func TestRunPipe_SkipWhenPublishFalse(t *testing.T) {
 	}
 	ctx.SkipPublish = true
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	err := Pipe{}.Publish(ctx)
 	assert.True(t, pipe.IsSkip(err))
 	assert.EqualError(t, err, pipe.ErrSkipPublishEnabled.Error())
@@ -635,7 +646,7 @@ func TestRunPipe_DirUpload(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Artifactories: []config.Put{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "binary",
@@ -655,6 +666,7 @@ func TestRunPipe_DirUpload(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.EqualError(t, Pipe{}.Publish(ctx), `artifactory: upload failed: the asset to upload can't be a directory`)
 }
 
@@ -663,7 +675,9 @@ func TestDescription(t *testing.T) {
 }
 
 func TestNoArtifactories(t *testing.T) {
-	assert.True(t, pipe.IsSkip(Pipe{}.Publish(context.New(config.Project{}))))
+	var ctx = context.New(config.Project{})
+	assert.NoError(t, Pipe{}.Default(ctx))
+	assert.True(t, pipe.IsSkip(Pipe{}.Publish(ctx)))
 }
 
 func TestArtifactoriesWithoutTarget(t *testing.T) {
@@ -672,7 +686,7 @@ func TestArtifactoriesWithoutTarget(t *testing.T) {
 			"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
 		},
 		Config: config.Project{
-			Artifactories: []config.Put{
+			Artifactories: []config.Upload{
 				{
 					Name:     "production",
 					Username: "deployuser",
@@ -681,6 +695,7 @@ func TestArtifactoriesWithoutTarget(t *testing.T) {
 		},
 	}
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.True(t, pipe.IsSkip(Pipe{}.Publish(ctx)))
 }
 
@@ -690,7 +705,7 @@ func TestArtifactoriesWithoutUsername(t *testing.T) {
 			"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
 		},
 		Config: config.Project{
-			Artifactories: []config.Put{
+			Artifactories: []config.Upload{
 				{
 					Name:   "production",
 					Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
@@ -699,30 +714,35 @@ func TestArtifactoriesWithoutUsername(t *testing.T) {
 		},
 	}
 
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.True(t, pipe.IsSkip(Pipe{}.Publish(ctx)))
 }
 
 func TestArtifactoriesWithoutName(t *testing.T) {
-	assert.True(t, pipe.IsSkip(Pipe{}.Publish(context.New(config.Project{
-		Artifactories: []config.Put{
+	var ctx = context.New(config.Project{
+		Artifactories: []config.Upload{
 			{
 				Username: "deployuser",
 				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
 			},
 		},
-	}))))
+	})
+	assert.NoError(t, Pipe{}.Default(ctx))
+	assert.True(t, pipe.IsSkip(Pipe{}.Publish(ctx)))
 }
 
 func TestArtifactoriesWithoutSecret(t *testing.T) {
-	assert.True(t, pipe.IsSkip(Pipe{}.Publish(context.New(config.Project{
-		Artifactories: []config.Put{
+	var ctx = context.New(config.Project{
+		Artifactories: []config.Upload{
 			{
 				Name:     "production",
 				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
 				Username: "deployuser",
 			},
 		},
-	}))))
+	})
+	assert.NoError(t, Pipe{}.Default(ctx))
+	assert.True(t, pipe.IsSkip(Pipe{}.Publish(ctx)))
 }
 
 func TestArtifactoriesWithInvalidMode(t *testing.T) {
@@ -731,7 +751,7 @@ func TestArtifactoriesWithInvalidMode(t *testing.T) {
 			"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
 		},
 		Config: config.Project{
-			Artifactories: []config.Put{
+			Artifactories: []config.Upload{
 				{
 					Name:     "production",
 					Mode:     "does-not-exists",
@@ -741,13 +761,15 @@ func TestArtifactoriesWithInvalidMode(t *testing.T) {
 			},
 		},
 	}
+
+	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.Error(t, Pipe{}.Publish(ctx))
 }
 
 func TestDefault(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
-			Artifactories: []config.Put{
+			Artifactories: []config.Upload{
 				{
 					Name:     "production",
 					Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
@@ -756,6 +778,7 @@ func TestDefault(t *testing.T) {
 			},
 		},
 	}
+
 	assert.NoError(t, Pipe{}.Default(ctx))
 	assert.Len(t, ctx.Config.Artifactories, 1)
 	var artifactory = ctx.Config.Artifactories[0]
@@ -765,7 +788,7 @@ func TestDefault(t *testing.T) {
 func TestDefaultNoArtifactories(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
-			Artifactories: []config.Put{},
+			Artifactories: []config.Upload{},
 		},
 	}
 	assert.NoError(t, Pipe{}.Default(ctx))
@@ -775,7 +798,7 @@ func TestDefaultNoArtifactories(t *testing.T) {
 func TestDefaultSet(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
-			Artifactories: []config.Put{
+			Artifactories: []config.Upload{
 				{
 					Mode: "custom",
 				},
diff --git a/internal/pipe/put/put_test.go b/internal/pipe/upload/upload_test.go
similarity index 86%
rename from internal/pipe/put/put_test.go
rename to internal/pipe/upload/upload_test.go
index b783b891..b7ce8708 100644
--- a/internal/pipe/put/put_test.go
+++ b/internal/pipe/upload/upload_test.go
@@ -1,9 +1,10 @@
-package put
+package upload
 
 import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	h "net/http"
 	"net/http/httptest"
 	"os"
 	"path/filepath"
@@ -106,14 +107,16 @@ func TestRunPipe_ModeBinary(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Name:     "production-us",
 				Mode:     "binary",
 				Target:   fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL),
 				Username: "deployuser",
 			},
 			{
+				Method:   h.MethodPut,
 				Name:     "production-eu",
 				Mode:     "binary",
 				Target:   fmt.Sprintf("%s/production-repo-remote/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL),
@@ -122,8 +125,8 @@ func TestRunPipe_ModeBinary(t *testing.T) {
 		},
 	})
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION-US_SECRET": "deployuser-secret",
-		"PUT_PRODUCTION-EU_SECRET": "productionuser-apikey",
+		"UPLOAD_PRODUCTION-US_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION-EU_SECRET": "productionuser-apikey",
 	}
 	for _, goos := range []string{"linux", "darwin"} {
 		ctx.Artifacts.Add(&artifact.Artifact{
@@ -152,8 +155,9 @@ func TestRunPipe_ModeArchive(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "goreleaser",
 		Dist:        folder,
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Name:     "production",
 				Mode:     "archive",
 				Target:   fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Version }}/", server.URL),
@@ -162,7 +166,7 @@ func TestRunPipe_ModeArchive(t *testing.T) {
 		},
 	})
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 	}
 	ctx.Version = "1.0.0"
 	ctx.Artifacts.Add(&artifact.Artifact{
@@ -214,8 +218,9 @@ func TestRunPipe_ArtifactoryDown(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "goreleaser",
 		Dist:        folder,
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Name:     "production",
 				Mode:     "archive",
 				Target:   "http://localhost:1234/example-repo-local/{{ .ProjectName }}/{{ .Version }}/",
@@ -225,7 +230,7 @@ func TestRunPipe_ArtifactoryDown(t *testing.T) {
 	})
 	ctx.Version = "2.0.0"
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 	}
 	ctx.Artifacts.Add(&artifact.Artifact{
 		Type: artifact.UploadableArchive,
@@ -246,10 +251,11 @@ func TestRunPipe_TargetTemplateError(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
-				Name: "production",
-				Mode: "binary",
+				Method: h.MethodPut,
+				Name:   "production",
+				Mode:   "binary",
 				// This template is not correct and should fail
 				Target:   "http://storage.company.com/example-repo-local/{{ .ProjectName /{{ .Version }}/",
 				Username: "deployuser",
@@ -257,7 +263,7 @@ func TestRunPipe_TargetTemplateError(t *testing.T) {
 		},
 	})
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 	}
 	ctx.Artifacts.Add(&artifact.Artifact{
 		Name:   "mybin",
@@ -268,7 +274,7 @@ func TestRunPipe_TargetTemplateError(t *testing.T) {
 	})
 	err = Pipe{}.Publish(ctx)
 	assert.Error(t, err)
-	assert.Contains(t, err.Error(), `put: 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: mybin:1: unexpected "/" in operand`)
 }
 
 func TestRunPipe_BadCredentials(t *testing.T) {
@@ -298,8 +304,9 @@ func TestRunPipe_BadCredentials(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Name:     "production",
 				Mode:     "binary",
 				Target:   fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL),
@@ -308,7 +315,7 @@ func TestRunPipe_BadCredentials(t *testing.T) {
 		},
 	})
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 	}
 	ctx.Artifacts.Add(&artifact.Artifact{
 		Name:   "mybin",
@@ -327,8 +334,9 @@ func TestRunPipe_FileNotFound(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        "archivetest/dist",
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Name:     "production",
 				Mode:     "binary",
 				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
@@ -337,7 +345,7 @@ func TestRunPipe_FileNotFound(t *testing.T) {
 		},
 	})
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 	}
 	ctx.Artifacts.Add(&artifact.Artifact{
 		Name:   "mybin",
@@ -364,8 +372,9 @@ func TestRunPipe_UnparsableTarget(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Name:     "production",
 				Mode:     "binary",
 				Target:   "://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
@@ -374,7 +383,7 @@ func TestRunPipe_UnparsableTarget(t *testing.T) {
 		},
 	})
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 	}
 	ctx.Artifacts.Add(&artifact.Artifact{
 		Name:   "mybin",
@@ -384,12 +393,12 @@ func TestRunPipe_UnparsableTarget(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
-	assert.EqualError(t, Pipe{}.Publish(ctx), `put: upload failed: parse ://artifacts.company.com/example-repo-local/mybin/darwin/amd64/mybin: missing protocol scheme`)
+	assert.EqualError(t, Pipe{}.Publish(ctx), `upload: upload failed: parse ://artifacts.company.com/example-repo-local/mybin/darwin/amd64/mybin: missing protocol scheme`)
 }
 
 func TestRunPipe_SkipWhenPublishFalse(t *testing.T) {
 	var ctx = context.New(config.Project{
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
 				Name:     "production",
 				Mode:     "binary",
@@ -399,7 +408,7 @@ func TestRunPipe_SkipWhenPublishFalse(t *testing.T) {
 		},
 	})
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 	}
 	ctx.SkipPublish = true
 
@@ -419,8 +428,9 @@ func TestRunPipe_DirUpload(t *testing.T) {
 	var ctx = context.New(config.Project{
 		ProjectName: "mybin",
 		Dist:        dist,
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Name:     "production",
 				Mode:     "binary",
 				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
@@ -429,7 +439,7 @@ func TestRunPipe_DirUpload(t *testing.T) {
 		},
 	})
 	ctx.Env = map[string]string{
-		"PUT_PRODUCTION_SECRET": "deployuser-secret",
+		"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 	}
 	ctx.Artifacts.Add(&artifact.Artifact{
 		Name:   "mybin",
@@ -439,7 +449,7 @@ func TestRunPipe_DirUpload(t *testing.T) {
 		Type:   artifact.UploadableBinary,
 	})
 
-	assert.EqualError(t, Pipe{}.Publish(ctx), `put: upload failed: the asset to upload can't be a directory`)
+	assert.EqualError(t, Pipe{}.Publish(ctx), `upload: upload failed: the asset to upload can't be a directory`)
 }
 
 func TestDescription(t *testing.T) {
@@ -453,11 +463,12 @@ func TestNoPuts(t *testing.T) {
 func TestPutsWithoutTarget(t *testing.T) {
 	var ctx = &context.Context{
 		Env: map[string]string{
-			"PUT_PRODUCTION_SECRET": "deployuser-secret",
+			"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 		},
 		Config: config.Project{
-			Puts: []config.Put{
+			Uploads: []config.Upload{
 				{
+					Method:   h.MethodPut,
 					Name:     "production",
 					Username: "deployuser",
 				},
@@ -471,11 +482,12 @@ func TestPutsWithoutTarget(t *testing.T) {
 func TestPutsWithoutUsername(t *testing.T) {
 	var ctx = &context.Context{
 		Env: map[string]string{
-			"PUT_PRODUCTION_SECRET": "deployuser-secret",
+			"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 		},
 		Config: config.Project{
-			Puts: []config.Put{
+			Uploads: []config.Upload{
 				{
+					Method: h.MethodPut,
 					Name:   "production",
 					Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
 				},
@@ -488,8 +500,9 @@ func TestPutsWithoutUsername(t *testing.T) {
 
 func TestPutsWithoutName(t *testing.T) {
 	assert.True(t, pipe.IsSkip(Pipe{}.Publish(context.New(config.Project{
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Username: "deployuser",
 				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
 			},
@@ -499,8 +512,9 @@ func TestPutsWithoutName(t *testing.T) {
 
 func TestPutsWithoutSecret(t *testing.T) {
 	assert.True(t, pipe.IsSkip(Pipe{}.Publish(context.New(config.Project{
-		Puts: []config.Put{
+		Uploads: []config.Upload{
 			{
+				Method:   h.MethodPut,
 				Name:     "production",
 				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
 				Username: "deployuser",
@@ -512,11 +526,12 @@ func TestPutsWithoutSecret(t *testing.T) {
 func TestPutsWithInvalidMode(t *testing.T) {
 	var ctx = &context.Context{
 		Env: map[string]string{
-			"PUT_PRODUCTION_SECRET": "deployuser-secret",
+			"UPLOAD_PRODUCTION_SECRET": "deployuser-secret",
 		},
 		Config: config.Project{
-			Puts: []config.Put{
+			Uploads: []config.Upload{
 				{
+					Method:   h.MethodPut,
 					Name:     "production",
 					Mode:     "does-not-exists",
 					Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
@@ -531,7 +546,7 @@ func TestPutsWithInvalidMode(t *testing.T) {
 func TestDefault(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
-			Puts: []config.Put{
+			Uploads: []config.Upload{
 				{
 					Name:     "production",
 					Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
@@ -541,33 +556,36 @@ func TestDefault(t *testing.T) {
 		},
 	}
 	assert.NoError(t, Pipe{}.Default(ctx))
-	assert.Len(t, ctx.Config.Puts, 1)
-	var put = ctx.Config.Puts[0]
-	assert.Equal(t, "archive", put.Mode)
+	assert.Len(t, ctx.Config.Uploads, 1)
+	var upload = ctx.Config.Uploads[0]
+	assert.Equal(t, "archive", upload.Mode)
+	assert.Equal(t, h.MethodPut, upload.Method)
 }
 
 func TestDefaultNoPuts(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
-			Puts: []config.Put{},
+			Uploads: []config.Upload{},
 		},
 	}
 	assert.NoError(t, Pipe{}.Default(ctx))
-	assert.Empty(t, ctx.Config.Puts)
+	assert.Empty(t, ctx.Config.Uploads)
 }
 
 func TestDefaultSet(t *testing.T) {
 	var ctx = &context.Context{
 		Config: config.Project{
-			Puts: []config.Put{
+			Uploads: []config.Upload{
 				{
-					Mode: "custom",
+					Method: h.MethodPost,
+					Mode:   "custom",
 				},
 			},
 		},
 	}
 	assert.NoError(t, Pipe{}.Default(ctx))
-	assert.Len(t, ctx.Config.Puts, 1)
-	var put = ctx.Config.Puts[0]
-	assert.Equal(t, "custom", put.Mode)
+	assert.Len(t, ctx.Config.Uploads, 1)
+	var upload = ctx.Config.Uploads[0]
+	assert.Equal(t, "custom", upload.Mode)
+	assert.Equal(t, h.MethodPost, upload.Method)
 }