From 0886ff14e89b593384d0f4eeb1d6eea3bde5df37 Mon Sep 17 00:00:00 2001 From: gal-legit Date: Wed, 9 Nov 2022 15:19:00 +0200 Subject: [PATCH 1/3] feat: add digest to artifacts info of published docker images --- internal/artifact/artifact.go | 4 +++- internal/pipe/docker/api.go | 30 +++++++++++++++++++++++++++++- internal/pipe/docker/api_docker.go | 19 +++++++++++++++---- internal/pipe/docker/docker.go | 6 +++++- internal/pipeline/pipeline.go | 4 ++-- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/internal/artifact/artifact.go b/internal/artifact/artifact.go index 06434e02c4c..0774360961a 100644 --- a/internal/artifact/artifact.go +++ b/internal/artifact/artifact.go @@ -78,8 +78,10 @@ func (t Type) String() string { return "Binary" case LinuxPackage: return "Linux Package" - case PublishableDockerImage, DockerImage: + case PublishableDockerImage: return "Docker Image" + case DockerImage: + return "Published Docker Image" case DockerManifest: return "Docker Manifest" case PublishableSnapcraft, Snapcraft: diff --git a/internal/pipe/docker/api.go b/internal/pipe/docker/api.go index ca5f29c8fc2..d3337e85edb 100644 --- a/internal/pipe/docker/api.go +++ b/internal/pipe/docker/api.go @@ -34,7 +34,7 @@ func registerImager(use string, impl imager) { // imager is something that can build and push docker images. type imager interface { Build(ctx *context.Context, root string, images, flags []string) error - Push(ctx *context.Context, image string, flags []string) error + Push(ctx *context.Context, image string, flags []string) (digest string, err error) } // manifester is something that can create and push docker manifests. @@ -66,3 +66,31 @@ func runCommand(ctx *context.Context, dir, binary string, args ...string) error } return nil } + +func runCommandWithOutput(ctx *context.Context, dir, binary string, args ...string) ([]byte, error) { + fields := log.Fields{ + "cmd": append([]string{binary}, args[0]), + "cwd": dir, + } + + /* #nosec */ + cmd := exec.CommandContext(ctx, binary, args...) + cmd.Dir = dir + cmd.Env = ctx.Env.Strings() + + var b bytes.Buffer + w := gio.Safe(&b) + cmd.Stderr = io.MultiWriter(logext.NewWriter(fields, logext.Error), w) + + log.WithFields(fields).WithField("args", args[1:]).Debug("running") + out, err := cmd.Output() + if out != nil { + // regardless of command success, always print stdout for backward-compatibility with runCommand() + io.MultiWriter(logext.NewWriter(fields, logext.Error), w).Write(out) + } + if err != nil { + return nil, fmt.Errorf("%w: %s", err, b.String()) + } + + return out, nil +} diff --git a/internal/pipe/docker/api_docker.go b/internal/pipe/docker/api_docker.go index fe791e65a8c..26e4f7a63c1 100644 --- a/internal/pipe/docker/api_docker.go +++ b/internal/pipe/docker/api_docker.go @@ -2,6 +2,7 @@ package docker import ( "fmt" + "regexp" "github.com/goreleaser/goreleaser/pkg/context" ) @@ -43,11 +44,21 @@ type dockerImager struct { buildx bool } -func (i dockerImager) Push(ctx *context.Context, image string, flags []string) error { - if err := runCommand(ctx, ".", "docker", "push", image); err != nil { - return fmt.Errorf("failed to push %s: %w", image, err) +var dockerDigestPattern = regexp.MustCompile("sha256:[a-z0-9]{64}") + +func (i dockerImager) Push(ctx *context.Context, image string, flags []string) (digest string, err error) { + outBytes, err := runCommandWithOutput(ctx, ".", "docker", "push", image) + if err != nil { + return "", fmt.Errorf("failed to push %s: %w", image, err) } - return nil + + out := string(outBytes) + digest = dockerDigestPattern.FindString(out) + if digest == "" { + return "", fmt.Errorf("failed to find docker digest in docker push output: %v", out) + } + + return digest, nil } func (i dockerImager) Build(ctx *context.Context, root string, images, flags []string) error { diff --git a/internal/pipe/docker/docker.go b/internal/pipe/docker/docker.go index 17f39143284..7f868af3bfb 100644 --- a/internal/pipe/docker/docker.go +++ b/internal/pipe/docker/docker.go @@ -20,6 +20,7 @@ import ( const ( dockerConfigExtra = "DockerConfig" + dockerDigestExtra = "digest" useBuildx = "buildx" useDocker = "docker" @@ -248,7 +249,8 @@ func dockerPush(ctx *context.Context, image *artifact.Artifact) error { return pipe.Skip("prerelease detected with 'auto' push, skipping docker publish: " + image.Name) } - if err := imagers[docker.Use].Push(ctx, image.Name, docker.PushFlags); err != nil { + digest, err := imagers[docker.Use].Push(ctx, image.Name, docker.PushFlags) + if err != nil { return err } @@ -264,6 +266,8 @@ func dockerPush(ctx *context.Context, image *artifact.Artifact) error { if docker.ID != "" { art.Extra[artifact.ExtraID] = docker.ID } + art.Extra[dockerDigestExtra] = digest + ctx.Artifacts.Add(art) return nil } diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index 9d5dd684368..277279e6ebc 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -108,10 +108,10 @@ var Pipeline = append( scoop.Pipe{}, // create and push docker images docker.Pipe{}, - // creates a metadata.json and an artifacts.json files in the dist folder - metadata.Pipe{}, // publishes artifacts publish.Pipe{}, + // creates a metadata.json and an artifacts.json files in the dist folder + metadata.Pipe{}, // announce releases announce.Pipe{}, ) From 3a07fd223ad163a7cd5c2d5833f74a83ad7282df Mon Sep 17 00:00:00 2001 From: gal-legit <99600389+gal-legit@users.noreply.github.com> Date: Sat, 12 Nov 2022 11:47:01 +0200 Subject: [PATCH 2/3] Update internal/pipe/docker/api.go Co-authored-by: Carlos Alexandro Becker --- internal/pipe/docker/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pipe/docker/api.go b/internal/pipe/docker/api.go index d3337e85edb..4a210471f9b 100644 --- a/internal/pipe/docker/api.go +++ b/internal/pipe/docker/api.go @@ -86,7 +86,7 @@ func runCommandWithOutput(ctx *context.Context, dir, binary string, args ...stri out, err := cmd.Output() if out != nil { // regardless of command success, always print stdout for backward-compatibility with runCommand() - io.MultiWriter(logext.NewWriter(fields, logext.Error), w).Write(out) + _, _ = io.MultiWriter(logext.NewWriter(fields, logext.Error), w).Write(out) } if err != nil { return nil, fmt.Errorf("%w: %s", err, b.String()) From b5e56c17c5b95f6b562e215188b8eb9b9959972a Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Sat, 12 Nov 2022 00:31:03 -0300 Subject: [PATCH 3/3] fix: ensure digest is not empty Signed-off-by: Carlos A Becker --- internal/pipe/docker/docker_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/pipe/docker/docker_test.go b/internal/pipe/docker/docker_test.go index eb5dace1d8c..76b0dcdf896 100644 --- a/internal/pipe/docker/docker_test.go +++ b/internal/pipe/docker/docker_test.go @@ -1091,6 +1091,13 @@ func TestRunPipe(t *testing.T) { // t.Log("removing docker image", img) require.NoError(t, rmi(img), "could not delete image %s", img) } + + _ = ctx.Artifacts.Filter(artifact.ByType(artifact.DockerImage)).Visit(func(a *artifact.Artifact) error { + digest, err := artifact.Extra[string](*a, dockerDigestExtra) + require.NoError(t, err) + require.NotEmpty(t, digest) + return nil + }) }) } }