diff --git a/internal/artifact/artifact.go b/internal/artifact/artifact.go index abd707f6122..2c28d76b613 100644 --- a/internal/artifact/artifact.go +++ b/internal/artifact/artifact.go @@ -137,6 +137,7 @@ const ( ExtraBinaries = "Binaries" ExtraRefresh = "Refresh" ExtraReplaces = "Replaces" + ExtraDigest = "Digest" ) // Extras represents the extra fields in an artifact. diff --git a/internal/pipe/docker/docker.go b/internal/pipe/docker/docker.go index 5d313e4aead..a8ccf5ff1e2 100644 --- a/internal/pipe/docker/docker.go +++ b/internal/pipe/docker/docker.go @@ -20,7 +20,7 @@ import ( const ( dockerConfigExtra = "DockerConfig" - dockerDigestExtra = "Digest" + dockerDigestExtra = artifact.ExtraDigest useBuildx = "buildx" useDocker = "docker" diff --git a/internal/pipe/sign/sign.go b/internal/pipe/sign/sign.go index 9cce3f3cc2a..cffdaf880a4 100644 --- a/internal/pipe/sign/sign.go +++ b/internal/pipe/sign/sign.go @@ -154,6 +154,7 @@ func signone(ctx *context.Context, cfg config.Sign, art *artifact.Artifact) ([]* env["artifactName"] = art.Name // shouldn't be used env["artifact"] = art.Path env["artifactID"] = art.ID() + env["digest"] = artifact.ExtraOr(*art, artifact.ExtraDigest, "") tmplEnv, err := templateEnvS(ctx, cfg.Env) if err != nil { diff --git a/internal/pipe/sign/sign_docker.go b/internal/pipe/sign/sign_docker.go index f8a62868c99..c349b245886 100644 --- a/internal/pipe/sign/sign_docker.go +++ b/internal/pipe/sign/sign_docker.go @@ -28,7 +28,7 @@ func (DockerPipe) Default(ctx *context.Context) error { cfg.Cmd = "cosign" } if len(cfg.Args) == 0 { - cfg.Args = []string{"sign", "--key=cosign.key", "$artifact"} + cfg.Args = []string{"sign", "--key=cosign.key", "${artifact}@${digest}"} } if cfg.Artifacts == "" { cfg.Artifacts = "none" diff --git a/internal/pipe/sign/sign_docker_test.go b/internal/pipe/sign/sign_docker_test.go index b571a08e0ab..ca131582a11 100644 --- a/internal/pipe/sign/sign_docker_test.go +++ b/internal/pipe/sign/sign_docker_test.go @@ -28,7 +28,7 @@ func TestDockerSignDefault(t *testing.T) { require.NoError(t, err) require.Equal(t, ctx.Config.DockerSigns[0].Cmd, "cosign") require.Equal(t, ctx.Config.DockerSigns[0].Signature, "") - require.Equal(t, ctx.Config.DockerSigns[0].Args, []string{"sign", "--key=cosign.key", "$artifact"}) + require.Equal(t, ctx.Config.DockerSigns[0].Args, []string{"sign", "--key=cosign.key", "${artifact}@${digest}"}) require.Equal(t, ctx.Config.DockerSigns[0].Artifacts, "none") } @@ -54,12 +54,15 @@ func TestDockerSignArtifacts(t *testing.T) { testlib.CheckPath(t, "cosign") key := "cosign.key" cmd := "sh" - args := []string{"-c", "echo ${artifact} > ${signature} && cosign sign --key=" + key + " --upload=false ${artifact} > ${signature}"} + args := []string{"-c", "echo ${artifact}@${digest} > ${signature} && cosign sign --key=" + key + " --upload=false ${artifact}@${digest} > ${signature}"} password := "password" img1 := "ghcr.io/caarlos0/goreleaser-docker-manifest-actions-example:1.2.1-amd64" + img1Digest := "sha256:d7bf8be1b156cc0cd9d2e33765a69bc968d4ef6b2dea9b207d63129b9709862a" img2 := "ghcr.io/caarlos0/goreleaser-docker-manifest-actions-example:1.2.1-arm64v8" + img2Digest := "sha256:551801b7f42f8c33bfabb06e25804c2aca14776d2b7df33e07de54e887910b72" man1 := "ghcr.io/caarlos0/goreleaser-docker-manifest-actions-example:1.2.1" + man1Digest := "sha256:b5db21408555f1ef5d68008a0a03a7caba3f29b62c64f1404e139b005a20bf03" for name, cfg := range map[string]struct { Signs []config.Sign @@ -88,7 +91,7 @@ func TestDockerSignArtifacts(t *testing.T) { Stdin: &password, Cmd: "cosign", Certificate: `{{ replace (replace (replace .Env.artifact "/" "-") ":" "-") "." "" }}.pem`, - Args: []string{"sign", "--output-certificate=${certificate}", "--key=" + key, "--upload=false", "${artifact}"}, + Args: []string{"sign", "--output-certificate=${certificate}", "--key=" + key, "--upload=false", "${artifact}@${digest}"}, }, }, }, @@ -165,7 +168,8 @@ func TestDockerSignArtifacts(t *testing.T) { Path: img1, Type: artifact.DockerImage, Extra: map[string]interface{}{ - artifact.ExtraID: "img1", + artifact.ExtraID: "img1", + artifact.ExtraDigest: img1Digest, }, }) ctx.Artifacts.Add(&artifact.Artifact{ @@ -173,7 +177,8 @@ func TestDockerSignArtifacts(t *testing.T) { Path: img2, Type: artifact.DockerImage, Extra: map[string]interface{}{ - artifact.ExtraID: "img2", + artifact.ExtraID: "img2", + artifact.ExtraDigest: img2Digest, }, }) ctx.Artifacts.Add(&artifact.Artifact{ @@ -181,7 +186,8 @@ func TestDockerSignArtifacts(t *testing.T) { Path: man1, Type: artifact.DockerManifest, Extra: map[string]interface{}{ - artifact.ExtraID: "man1", + artifact.ExtraID: "man1", + artifact.ExtraDigest: man1Digest, }, }) diff --git a/www/docs/customization/docker_sign.md b/www/docs/customization/docker_sign.md index 8e1fc7bbf4e..0079ee3fa57 100644 --- a/www/docs/customization/docker_sign.md +++ b/www/docs/customization/docker_sign.md @@ -28,7 +28,7 @@ docker_signs: # Command line templateable arguments for the command # - # defaults to `["sign", "--key=cosign.key", "${artifact}"]` + # defaults to `["sign", "--key=cosign.key", "${artifact}@${digest}"]` args: ["sign", "--key=cosign.key", "--upload=false", "${artifact}"] @@ -77,12 +77,16 @@ docker_signs: These environment variables might be available in the fields that are templateable: - `${artifact}`: the path to the artifact that will be signed [^1] +- `${digest}`: the digest of the image/manifest that will be signed [^2] - `${artifactID}`: the ID of the artifact that will be signed - `${certificate}`: the certificate file name, if provided [^1]: notice that this might contain `/` characters, which depending on how you use it might evaluate to actual paths within the file system. Use with care. +[^2]: those are extracted automatically when running Docker push from within + GoReleaser. Using the digest helps making sure you're signing the right image + and avoid concurrency issues. ## Common usage example