1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-16 03:52:12 +02:00

feat: http POST (#1246)

* feat: http POST

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* feat: http POST

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: deprecate

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: upload tests

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: artifactory

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: http username validation

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: renames

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
This commit is contained in:
Carlos Alexandro Becker 2019-11-18 10:34:17 -03:00 committed by GitHub
parent 173edbac54
commit b0481a14e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 336 additions and 210 deletions

1
go.sum
View File

@ -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=

View File

@ -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 put.TrustedCerts != "" && !x509.NewCertPool().AppendCertsFromPEM([]byte(put.TrustedCerts)) {
return misconfigured(kind, put, "no certificate could be added from the specified trusted_certificates configuration")
if _, err := getPassword(ctx, upload, kind); err != nil {
return err
}
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
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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",
},

View File

@ -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{},

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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"`

View File

@ -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

View File

@ -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