From 652645b3bf22b31cf1891273694abdef5f7e57af Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Mon, 1 Oct 2018 16:52:16 -0300 Subject: [PATCH] feat: artifactory checksum header (#772) * feat: artifactory checksum header * fix: merge fixes --- internal/artifact/artifact.go | 5 +- internal/artifact/artifact_test.go | 2 +- internal/http/http.go | 24 +++++++--- internal/http/http_test.go | 47 ++++++++++++++----- internal/pipe/artifactory/artifactory.go | 3 ++ internal/pipe/artifactory/artifactory_test.go | 1 + pkg/config/config.go | 15 +++--- www/content/put.md | 6 ++- 8 files changed, 75 insertions(+), 28 deletions(-) diff --git a/internal/artifact/artifact.go b/internal/artifact/artifact.go index 99c489d07..0199beb4c 100644 --- a/internal/artifact/artifact.go +++ b/internal/artifact/artifact.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/apex/log" + "github.com/pkg/errors" ) // Type defines the type of an artifact @@ -67,13 +68,13 @@ func (a Artifact) Checksum() (string, error) { log.Debugf("calculating sha256sum for %s", a.Path) file, err := os.Open(a.Path) if err != nil { - return "", err + return "", errors.Wrap(err, "failed to checksum") } defer file.Close() // nolint: errcheck var hash = sha256.New() _, err = io.Copy(hash, file) if err != nil { - return "", err + return "", errors.Wrap(err, "failed to checksum") } return hex.EncodeToString(hash.Sum(nil)), nil } diff --git a/internal/artifact/artifact_test.go b/internal/artifact/artifact_test.go index 8381b16e3..3adee819b 100644 --- a/internal/artifact/artifact_test.go +++ b/internal/artifact/artifact_test.go @@ -159,6 +159,6 @@ func TestChecksumFileDoesntExist(t *testing.T) { Path: "/tmp/adasdasdas/asdasd/asdas", } sum, err := artifact.Checksum() - require.EqualError(t, err, `open /tmp/adasdasdas/asdasd/asdas: no such file or directory`) + require.EqualError(t, err, `failed to checksum: open /tmp/adasdasdas/asdasd/asdas: no such file or directory`) require.Empty(t, sum) } diff --git a/internal/http/http.go b/internal/http/http.go index 4d0b39875..ecf7a5423 100644 --- a/internal/http/http.go +++ b/internal/http/http.go @@ -159,7 +159,7 @@ func Upload(ctx *context.Context, puts []config.Put, kind string, check Response func uploadWithFilter(ctx *context.Context, put *config.Put, filter artifact.Filter, kind string, check ResponseChecker) error { var artifacts = ctx.Artifacts.Filter(filter).List() - log.Infof("will upload %d artifacts", len(artifacts)) + log.Debugf("will upload %d artifacts", len(artifacts)) var g = semerrgroup.New(ctx.Parallelism) for _, artifact := range artifacts { artifact := artifact @@ -201,7 +201,16 @@ func uploadAsset(ctx *context.Context, put *config.Put, artifact artifact.Artifa } targetURL += artifact.Name - _, err = uploadAssetToServer(ctx, put, targetURL, username, secret, asset, check) + var headers = map[string]string{} + if put.ChecksumHeader != "" { + sum, err := artifact.Checksum() + if err != nil { + return err + } + headers[put.ChecksumHeader] = sum + } + + _, err = uploadAssetToServer(ctx, put, targetURL, username, secret, headers, asset, check) if err != nil { msg := fmt.Sprintf("%s: upload failed", kind) log.WithError(err).WithFields(log.Fields{ @@ -220,8 +229,8 @@ func uploadAsset(ctx *context.Context, put *config.Put, artifact artifact.Artifa } // uploadAssetToServer uploads the asset file to target -func uploadAssetToServer(ctx *context.Context, put *config.Put, target, username, secret string, a *asset, check ResponseChecker) (*h.Response, error) { - req, err := newUploadRequest(target, username, secret, a) +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) if err != nil { return nil, err } @@ -230,15 +239,18 @@ func uploadAssetToServer(ctx *context.Context, put *config.Put, target, username } // newUploadRequest creates a new h.Request for uploading -func newUploadRequest(target, username, secret string, a *asset) (*h.Request, error) { +func newUploadRequest(target, username, secret string, headers map[string]string, a *asset) (*h.Request, error) { req, err := h.NewRequest("PUT", target, a.ReadCloser) if err != nil { return nil, err } - req.ContentLength = a.Size req.SetBasicAuth(username, secret) + for k, v := range headers { + req.Header.Add(k, v) + } + return req, err } diff --git a/internal/http/http_test.go b/internal/http/http_test.go index ccad186d4..0d5532187 100644 --- a/internal/http/http_test.go +++ b/internal/http/http_test.go @@ -9,11 +9,13 @@ import ( h "net/http" "net/http/httptest" "os" + "path/filepath" "strings" "sync" "testing" "github.com/pkg/errors" + "github.com/stretchr/testify/require" "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/pkg/config" @@ -133,6 +135,7 @@ type check struct { user string pass string content []byte + headers map[string]string } func checks(checks ...check) func(rs []*h.Request) error { @@ -181,6 +184,11 @@ func doCheck(c check, r *h.Request) error { if u, p, ok := r.BasicAuth(); !ok || u != c.user || p != c.pass { return errors.Errorf("bad basic auth credentials: %s/%s", u, p) } + for k, v := range c.headers { + if r.Header.Get(k) != v { + return errors.Errorf("bad header value for %s: expected %s, got %s", k, v, r.Header.Get(k)) + } + } return nil } @@ -221,6 +229,8 @@ func TestUpload(t *testing.T) { ctx.Env["TEST_A_USERNAME"] = "u2" ctx.Version = "2.1.0" ctx.Artifacts = artifact.New() + folder, err := ioutil.TempDir("", "goreleasertest") + require.NoError(t, err) for _, a := range []struct { ext string typ artifact.Type @@ -233,7 +243,9 @@ func TestUpload(t *testing.T) { {"sum", artifact.Checksum}, {"sig", artifact.Signature}, } { - ctx.Artifacts.Add(artifact.Artifact{Name: "a." + a.ext, Path: "/a/a." + a.ext, Type: a.typ}) + var file = filepath.Join(folder, "a."+a.ext) + require.NoError(t, ioutil.WriteFile(file, []byte("lorem ipsum"), 0644)) + ctx.Artifacts.Add(artifact.Artifact{Name: "a." + a.ext, Path: file, Type: a.typ}) } tests := []struct { @@ -267,8 +279,8 @@ func TestUpload(t *testing.T) { } }, checks( - check{"/blah/2.1.0/a.deb", "u2", "x", content}, - check{"/blah/2.1.0/a.tar", "u2", "x", content}, + check{"/blah/2.1.0/a.deb", "u2", "x", content, map[string]string{}}, + check{"/blah/2.1.0/a.tar", "u2", "x", content, map[string]string{}}, ), }, {"archive", true, true, false, false, @@ -282,8 +294,8 @@ func TestUpload(t *testing.T) { } }, checks( - check{"/blah/2.1.0/a.deb", "u1", "x", content}, - check{"/blah/2.1.0/a.tar", "u1", "x", content}, + 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{}}, ), }, {"binary", true, true, false, false, @@ -296,7 +308,7 @@ func TestUpload(t *testing.T) { TrustedCerts: cert(s), } }, - checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content}), + 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) { @@ -308,7 +320,7 @@ func TestUpload(t *testing.T) { TrustedCerts: cert(s), } }, - checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content}), + 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) { @@ -323,10 +335,10 @@ func TestUpload(t *testing.T) { } }, checks( - check{"/blah/2.1.0/a.deb", "u3", "x", content}, - check{"/blah/2.1.0/a.tar", "u3", "x", content}, - check{"/blah/2.1.0/a.sum", "u3", "x", content}, - check{"/blah/2.1.0/a.sig", "u3", "x", content}, + check{"/blah/2.1.0/a.deb", "u3", "x", content, map[string]string{}}, + check{"/blah/2.1.0/a.tar", "u3", "x", content, map[string]string{}}, + check{"/blah/2.1.0/a.sum", "u3", "x", content, map[string]string{}}, + check{"/blah/2.1.0/a.sig", "u3", "x", content, map[string]string{}}, ), }, {"bad-template", true, true, true, true, @@ -379,6 +391,19 @@ func TestUpload(t *testing.T) { }, checks(), }, + {"checksumheader", true, true, false, false, + func(s *httptest.Server) (*context.Context, config.Put) { + return ctx, config.Put{ + Mode: ModeBinary, + Name: "a", + Target: s.URL + "/{{.ProjectName}}/{{.Version}}/", + Username: "u2", + ChecksumHeader: "-x-sha256", + TrustedCerts: cert(s), + } + }, + checks(check{"/blah/2.1.0/a.ubi", "u2", "x", content, map[string]string{"-x-sha256": "5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269"}}), + }, } uploadAndCheck := func(setup func(*httptest.Server) (*context.Context, config.Put), wantErrPlain, wantErrTLS bool, check func(r []*h.Request) error, srv *httptest.Server) { diff --git a/internal/pipe/artifactory/artifactory.go b/internal/pipe/artifactory/artifactory.go index 6bebb7ffa..271236b34 100644 --- a/internal/pipe/artifactory/artifactory.go +++ b/internal/pipe/artifactory/artifactory.go @@ -45,6 +45,9 @@ func (Pipe) String() string { // Default sets the pipe defaults func (Pipe) Default(ctx *context.Context) error { + for i := range ctx.Config.Artifactories { + ctx.Config.Artifactories[i].ChecksumHeader = "X-Checksum-SHA256" + } return http.Defaults(ctx.Config.Artifactories) } diff --git a/internal/pipe/artifactory/artifactory_test.go b/internal/pipe/artifactory/artifactory_test.go index 9797c66ea..4238073d1 100644 --- a/internal/pipe/artifactory/artifactory_test.go +++ b/internal/pipe/artifactory/artifactory_test.go @@ -786,4 +786,5 @@ func TestDefaultSet(t *testing.T) { assert.Len(t, ctx.Config.Artifactories, 1) var artifactory = ctx.Config.Artifactories[0] assert.Equal(t, "custom", artifactory.Mode) + assert.Equal(t, "X-Checksum-SHA256", artifactory.ChecksumHeader) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 9563f0726..97ecfd671 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -288,13 +288,14 @@ type S3 struct { // Put HTTP upload configuration type Put struct { - Name string `yaml:",omitempty"` - Target string `yaml:",omitempty"` - Username string `yaml:",omitempty"` - Mode string `yaml:",omitempty"` - Checksum bool `yaml:",omitempty"` - Signature bool `yaml:",omitempty"` - TrustedCerts string `yaml:"trusted_certificates,omitempty"` + Name string `yaml:",omitempty"` + Target string `yaml:",omitempty"` + Username string `yaml:",omitempty"` + Mode string `yaml:",omitempty"` + ChecksumHeader string `yaml:"checksum_header,omitempty"` + TrustedCerts string `yaml:"trusted_certificates,omitempty"` + Checksum bool `yaml:",omitempty"` + Signature bool `yaml:",omitempty"` } // Project includes all project configuration diff --git a/www/content/put.md b/www/content/put.md index 5a1f33aec..14b3f9a08 100644 --- a/www/content/put.md +++ b/www/content/put.md @@ -49,7 +49,7 @@ Supported variables: - Arch - Arm -_Attention_: Variables _Os_, _Arch_ and _Arm_ are only supported in upload mode `binary`. +> **Warning**: Variables `Os`, `Arch` and `Arm` are only supported in upload mode `binary`. ### Username @@ -132,6 +132,10 @@ puts: target: https://some.server/some/path/example-repo-local/{{ .ProjectName }}/{{ .Version }}/ # User that will be used for the deployment username: deployuser + # An optional header you can use to tell GoReleaser to pass the artifact's + # SHA256 checksum withing the upload request. + # Default is empty. + checksum_header: -X-SHA256-Sum # Upload checksums (defaults to false) checksum: true # Upload signatures (defaults to false)