mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-02-07 13:31:37 +02:00
feat: add digest to artifacts info of published docker images (#3540)
Extract the digest (sha256) of docker images from the `docker push` command for dockers published to the docker registry. Outputting the digest is required to avoid a race condition when referencing the image, where the image tag is being modified before the reference is done. See this [blog post](https://github.com/goreleaser/goreleaser/issues/3496) for more info. This PR fixes https://github.com/goreleaser/goreleaser/issues/3496. Note that the 'publish' pipe now must run before the 'metadata' pipe, so that the information extracted during the 'publish' pipe would appear in the metadata. Previously, the published docker images metadata wasn't printed (because of the order). It made sense because the content of the published image was just a subset of the local one. Now that it is printed to the metadata, it should have a different name to avoid confusion. As I mentioned, it wasn't printed before - so there shouldn't be any backward-compatibility issues. --- Local tests: ``` go test -v . === RUN TestVersion === RUN TestVersion/only_version === RUN TestVersion/version_and_date === RUN TestVersion/version,_date,_built_by === RUN TestVersion/all_empty === RUN TestVersion/complete --- PASS: TestVersion (0.00s) --- PASS: TestVersion/only_version (0.00s) --- PASS: TestVersion/version_and_date (0.00s) --- PASS: TestVersion/version,_date,_built_by (0.00s) --- PASS: TestVersion/all_empty (0.00s) --- PASS: TestVersion/complete (0.00s) PASS ok github.com/goreleaser/goreleaser 0.764s ``` Output example: ``` { "name": "gallegit/hello-world:latest", "path": "gallegit/hello-world:latest", "goos": "linux", "goarch": "amd64", "internal_type": 10, "type": "Published Docker Image", "extra": { "digest": "sha256:c3f7dd196a046dc061236d3c6ae1e2946269e90da30b0a959240ca799750e632" } } ``` Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com> Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
parent
778f099a9a
commit
5eb1e4ad0d
@ -86,8 +86,10 @@ func (t Type) String() string {
|
|||||||
return "Binary"
|
return "Binary"
|
||||||
case LinuxPackage:
|
case LinuxPackage:
|
||||||
return "Linux Package"
|
return "Linux Package"
|
||||||
case PublishableDockerImage, DockerImage:
|
case PublishableDockerImage:
|
||||||
return "Docker Image"
|
return "Docker Image"
|
||||||
|
case DockerImage:
|
||||||
|
return "Published Docker Image"
|
||||||
case DockerManifest:
|
case DockerManifest:
|
||||||
return "Docker Manifest"
|
return "Docker Manifest"
|
||||||
case PublishableSnapcraft, Snapcraft:
|
case PublishableSnapcraft, Snapcraft:
|
||||||
|
@ -34,7 +34,7 @@ func registerImager(use string, impl imager) {
|
|||||||
// imager is something that can build and push docker images.
|
// imager is something that can build and push docker images.
|
||||||
type imager interface {
|
type imager interface {
|
||||||
Build(ctx *context.Context, root string, images, flags []string) error
|
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.
|
// 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
|
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
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package docker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/goreleaser/goreleaser/pkg/context"
|
"github.com/goreleaser/goreleaser/pkg/context"
|
||||||
)
|
)
|
||||||
@ -43,11 +44,21 @@ type dockerImager struct {
|
|||||||
buildx bool
|
buildx bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i dockerImager) Push(ctx *context.Context, image string, flags []string) error {
|
var dockerDigestPattern = regexp.MustCompile("sha256:[a-z0-9]{64}")
|
||||||
if err := runCommand(ctx, ".", "docker", "push", image); err != nil {
|
|
||||||
return fmt.Errorf("failed to push %s: %w", image, err)
|
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 {
|
func (i dockerImager) Build(ctx *context.Context, root string, images, flags []string) error {
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
dockerConfigExtra = "DockerConfig"
|
dockerConfigExtra = "DockerConfig"
|
||||||
|
dockerDigestExtra = "digest"
|
||||||
|
|
||||||
useBuildx = "buildx"
|
useBuildx = "buildx"
|
||||||
useDocker = "docker"
|
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)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,6 +266,8 @@ func dockerPush(ctx *context.Context, image *artifact.Artifact) error {
|
|||||||
if docker.ID != "" {
|
if docker.ID != "" {
|
||||||
art.Extra[artifact.ExtraID] = docker.ID
|
art.Extra[artifact.ExtraID] = docker.ID
|
||||||
}
|
}
|
||||||
|
art.Extra[dockerDigestExtra] = digest
|
||||||
|
|
||||||
ctx.Artifacts.Add(art)
|
ctx.Artifacts.Add(art)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1091,6 +1091,13 @@ func TestRunPipe(t *testing.T) {
|
|||||||
// t.Log("removing docker image", img)
|
// t.Log("removing docker image", img)
|
||||||
require.NoError(t, rmi(img), "could not delete image %s", 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
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,10 +111,10 @@ var Pipeline = append(
|
|||||||
chocolatey.Pipe{},
|
chocolatey.Pipe{},
|
||||||
// create and push docker images
|
// create and push docker images
|
||||||
docker.Pipe{},
|
docker.Pipe{},
|
||||||
// creates a metadata.json and an artifacts.json files in the dist folder
|
|
||||||
metadata.Pipe{},
|
|
||||||
// publishes artifacts
|
// publishes artifacts
|
||||||
publish.Pipe{},
|
publish.Pipe{},
|
||||||
|
// creates a metadata.json and an artifacts.json files in the dist folder
|
||||||
|
metadata.Pipe{},
|
||||||
// announce releases
|
// announce releases
|
||||||
announce.Pipe{},
|
announce.Pipe{},
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user