From 677288779d0020b8446a4bd23dd7c6d7bb86918c Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Fri, 27 Nov 2020 22:32:04 -0300 Subject: [PATCH 01/11] feat: multi-arch docker images Signed-off-by: Carlos Alexandro Becker --- internal/pipe/docker/docker.go | 65 ++++++++++++++++++++++++++++++++ internal/pipe/publish/publish.go | 1 + pkg/config/config.go | 53 +++++++++++++++----------- 3 files changed, 96 insertions(+), 23 deletions(-) diff --git a/internal/pipe/docker/docker.go b/internal/pipe/docker/docker.go index d1eec2c88f7..b021e003e09 100644 --- a/internal/pipe/docker/docker.go +++ b/internal/pipe/docker/docker.go @@ -293,3 +293,68 @@ func dockerPush(ctx *context.Context, image *artifact.Artifact) error { }) return nil } + +// ManifestPipe is beta implementation of for the docker manifest feature, +// allowing to publish multi-arch docker images. +type ManifestPipe struct{} + +func (ManifestPipe) String() string { + return "docker manifests" +} + +// Publish the docker manifests. +func (ManifestPipe) Publish(ctx *context.Context) error { + if ctx.SkipPublish { + return pipe.ErrSkipPublishEnabled + } + var tmplt = tmpl.New(ctx) + for _, manifest := range ctx.Config.DockerManifests { + var imgs []string + for _, img := range manifest.ImageTemplates { + str, err := tmplt.Apply(img) + if err != nil { + return err + } + imgs = append(imgs, str) + } + man, err:= tmplt.Apply(manifest.ManifestTemplate) + if err != nil{ + return err + } + if err := dockerManifestCreate(ctx, man, imgs); err != nil { + return err + } + return dockerManifestPush(ctx, man) + } + return nil +} + +func dockerManifestCreate(ctx *context.Context, manifest string, images []string) error { + log.WithField("manifest", manifest).Info("creating docker manifest") + var args = []string{"manifest", "create", manifest} + for _, img := range images { + args = append(args, "--amend", img) + } + /* #nosec */ + var cmd = exec.CommandContext(ctx, "docker", args...) + log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to create docker manifest: %s: \n%s: %w", manifest, string(out), err) + } + log.Debugf("docker manifest output: \n%s", string(out)) + return nil +} + +func dockerManifestPush(ctx *context.Context, manifest string) error { + log.WithField("manifest", manifest).Info("pushing docker manifest") + /* #nosec */ + var cmd = exec.CommandContext(ctx, "docker", "manifest", "push", manifest) + log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to push docker manifest: %s: \n%s: %w", manifest, string(out), err) + } + log.Debugf("docker manifest output: \n%s", string(out)) + return nil +} diff --git a/internal/pipe/publish/publish.go b/internal/pipe/publish/publish.go index b8cfb25c194..223e4ef10f3 100644 --- a/internal/pipe/publish/publish.go +++ b/internal/pipe/publish/publish.go @@ -40,6 +40,7 @@ var publishers = []Publisher{ custompublishers.Pipe{}, artifactory.Pipe{}, docker.Pipe{}, + docker.ManifestPipe{}, snapcraft.Pipe{}, // This should be one of the last steps release.Pipe{}, diff --git a/pkg/config/config.go b/pkg/config/config.go index c5eca187e84..fdfb8456eb7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -490,6 +490,12 @@ type Docker struct { BuildFlagTemplates []string `yaml:"build_flag_templates,omitempty"` } +// DockerManifest config. +type DockerManifest struct { + ManifestTemplate string `yaml:"manifest_template,omitempty"` + ImageTemplates []string `yaml:"image_templates,omitempty"` +} + // Filters config. type Filters struct { Exclude []string `yaml:",omitempty"` @@ -563,29 +569,30 @@ type Source struct { // Project includes all project configuration. type Project struct { - ProjectName string `yaml:"project_name,omitempty"` - Env []string `yaml:",omitempty"` - Release Release `yaml:",omitempty"` - Milestones []Milestone `yaml:",omitempty"` - Brews []Homebrew `yaml:",omitempty"` - Scoop Scoop `yaml:",omitempty"` - Builds []Build `yaml:",omitempty"` - Archives []Archive `yaml:",omitempty"` - NFPMs []NFPM `yaml:"nfpms,omitempty"` - Snapcrafts []Snapcraft `yaml:",omitempty"` - Snapshot Snapshot `yaml:",omitempty"` - Checksum Checksum `yaml:",omitempty"` - Dockers []Docker `yaml:",omitempty"` - Artifactories []Upload `yaml:",omitempty"` - Uploads []Upload `yaml:",omitempty"` - Blobs []Blob `yaml:"blobs,omitempty"` - Publishers []Publisher `yaml:"publishers,omitempty"` - Changelog Changelog `yaml:",omitempty"` - Dist string `yaml:",omitempty"` - Signs []Sign `yaml:",omitempty"` - EnvFiles EnvFiles `yaml:"env_files,omitempty"` - Before Before `yaml:",omitempty"` - Source Source `yaml:",omitempty"` + ProjectName string `yaml:"project_name,omitempty"` + Env []string `yaml:",omitempty"` + Release Release `yaml:",omitempty"` + Milestones []Milestone `yaml:",omitempty"` + Brews []Homebrew `yaml:",omitempty"` + Scoop Scoop `yaml:",omitempty"` + Builds []Build `yaml:",omitempty"` + Archives []Archive `yaml:",omitempty"` + NFPMs []NFPM `yaml:"nfpms,omitempty"` + Snapcrafts []Snapcraft `yaml:",omitempty"` + Snapshot Snapshot `yaml:",omitempty"` + Checksum Checksum `yaml:",omitempty"` + Dockers []Docker `yaml:",omitempty"` + DockerManifests []DockerManifest `yaml:"docker_manifests,omitempty"` + Artifactories []Upload `yaml:",omitempty"` + Uploads []Upload `yaml:",omitempty"` + Blobs []Blob `yaml:"blobs,omitempty"` + Publishers []Publisher `yaml:"publishers,omitempty"` + Changelog Changelog `yaml:",omitempty"` + Dist string `yaml:",omitempty"` + Signs []Sign `yaml:",omitempty"` + EnvFiles EnvFiles `yaml:"env_files,omitempty"` + Before Before `yaml:",omitempty"` + Source Source `yaml:",omitempty"` // this is a hack ¯\_(ツ)_/¯ SingleBuild Build `yaml:"build,omitempty"` From 020e2b48288718d3b758d6f627f63e0ee9a99b07 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Fri, 27 Nov 2020 22:34:51 -0300 Subject: [PATCH 02/11] feat: split files Signed-off-by: Carlos Alexandro Becker --- internal/pipe/docker/doc.go | 3 ++ internal/pipe/docker/docker.go | 66 --------------------------- internal/pipe/docker/manifest.go | 76 ++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 66 deletions(-) create mode 100644 internal/pipe/docker/doc.go create mode 100644 internal/pipe/docker/manifest.go diff --git a/internal/pipe/docker/doc.go b/internal/pipe/docker/doc.go new file mode 100644 index 00000000000..4e8963a2b77 --- /dev/null +++ b/internal/pipe/docker/doc.go @@ -0,0 +1,3 @@ +// Package docker provides a Pipe that creates and pushes Docker images and +// manifests. +package docker diff --git a/internal/pipe/docker/docker.go b/internal/pipe/docker/docker.go index b021e003e09..7fcae54afe9 100644 --- a/internal/pipe/docker/docker.go +++ b/internal/pipe/docker/docker.go @@ -1,4 +1,3 @@ -// Package docker provides a Pipe that creates and pushes a Docker image package docker import ( @@ -293,68 +292,3 @@ func dockerPush(ctx *context.Context, image *artifact.Artifact) error { }) return nil } - -// ManifestPipe is beta implementation of for the docker manifest feature, -// allowing to publish multi-arch docker images. -type ManifestPipe struct{} - -func (ManifestPipe) String() string { - return "docker manifests" -} - -// Publish the docker manifests. -func (ManifestPipe) Publish(ctx *context.Context) error { - if ctx.SkipPublish { - return pipe.ErrSkipPublishEnabled - } - var tmplt = tmpl.New(ctx) - for _, manifest := range ctx.Config.DockerManifests { - var imgs []string - for _, img := range manifest.ImageTemplates { - str, err := tmplt.Apply(img) - if err != nil { - return err - } - imgs = append(imgs, str) - } - man, err:= tmplt.Apply(manifest.ManifestTemplate) - if err != nil{ - return err - } - if err := dockerManifestCreate(ctx, man, imgs); err != nil { - return err - } - return dockerManifestPush(ctx, man) - } - return nil -} - -func dockerManifestCreate(ctx *context.Context, manifest string, images []string) error { - log.WithField("manifest", manifest).Info("creating docker manifest") - var args = []string{"manifest", "create", manifest} - for _, img := range images { - args = append(args, "--amend", img) - } - /* #nosec */ - var cmd = exec.CommandContext(ctx, "docker", args...) - log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") - out, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("failed to create docker manifest: %s: \n%s: %w", manifest, string(out), err) - } - log.Debugf("docker manifest output: \n%s", string(out)) - return nil -} - -func dockerManifestPush(ctx *context.Context, manifest string) error { - log.WithField("manifest", manifest).Info("pushing docker manifest") - /* #nosec */ - var cmd = exec.CommandContext(ctx, "docker", "manifest", "push", manifest) - log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") - out, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("failed to push docker manifest: %s: \n%s: %w", manifest, string(out), err) - } - log.Debugf("docker manifest output: \n%s", string(out)) - return nil -} diff --git a/internal/pipe/docker/manifest.go b/internal/pipe/docker/manifest.go new file mode 100644 index 00000000000..f369ec70ed8 --- /dev/null +++ b/internal/pipe/docker/manifest.go @@ -0,0 +1,76 @@ +package docker + +import ( + "fmt" + "os/exec" + + "github.com/apex/log" + "github.com/goreleaser/goreleaser/internal/pipe" + "github.com/goreleaser/goreleaser/internal/tmpl" + "github.com/goreleaser/goreleaser/pkg/context" +) + +// ManifestPipe is beta implementation of for the docker manifest feature, +// allowing to publish multi-arch docker images. +type ManifestPipe struct{} + +func (ManifestPipe) String() string { + return "docker manifests" +} + +// Publish the docker manifests. +func (ManifestPipe) Publish(ctx *context.Context) error { + if ctx.SkipPublish { + return pipe.ErrSkipPublishEnabled + } + var tmplt = tmpl.New(ctx) + for _, manifest := range ctx.Config.DockerManifests { + var imgs []string + for _, img := range manifest.ImageTemplates { + str, err := tmplt.Apply(img) + if err != nil { + return err + } + imgs = append(imgs, str) + } + man, err:= tmplt.Apply(manifest.ManifestTemplate) + if err != nil{ + return err + } + if err := dockerManifestCreate(ctx, man, imgs); err != nil { + return err + } + return dockerManifestPush(ctx, man) + } + return nil +} + +func dockerManifestCreate(ctx *context.Context, manifest string, images []string) error { + log.WithField("manifest", manifest).Info("creating docker manifest") + var args = []string{"manifest", "create", manifest} + for _, img := range images { + args = append(args, "--amend", img) + } + /* #nosec */ + var cmd = exec.CommandContext(ctx, "docker", args...) + log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to create docker manifest: %s: \n%s: %w", manifest, string(out), err) + } + log.Debugf("docker manifest output: \n%s", string(out)) + return nil +} + +func dockerManifestPush(ctx *context.Context, manifest string) error { + log.WithField("manifest", manifest).Info("pushing docker manifest") + /* #nosec */ + var cmd = exec.CommandContext(ctx, "docker", "manifest", "push", manifest) + log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to push docker manifest: %s: \n%s: %w", manifest, string(out), err) + } + log.Debugf("docker manifest output: \n%s", string(out)) + return nil +} From 806d8c03a7440aa95d7b70ab44465d1901701a3a Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Fri, 27 Nov 2020 22:52:56 -0300 Subject: [PATCH 03/11] docs: manifest Signed-off-by: Carlos Alexandro Becker --- www/docs/customization/docker_manifest.md | 109 ++++++++++++++++++++++ www/mkdocs.yml | 1 + 2 files changed, 110 insertions(+) create mode 100644 www/docs/customization/docker_manifest.md diff --git a/www/docs/customization/docker_manifest.md b/www/docs/customization/docker_manifest.md new file mode 100644 index 00000000000..46caeaa5998 --- /dev/null +++ b/www/docs/customization/docker_manifest.md @@ -0,0 +1,109 @@ +--- +title: Docker Manifest +--- + +Since [v0.148.0](https://github.com/goreleaser/goreleaser/releases/tag/v0.148.0), +GoReleaser supports building and pushing Docker multi-platform images through +the `docker manifest` tool. + +For it to work, it [has to be enabled in the client configurations](https://github.com/docker/cli/blob/master/experimental/README.md). + +Please make sure `docker manifest` works before opening issues. + +!!! warning + Please note that this is a beta feature, and it may change or be removed + at any time. + +## Customization + +You can create several manifests in a single GoReleaser run, here are all the +options available: + +```yaml +# .goreleaser.yml +docker_manifests: + # You can have multiple Docker manifests. +- + # Name template for the manifest. + # Defaults to empty. + manifest_template: foo/bar:{{ .Version }} + + # Image name templates to be added to this manifest. + # Defaults to empty. + image_templates: + - foo/bar:{{ .Version }}-amd64 + - foo/bar:{{ .Version }}-arm64v8 +``` + +!!! tip + Learn more about the [name template engine](/customization/templates/). + +## How it works + +We basically build and push our images as usual, but we also add a new +section to our config defining which images are part of which manifests. + +GoReleaser will create and publish the manifest in its publish phase. + +!!! warning + Unfortunately, the manifest tool needs the images to be pushed to create + the manifest, that's why we both create and push it in the publish phase. + +## Example config + +In this example we will use Docker's `--build-arg` passing an `ARCH` argument. +This way we can use the same `Dockerfile` for both the `amd64` and the `arm64` +images: + +```dockerfile +# Dockerfile +ARG ARCH +FROM ${ARCH}/alpine +COPY mybin /usr/bin/mybin +ENTRYPOINT ["/usr/bin/mybin"] +``` + +Then, on our GoReleaser config file, we need to define both the `dockers` and +the `docker_manifests` section: + +```yaml +# goreleaser.yml +builds: +- env: + - CGO_ENABLED=0 + binary: mybin + goos: + - linux + goarch: + - amd64 + - arm64 +dockers: +- image_templates: + - "foo/bar:{{ .Version }}-amd64" + binaries: + - mybin + dockerfile: Dockerfile + build_flag_templates: + - "--build-arg" + - "ARCH=amd64" +- image_templates: + - "foo/bar:{{ .Version }}-arm64v8" + binaries: + - mybin + goarch: arm64 + dockerfile: Dockerfile + build_flag_templates: + - "--build-arg" + - "ARCH=arm64v8" +docker_manifests: +- manifest_template: foo/bar:{{ .Version }} + image_templates: + - foo/bar:{{ .Version }}-amd64 + - foo/bar:{{ .Version }}-arm64v8 +``` + +!!! warning + Notice that `ARCH` needs to be in the Docker arch format, not Go's. + +That config will build the 2 Docker images defined, as well as the manifest, +and push everything to Docker Hub. diff --git a/www/mkdocs.yml b/www/mkdocs.yml index 7f4e844746d..1fcdf167c3f 100644 --- a/www/mkdocs.yml +++ b/www/mkdocs.yml @@ -66,6 +66,7 @@ nav: - customization/checksum.md - customization/publishers.md - customization/docker.md + - customization/docker_manifest.md - customization/env.md - customization/hooks.md - customization/homebrew.md From 8016489346720abdd498a4fc906e0866efc7bbbf Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Fri, 27 Nov 2020 23:00:31 -0300 Subject: [PATCH 04/11] refactor: split files Signed-off-by: Carlos Alexandro Becker --- .../pipe/docker/docker_integration_test.go | 49 +++++++++++++++++++ internal/pipe/docker/docker_test.go | 42 ---------------- 2 files changed, 49 insertions(+), 42 deletions(-) create mode 100644 internal/pipe/docker/docker_integration_test.go diff --git a/internal/pipe/docker/docker_integration_test.go b/internal/pipe/docker/docker_integration_test.go new file mode 100644 index 00000000000..be966ef5cca --- /dev/null +++ b/internal/pipe/docker/docker_integration_test.go @@ -0,0 +1,49 @@ +package docker + +import ( + "flag" + "os" + "os/exec" + "testing" +) + +var it = flag.Bool("it", false, "push images to docker hub") +var registry = "localhost:5000/" +var altRegistry = "localhost:5050/" + +func TestMain(m *testing.M) { + flag.Parse() + if *it { + registry = "docker.io/" + } + os.Exit(m.Run()) +} + +func start(t *testing.T) { + if *it { + return + } + if out, err := exec.Command( + "docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2", + ).CombinedOutput(); err != nil { + t.Log("failed to start docker registry", string(out), err) + t.FailNow() + } + if out, err := exec.Command( + "docker", "run", "-d", "-p", "5050:5000", "--name", "alt_registry", "registry:2", + ).CombinedOutput(); err != nil { + t.Log("failed to start alternate docker registry", string(out), err) + t.FailNow() + } +} + +func killAndRm(t *testing.T) { + if *it { + return + } + t.Log("killing registry") + _ = exec.Command("docker", "kill", "registry").Run() + _ = exec.Command("docker", "rm", "registry").Run() + _ = exec.Command("docker", "kill", "alt_registry").Run() + _ = exec.Command("docker", "rm", "alt_registry").Run() +} diff --git a/internal/pipe/docker/docker_test.go b/internal/pipe/docker/docker_test.go index ee48fd4120e..0ae8a129d79 100644 --- a/internal/pipe/docker/docker_test.go +++ b/internal/pipe/docker/docker_test.go @@ -1,7 +1,6 @@ package docker import ( - "flag" "fmt" "io/ioutil" "os" @@ -19,47 +18,6 @@ import ( "github.com/stretchr/testify/require" ) -var it = flag.Bool("it", false, "push images to docker hub") -var registry = "localhost:5000/" -var altRegistry = "localhost:5050/" - -func TestMain(m *testing.M) { - flag.Parse() - if *it { - registry = "docker.io/" - } - os.Exit(m.Run()) -} - -func start(t *testing.T) { - if *it { - return - } - if out, err := exec.Command( - "docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2", - ).CombinedOutput(); err != nil { - t.Log("failed to start docker registry", string(out), err) - t.FailNow() - } - if out, err := exec.Command( - "docker", "run", "-d", "-p", "5050:5000", "--name", "alt_registry", "registry:2", - ).CombinedOutput(); err != nil { - t.Log("failed to start alternate docker registry", string(out), err) - t.FailNow() - } -} - -func killAndRm(t *testing.T) { - if *it { - return - } - t.Log("killing registry") - _ = exec.Command("docker", "kill", "registry").Run() - _ = exec.Command("docker", "rm", "registry").Run() - _ = exec.Command("docker", "kill", "alt_registry").Run() - _ = exec.Command("docker", "rm", "alt_registry").Run() -} - func TestRunPipe(t *testing.T) { type errChecker func(*testing.T, error) var shouldErr = func(msg string) errChecker { From fac97be93fb716fdda91e83a7afe7c8662840487 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 28 Nov 2020 00:46:51 -0300 Subject: [PATCH 05/11] test: added some Signed-off-by: Carlos Alexandro Becker --- ...ration_test.go => docker_registry_test.go} | 0 internal/pipe/docker/docker_test.go | 237 +++++++++++++++--- internal/pipe/docker/manifest.go | 52 ++-- internal/pipe/docker/testdata/Dockerfile.arch | 3 + pkg/config/config.go | 2 + 5 files changed, 240 insertions(+), 54 deletions(-) rename internal/pipe/docker/{docker_integration_test.go => docker_registry_test.go} (100%) create mode 100644 internal/pipe/docker/testdata/Dockerfile.arch diff --git a/internal/pipe/docker/docker_integration_test.go b/internal/pipe/docker/docker_registry_test.go similarity index 100% rename from internal/pipe/docker/docker_integration_test.go rename to internal/pipe/docker/docker_registry_test.go diff --git a/internal/pipe/docker/docker_test.go b/internal/pipe/docker/docker_test.go index 0ae8a129d79..be3eb0558c6 100644 --- a/internal/pipe/docker/docker_test.go +++ b/internal/pipe/docker/docker_test.go @@ -18,6 +18,7 @@ import ( "github.com/stretchr/testify/require" ) +// TODO: this test is too big... split in smaller tests? Mainly the manifest ones... func TestRunPipe(t *testing.T) { type errChecker func(*testing.T, error) var shouldErr = func(msg string) errChecker { @@ -47,13 +48,168 @@ func TestRunPipe(t *testing.T) { var noLabels = func(t *testing.T, count int) {} var table = map[string]struct { - dockers []config.Docker - env map[string]string - expect []string - assertImageLabels imageLabelFinder - assertError errChecker - pubAssertError errChecker + dockers []config.Docker + manifests []config.DockerManifest + env map[string]string + expect []string + assertImageLabels imageLabelFinder + assertError errChecker + pubAssertError errChecker + manifestAssertError errChecker }{ + "multiarch": { + dockers: []config.Docker{ + { + ImageTemplates: []string{registry + "goreleaser/test_multiarch:test-amd64"}, + Goos: "linux", + Goarch: "amd64", + Dockerfile: "testdata/Dockerfile.arch", + Binaries: []string{"mybin"}, + BuildFlagTemplates: []string{"--build-arg", "ARCH=amd64"}, + }, + { + ImageTemplates: []string{registry + "goreleaser/test_multiarch:test-arm64v8"}, + Goos: "linux", + Goarch: "arm64", + Dockerfile: "testdata/Dockerfile.arch", + Binaries: []string{"mybin"}, + BuildFlagTemplates: []string{"--build-arg", "ARCH=arm64v8"}, + }, + }, + manifests: []config.DockerManifest{ + { + // XXX: fails if :latest https://github.com/docker/distribution/issues/3100 + ManifestTemplate: registry + "goreleaser/test_multiarch:test", + ImageTemplates: []string{ + registry + "goreleaser/test_multiarch:test-amd64", + registry + "goreleaser/test_multiarch:test-arm64v8", + }, + CreateFlags: []string{"--insecure"}, + PushFlags: []string{"--insecure"}, + }, + }, + expect: []string{ + registry + "goreleaser/test_multiarch:test-amd64", + registry + "goreleaser/test_multiarch:test-arm64v8", + }, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, + assertImageLabels: noLabels, + }, + "multiarch image not found": { + dockers: []config.Docker{ + { + ImageTemplates: []string{registry + "goreleaser/test_multiarch_fail:latest-arm64v8"}, + Goos: "linux", + Goarch: "arm64", + Dockerfile: "testdata/Dockerfile.arch", + Binaries: []string{"mybin"}, + BuildFlagTemplates: []string{"--build-arg", "ARCH=arm64v8"}, + }, + }, + manifests: []config.DockerManifest{ + { + ManifestTemplate: registry + "goreleaser/test_multiarch_fail:test", + ImageTemplates: []string{registry + "goreleaser/test_multiarch_fail:latest-amd64"}, + CreateFlags: []string{"--insecure"}, + PushFlags: []string{"--insecure"}, + }, + }, + expect: []string{registry + "goreleaser/test_multiarch_fail:latest-arm64v8"}, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldErr("failed to create docker manifest: localhost:5000/goreleaser/test_multiarch_fail:test"), + assertImageLabels: noLabels, + }, + "multiarch manifest template error": { + dockers: []config.Docker{ + { + ImageTemplates: []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"}, + Goos: "linux", + Goarch: "arm64", + Dockerfile: "testdata/Dockerfile", + Binaries: []string{"mybin"}, + }, + }, + manifests: []config.DockerManifest{ + { + ManifestTemplate: registry + "goreleaser/test_multiarch_manifest_tmpl_error:{{ .Goos }", + ImageTemplates: []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"}, + }, + }, + expect: []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"}, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldErr(`template: tmpl:1: unexpected "}" in operand`), + assertImageLabels: noLabels, + }, + "multiarch image template error": { + dockers: []config.Docker{ + { + ImageTemplates: []string{registry + "goreleaser/test_multiarch_img_tmpl_error"}, + Goos: "linux", + Goarch: "arm64", + Dockerfile: "testdata/Dockerfile", + Binaries: []string{"mybin"}, + }, + }, + manifests: []config.DockerManifest{ + { + ManifestTemplate: registry + "goreleaser/test_multiarch_img_tmpl_error", + ImageTemplates: []string{registry + "goreleaser/test_multiarch_img_tmpl_error:{{ .Goos }"}, + }, + }, + expect: []string{registry + "goreleaser/test_multiarch_img_tmpl_error"}, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldErr(`template: tmpl:1: unexpected "}" in operand`), + assertImageLabels: noLabels, + }, + "multiarch missing manifest name": { + dockers: []config.Docker{ + { + ImageTemplates: []string{registry + "goreleaser/test_multiarch_no_mainifest_name"}, + Goos: "linux", + Goarch: "arm64", + Dockerfile: "testdata/Dockerfile", + Binaries: []string{"mybin"}, + }, + }, + manifests: []config.DockerManifest{ + { + ManifestTemplate: " ", + ImageTemplates: []string{registry + "goreleaser/test_multiarch_no_mainifest_name"}, + }, + }, + expect: []string{registry + "goreleaser/test_multiarch_no_mainifest_name"}, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: testlib.AssertSkipped, + assertImageLabels: noLabels, + }, + "multiarch missing images": { + dockers: []config.Docker{ + { + ImageTemplates: []string{registry + "goreleaser/test_multiarch_no_mainifest_images"}, + Dockerfile: "testdata/Dockerfile", + Goos: "linux", + Goarch: "arm64", + Binaries: []string{"mybin"}, + }, + }, + manifests: []config.DockerManifest{ + { + ManifestTemplate: "ignored", + ImageTemplates: []string{" ", " ", ""}, + }, + }, + expect: []string{registry + "goreleaser/test_multiarch_no_mainifest_images"}, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: testlib.AssertSkipped, + assertImageLabels: noLabels, + }, "valid": { env: map[string]string{ "FOO": "123", @@ -107,8 +263,9 @@ func TestRunPipe(t *testing.T) { "label=org.label-schema.vcs-ref=a1b2c3d4", "label=org.label-schema.name=mybin", ), - assertError: shouldNotErr, - pubAssertError: shouldNotErr, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, }, "valid-with-builds": { dockers: []config.Docker{ @@ -126,9 +283,10 @@ func TestRunPipe(t *testing.T) { expect: []string{ registry + "goreleaser/test_run_pipe_build:latest", }, - assertImageLabels: noLabels, - assertError: shouldNotErr, - pubAssertError: shouldNotErr, + assertImageLabels: noLabels, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, }, "multiple images with same extra file": { dockers: []config.Docker{ @@ -157,9 +315,10 @@ func TestRunPipe(t *testing.T) { registry + "goreleaser/multiplefiles1:latest", registry + "goreleaser/multiplefiles2:latest", }, - assertImageLabels: noLabels, - assertError: shouldNotErr, - pubAssertError: shouldNotErr, + assertImageLabels: noLabels, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, }, "multiple images with same dockerfile": { dockers: []config.Docker{ @@ -187,8 +346,9 @@ func TestRunPipe(t *testing.T) { registry + "goreleaser/test_run_pipe:latest", registry + "goreleaser/test_run_pipe2:latest", }, - assertError: shouldNotErr, - pubAssertError: shouldNotErr, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, }, "valid_skip_push": { dockers: []config.Docker{ @@ -253,9 +413,10 @@ func TestRunPipe(t *testing.T) { expect: []string{ registry + "goreleaser/test_run_pipe:1.0.0", }, - assertImageLabels: noLabels, - assertError: shouldNotErr, - pubAssertError: shouldNotErr, + assertImageLabels: noLabels, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, }, "valid build args": { dockers: []config.Docker{ @@ -275,9 +436,10 @@ func TestRunPipe(t *testing.T) { expect: []string{ registry + "goreleaser/test_build_args:latest", }, - assertImageLabels: noLabels, - assertError: shouldNotErr, - pubAssertError: shouldNotErr, + assertImageLabels: noLabels, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, }, "bad build args": { dockers: []config.Docker{ @@ -415,9 +577,10 @@ func TestRunPipe(t *testing.T) { expect: []string{ "docker.io/nope:latest", }, - assertImageLabels: noLabels, - assertError: shouldNotErr, - pubAssertError: shouldErr(`requested access to the resource is denied`), + assertImageLabels: noLabels, + assertError: shouldNotErr, + pubAssertError: shouldErr(`requested access to the resource is denied`), + manifestAssertError: shouldNotErr, }, "dockerfile_doesnt_exist": { dockers: []config.Docker{ @@ -471,9 +634,10 @@ func TestRunPipe(t *testing.T) { Dockerfile: "testdata/Dockerfile.multiple", }, }, - assertImageLabels: noLabels, - assertError: shouldNotErr, - pubAssertError: shouldNotErr, + assertImageLabels: noLabels, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, expect: []string{ registry + "goreleaser/multiple:latest", }, @@ -492,9 +656,10 @@ func TestRunPipe(t *testing.T) { Dockerfile: "testdata/Dockerfile", }, }, - assertImageLabels: noLabels, - assertError: shouldNotErr, - pubAssertError: shouldNotErr, + assertImageLabels: noLabels, + assertError: shouldNotErr, + pubAssertError: shouldNotErr, + manifestAssertError: shouldNotErr, expect: []string{ registry + "goreleaser/templatedbins:latest", }, @@ -533,9 +698,10 @@ func TestRunPipe(t *testing.T) { require.NoError(tt, err) var ctx = context.New(config.Project{ - ProjectName: "mybin", - Dist: dist, - Dockers: docker.dockers, + ProjectName: "mybin", + Dist: dist, + Dockers: docker.dockers, + DockerManifests: docker.manifests, }) ctx.Parallelism = 1 ctx.Env = docker.env @@ -550,7 +716,7 @@ func TestRunPipe(t *testing.T) { Patch: 0, } for _, os := range []string{"linux", "darwin"} { - for _, arch := range []string{"amd64", "386"} { + for _, arch := range []string{"amd64", "386", "arm64"} { for _, bin := range []string{"mybin", "anotherbin"} { ctx.Artifacts.Add(&artifact.Artifact{ Name: bin, @@ -576,6 +742,7 @@ func TestRunPipe(t *testing.T) { docker.assertError(tt, err) if err == nil { docker.pubAssertError(tt, Pipe{}.Publish(ctx)) + docker.manifestAssertError(tt, ManifestPipe{}.Publish(ctx)) } for _, d := range docker.dockers { diff --git a/internal/pipe/docker/manifest.go b/internal/pipe/docker/manifest.go index f369ec70ed8..f4a1d614ca6 100644 --- a/internal/pipe/docker/manifest.go +++ b/internal/pipe/docker/manifest.go @@ -3,9 +3,11 @@ package docker import ( "fmt" "os/exec" + "strings" "github.com/apex/log" "github.com/goreleaser/goreleaser/internal/pipe" + "github.com/goreleaser/goreleaser/internal/semerrgroup" "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/context" ) @@ -23,34 +25,44 @@ func (ManifestPipe) Publish(ctx *context.Context) error { if ctx.SkipPublish { return pipe.ErrSkipPublishEnabled } - var tmplt = tmpl.New(ctx) + var g = semerrgroup.NewSkipAware(semerrgroup.New(ctx.Parallelism)) for _, manifest := range ctx.Config.DockerManifests { - var imgs []string - for _, img := range manifest.ImageTemplates { - str, err := tmplt.Apply(img) - if err != nil { + manifest:=manifest + g.Go(func() error { + man, err:= tmpl.New(ctx).Apply(manifest.ManifestTemplate) + if err != nil{ return err } - imgs = append(imgs, str) - } - man, err:= tmplt.Apply(manifest.ManifestTemplate) - if err != nil{ - return err - } - if err := dockerManifestCreate(ctx, man, imgs); err != nil { - return err - } - return dockerManifestPush(ctx, man) + var imgs []string + for _, img := range manifest.ImageTemplates { + str, err := tmpl.New(ctx).Apply(img) + if err != nil { + return err + } + imgs = append(imgs, str) + } + if strings.TrimSpace(man) == "" { + return pipe.Skip("manifest name is empty") + } + if strings.TrimSpace(strings.Join(manifest.ImageTemplates, "")) == "" { + return pipe.Skip("manifest has no images") + } + if err := dockerManifestCreate(ctx, man, imgs, manifest.CreateFlags); err != nil { + return err + } + return dockerManifestPush(ctx, man, manifest.PushFlags) + }) } - return nil + return g.Wait() } -func dockerManifestCreate(ctx *context.Context, manifest string, images []string) error { +func dockerManifestCreate(ctx *context.Context, manifest string, images, flags []string) error { log.WithField("manifest", manifest).Info("creating docker manifest") var args = []string{"manifest", "create", manifest} for _, img := range images { args = append(args, "--amend", img) } + args = append(args, flags...) /* #nosec */ var cmd = exec.CommandContext(ctx, "docker", args...) log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") @@ -62,10 +74,12 @@ func dockerManifestCreate(ctx *context.Context, manifest string, images []string return nil } -func dockerManifestPush(ctx *context.Context, manifest string) error { +func dockerManifestPush(ctx *context.Context, manifest string, flags []string) error { log.WithField("manifest", manifest).Info("pushing docker manifest") + var args = []string{"manifest", "push", manifest} + args = append(args, flags...) /* #nosec */ - var cmd = exec.CommandContext(ctx, "docker", "manifest", "push", manifest) + var cmd = exec.CommandContext(ctx, "docker", args...) log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") out, err := cmd.CombinedOutput() if err != nil { diff --git a/internal/pipe/docker/testdata/Dockerfile.arch b/internal/pipe/docker/testdata/Dockerfile.arch new file mode 100644 index 00000000000..588858ed86c --- /dev/null +++ b/internal/pipe/docker/testdata/Dockerfile.arch @@ -0,0 +1,3 @@ +ARG ARCH +FROM ${ARCH}/alpine +ADD mybin / diff --git a/pkg/config/config.go b/pkg/config/config.go index fdfb8456eb7..32c67fd5cca 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -494,6 +494,8 @@ type Docker struct { type DockerManifest struct { ManifestTemplate string `yaml:"manifest_template,omitempty"` ImageTemplates []string `yaml:"image_templates,omitempty"` + CreateFlags []string `yaml:"create_flags,omitempty"` + PushFlags []string `yaml:"push_flags,omitempty"` } // Filters config. From 8f7c18da08d6f8837bf52730aa9f6d76c7d9f8b1 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 28 Nov 2020 00:47:48 -0300 Subject: [PATCH 06/11] docs: flags Signed-off-by: Carlos Alexandro Becker --- www/docs/customization/docker_manifest.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/www/docs/customization/docker_manifest.md b/www/docs/customization/docker_manifest.md index 46caeaa5998..299688da13f 100644 --- a/www/docs/customization/docker_manifest.md +++ b/www/docs/customization/docker_manifest.md @@ -33,6 +33,16 @@ docker_manifests: image_templates: - foo/bar:{{ .Version }}-amd64 - foo/bar:{{ .Version }}-arm64v8 + + # Extra flags to be passed down to the manifest create command. + # Defaults to empty. + create_flags: + - --insecure + + # Extra flags to be passed down to the manifest push command. + # Defaults to empty. + push_flags: + - --insecure ``` !!! tip From 61e6b78dafd7e050691ffd375ffcbc43f808ba55 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 28 Nov 2020 00:48:00 -0300 Subject: [PATCH 07/11] fix: fmt Signed-off-by: Carlos Alexandro Becker --- internal/pipe/docker/docker_test.go | 20 ++++++++++---------- internal/pipe/docker/manifest.go | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/internal/pipe/docker/docker_test.go b/internal/pipe/docker/docker_test.go index be3eb0558c6..a9db893fe22 100644 --- a/internal/pipe/docker/docker_test.go +++ b/internal/pipe/docker/docker_test.go @@ -125,11 +125,11 @@ func TestRunPipe(t *testing.T) { "multiarch manifest template error": { dockers: []config.Docker{ { - ImageTemplates: []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"}, - Goos: "linux", - Goarch: "arm64", - Dockerfile: "testdata/Dockerfile", - Binaries: []string{"mybin"}, + ImageTemplates: []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"}, + Goos: "linux", + Goarch: "arm64", + Dockerfile: "testdata/Dockerfile", + Binaries: []string{"mybin"}, }, }, manifests: []config.DockerManifest{ @@ -147,11 +147,11 @@ func TestRunPipe(t *testing.T) { "multiarch image template error": { dockers: []config.Docker{ { - ImageTemplates: []string{registry + "goreleaser/test_multiarch_img_tmpl_error"}, - Goos: "linux", - Goarch: "arm64", - Dockerfile: "testdata/Dockerfile", - Binaries: []string{"mybin"}, + ImageTemplates: []string{registry + "goreleaser/test_multiarch_img_tmpl_error"}, + Goos: "linux", + Goarch: "arm64", + Dockerfile: "testdata/Dockerfile", + Binaries: []string{"mybin"}, }, }, manifests: []config.DockerManifest{ diff --git a/internal/pipe/docker/manifest.go b/internal/pipe/docker/manifest.go index f4a1d614ca6..0321db36e76 100644 --- a/internal/pipe/docker/manifest.go +++ b/internal/pipe/docker/manifest.go @@ -27,10 +27,10 @@ func (ManifestPipe) Publish(ctx *context.Context) error { } var g = semerrgroup.NewSkipAware(semerrgroup.New(ctx.Parallelism)) for _, manifest := range ctx.Config.DockerManifests { - manifest:=manifest + manifest := manifest g.Go(func() error { - man, err:= tmpl.New(ctx).Apply(manifest.ManifestTemplate) - if err != nil{ + man, err := tmpl.New(ctx).Apply(manifest.ManifestTemplate) + if err != nil { return err } var imgs []string From 9393c316b046a4962869e2cdda28350fe15c18a7 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 28 Nov 2020 00:50:29 -0300 Subject: [PATCH 08/11] fix: diff Signed-off-by: Carlos Alexandro Becker --- internal/pipe/docker/docker_registry_test.go | 49 -------------------- internal/pipe/docker/docker_test.go | 42 +++++++++++++++++ 2 files changed, 42 insertions(+), 49 deletions(-) delete mode 100644 internal/pipe/docker/docker_registry_test.go diff --git a/internal/pipe/docker/docker_registry_test.go b/internal/pipe/docker/docker_registry_test.go deleted file mode 100644 index be966ef5cca..00000000000 --- a/internal/pipe/docker/docker_registry_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package docker - -import ( - "flag" - "os" - "os/exec" - "testing" -) - -var it = flag.Bool("it", false, "push images to docker hub") -var registry = "localhost:5000/" -var altRegistry = "localhost:5050/" - -func TestMain(m *testing.M) { - flag.Parse() - if *it { - registry = "docker.io/" - } - os.Exit(m.Run()) -} - -func start(t *testing.T) { - if *it { - return - } - if out, err := exec.Command( - "docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2", - ).CombinedOutput(); err != nil { - t.Log("failed to start docker registry", string(out), err) - t.FailNow() - } - if out, err := exec.Command( - "docker", "run", "-d", "-p", "5050:5000", "--name", "alt_registry", "registry:2", - ).CombinedOutput(); err != nil { - t.Log("failed to start alternate docker registry", string(out), err) - t.FailNow() - } -} - -func killAndRm(t *testing.T) { - if *it { - return - } - t.Log("killing registry") - _ = exec.Command("docker", "kill", "registry").Run() - _ = exec.Command("docker", "rm", "registry").Run() - _ = exec.Command("docker", "kill", "alt_registry").Run() - _ = exec.Command("docker", "rm", "alt_registry").Run() -} diff --git a/internal/pipe/docker/docker_test.go b/internal/pipe/docker/docker_test.go index a9db893fe22..c06da56986f 100644 --- a/internal/pipe/docker/docker_test.go +++ b/internal/pipe/docker/docker_test.go @@ -1,6 +1,7 @@ package docker import ( + "flag" "fmt" "io/ioutil" "os" @@ -18,6 +19,47 @@ import ( "github.com/stretchr/testify/require" ) +var it = flag.Bool("it", false, "push images to docker hub") +var registry = "localhost:5000/" +var altRegistry = "localhost:5050/" + +func TestMain(m *testing.M) { + flag.Parse() + if *it { + registry = "docker.io/" + } + os.Exit(m.Run()) +} + +func start(t *testing.T) { + if *it { + return + } + if out, err := exec.Command( + "docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2", + ).CombinedOutput(); err != nil { + t.Log("failed to start docker registry", string(out), err) + t.FailNow() + } + if out, err := exec.Command( + "docker", "run", "-d", "-p", "5050:5000", "--name", "alt_registry", "registry:2", + ).CombinedOutput(); err != nil { + t.Log("failed to start alternate docker registry", string(out), err) + t.FailNow() + } +} + +func killAndRm(t *testing.T) { + if *it { + return + } + t.Log("killing registry") + _ = exec.Command("docker", "kill", "registry").Run() + _ = exec.Command("docker", "rm", "registry").Run() + _ = exec.Command("docker", "kill", "alt_registry").Run() + _ = exec.Command("docker", "rm", "alt_registry").Run() +} + // TODO: this test is too big... split in smaller tests? Mainly the manifest ones... func TestRunPipe(t *testing.T) { type errChecker func(*testing.T, error) From 1befa43a90a8c68187328c0e7a660906403ba946 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 28 Nov 2020 01:00:10 -0300 Subject: [PATCH 09/11] ci: enable experimental Signed-off-by: Carlos Alexandro Becker --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c362e7725d..c1d2945decd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,8 @@ on: jobs: goreleaser: runs-on: ubuntu-latest + env: + DOCKER_CLI_EXPERIMENTAL: "enabled" steps: - name: Checkout From 1c468911ee3ade5960a7240324473c04869482dd Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 28 Nov 2020 01:42:42 -0300 Subject: [PATCH 10/11] ci: multi-arch goreleaser images Signed-off-by: Carlos Alexandro Becker --- .goreleaser.yml | 106 +++++++++++++++++++++++++++++++++++++++--------- Dockerfile | 3 +- Dockerfile.cgo | 3 +- 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 10cf16535c6..7d876fc967b 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -35,12 +35,12 @@ changelog: - go mod tidy dockers: - image_templates: - - 'goreleaser/goreleaser:{{ .Tag }}-cgo' - - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-cgo' - - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-cgo' - - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-cgo' - - 'goreleaser/goreleaser:latest-cgo' - - 'ghcr.io/goreleaser/goreleaser:latest-cgo' + - 'goreleaser/goreleaser:{{ .Tag }}-cgo-amd64' + - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-cgo-amd64' + - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-cgo-amd64' + - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-cgo-amd64' + - 'goreleaser/goreleaser:latest-cgo-amd64' + - 'ghcr.io/goreleaser/goreleaser:latest-cgo-amd64' dockerfile: Dockerfile.cgo binaries: - goreleaser @@ -50,15 +50,61 @@ dockers: - "--label=org.opencontainers.image.name={{.ProjectName}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source={{.GitURL}}" + - "--build-arg" + - "ARCH=amd64" + extra_files: + - scripts/entrypoint.sh +- image_templates: + - 'goreleaser/goreleaser:{{ .Tag }}-cgo-arm64v8' + - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-cgo-arm64v8' + - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-cgo-arm64v8' + - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-cgo-arm64v8' + - 'goreleaser/goreleaser:latest-cgo-arm64v8' + - 'ghcr.io/goreleaser/goreleaser:latest-cgo-arm64v8' + dockerfile: Dockerfile.cgo + binaries: + - goreleaser + build_flag_templates: + - "--pull" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.name={{.ProjectName}}" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source={{.GitURL}}" + - "--build-arg" + - "ARCH=arm64v8" + goarch: arm64 + extra_files: + - scripts/entrypoint.sh +- image_templates: + - 'goreleaser/goreleaser:{{ .Tag }}-amd64' + - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-amd64' + - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-amd64' + - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-amd64' + - 'goreleaser/goreleaser:latest-amd64' + - 'ghcr.io/goreleaser/goreleaser:latest-amd64' + dockerfile: Dockerfile + binaries: + - goreleaser + build_flag_templates: + - "--pull" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.name={{.ProjectName}}" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source={{.GitURL}}" + - "--build-arg" + - "ARCH=amd64" extra_files: - scripts/entrypoint.sh - image_templates: - - 'goreleaser/goreleaser:{{ .Tag }}' - - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}' - - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}' - - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}' - - 'goreleaser/goreleaser:latest' - - 'ghcr.io/goreleaser/goreleaser:latest' + - 'goreleaser/goreleaser:{{ .Tag }}-arm64v8' + - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-arm64v8' + - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-arm64v8' + - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-arm64v8' + - 'goreleaser/goreleaser:latest-arm64v8' + - 'ghcr.io/goreleaser/goreleaser:latest-arm64v8' dockerfile: Dockerfile binaries: - goreleaser @@ -69,16 +115,36 @@ dockers: - "--label=org.opencontainers.image.revision={{.FullCommit}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.source={{.GitURL}}" - - "--label=com.github.actions.name={{.ProjectName}}" - - "--label=com.github.actions.description=Deliver Go binaries as fast and easily as possible" - - "--label=com.github.actions.icon=terminal" - - "--label=com.github.actions.color=blue" - - "--label=repository=http://github.com/goreleaser/goreleaser" - - "--label=homepage=http://goreleaser.com" - - "--label=maintainer=Carlos Becker " - + - "--build-arg" + - "ARCH=arm64v8" + goarch: arm64 extra_files: - scripts/entrypoint.sh +docker_manifests: +- manifest_template: 'goreleaser/goreleaser:{{ .Tag }}' + image_templates: + - 'goreleaser/goreleaser:{{ .Tag }}-amd64' + - 'goreleaser/goreleaser:{{ .Tag }}-arm64v8' +- manifest_template: 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}' + image_templates: + - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-amd64' + - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-arm64v8' +- manifest_template: 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}' + image_templates: + - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-amd64' + - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-arm64v8' +- manifest_template: 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}' + image_templates: + - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-amd64' + - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-arm64v8' +- manifest_template: 'goreleaser/goreleaser:latest' + image_templates: + - 'goreleaser/goreleaser:latest-amd64' + - 'goreleaser/goreleaser:latest-arm64v8' +- manifest_template: 'ghcr.io/goreleaser/goreleaser:latest' + image_templates: + - 'ghcr.io/goreleaser/goreleaser:latest-amd64' + - 'ghcr.io/goreleaser/goreleaser:latest-arm64v8' archives: - name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' replacements: diff --git a/Dockerfile b/Dockerfile index ac98d739652..35ac1a8a5ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM golang:1.15-alpine +ARG ARCH +FROM ${ARCH}/golang:1.15-alpine RUN apk add --no-cache bash \ curl \ diff --git a/Dockerfile.cgo b/Dockerfile.cgo index 6d1ae7ab9ef..0a282b14ecd 100644 --- a/Dockerfile.cgo +++ b/Dockerfile.cgo @@ -1,4 +1,5 @@ -FROM golang:1.15-alpine +ARG ARCH +FROM ${ARCH}/golang:1.15-alpine RUN apk add --no-cache bash \ build-base \ From 2a24feaee4acac26b7ba1f49f442f23341450d6f Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 28 Nov 2020 14:37:40 -0300 Subject: [PATCH 11/11] refactor: improve code a bit Signed-off-by: Carlos Alexandro Becker --- .goreleaser.yml | 12 +++--- internal/pipe/docker/docker_test.go | 12 +++--- internal/pipe/docker/manifest.go | 49 +++++++++++++++-------- pkg/config/config.go | 8 ++-- www/docs/customization/docker_manifest.md | 4 +- 5 files changed, 51 insertions(+), 34 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 7d876fc967b..592dc12dca2 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -121,27 +121,27 @@ dockers: extra_files: - scripts/entrypoint.sh docker_manifests: -- manifest_template: 'goreleaser/goreleaser:{{ .Tag }}' +- name_template: 'goreleaser/goreleaser:{{ .Tag }}' image_templates: - 'goreleaser/goreleaser:{{ .Tag }}-amd64' - 'goreleaser/goreleaser:{{ .Tag }}-arm64v8' -- manifest_template: 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}' +- name_template: 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}' image_templates: - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-amd64' - 'ghcr.io/goreleaser/goreleaser:{{ .Tag }}-arm64v8' -- manifest_template: 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}' +- name_template: 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}' image_templates: - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-amd64' - 'goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-arm64v8' -- manifest_template: 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}' +- name_template: 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}' image_templates: - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-amd64' - 'ghcr.io/goreleaser/goreleaser:v{{ .Major }}.{{ .Minor }}-arm64v8' -- manifest_template: 'goreleaser/goreleaser:latest' +- name_template: 'goreleaser/goreleaser:latest' image_templates: - 'goreleaser/goreleaser:latest-amd64' - 'goreleaser/goreleaser:latest-arm64v8' -- manifest_template: 'ghcr.io/goreleaser/goreleaser:latest' +- name_template: 'ghcr.io/goreleaser/goreleaser:latest' image_templates: - 'ghcr.io/goreleaser/goreleaser:latest-amd64' - 'ghcr.io/goreleaser/goreleaser:latest-arm64v8' diff --git a/internal/pipe/docker/docker_test.go b/internal/pipe/docker/docker_test.go index c06da56986f..40e5ede2ca8 100644 --- a/internal/pipe/docker/docker_test.go +++ b/internal/pipe/docker/docker_test.go @@ -121,7 +121,7 @@ func TestRunPipe(t *testing.T) { manifests: []config.DockerManifest{ { // XXX: fails if :latest https://github.com/docker/distribution/issues/3100 - ManifestTemplate: registry + "goreleaser/test_multiarch:test", + NameTemplate: registry + "goreleaser/test_multiarch:test", ImageTemplates: []string{ registry + "goreleaser/test_multiarch:test-amd64", registry + "goreleaser/test_multiarch:test-arm64v8", @@ -152,7 +152,7 @@ func TestRunPipe(t *testing.T) { }, manifests: []config.DockerManifest{ { - ManifestTemplate: registry + "goreleaser/test_multiarch_fail:test", + NameTemplate: registry + "goreleaser/test_multiarch_fail:test", ImageTemplates: []string{registry + "goreleaser/test_multiarch_fail:latest-amd64"}, CreateFlags: []string{"--insecure"}, PushFlags: []string{"--insecure"}, @@ -176,7 +176,7 @@ func TestRunPipe(t *testing.T) { }, manifests: []config.DockerManifest{ { - ManifestTemplate: registry + "goreleaser/test_multiarch_manifest_tmpl_error:{{ .Goos }", + NameTemplate: registry + "goreleaser/test_multiarch_manifest_tmpl_error:{{ .Goos }", ImageTemplates: []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"}, }, }, @@ -198,7 +198,7 @@ func TestRunPipe(t *testing.T) { }, manifests: []config.DockerManifest{ { - ManifestTemplate: registry + "goreleaser/test_multiarch_img_tmpl_error", + NameTemplate: registry + "goreleaser/test_multiarch_img_tmpl_error", ImageTemplates: []string{registry + "goreleaser/test_multiarch_img_tmpl_error:{{ .Goos }"}, }, }, @@ -220,7 +220,7 @@ func TestRunPipe(t *testing.T) { }, manifests: []config.DockerManifest{ { - ManifestTemplate: " ", + NameTemplate: " ", ImageTemplates: []string{registry + "goreleaser/test_multiarch_no_mainifest_name"}, }, }, @@ -242,7 +242,7 @@ func TestRunPipe(t *testing.T) { }, manifests: []config.DockerManifest{ { - ManifestTemplate: "ignored", + NameTemplate: "ignored", ImageTemplates: []string{" ", " ", ""}, }, }, diff --git a/internal/pipe/docker/manifest.go b/internal/pipe/docker/manifest.go index 0321db36e76..6eb4da572ea 100644 --- a/internal/pipe/docker/manifest.go +++ b/internal/pipe/docker/manifest.go @@ -9,6 +9,7 @@ import ( "github.com/goreleaser/goreleaser/internal/pipe" "github.com/goreleaser/goreleaser/internal/semerrgroup" "github.com/goreleaser/goreleaser/internal/tmpl" + "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" ) @@ -29,33 +30,49 @@ func (ManifestPipe) Publish(ctx *context.Context) error { for _, manifest := range ctx.Config.DockerManifests { manifest := manifest g.Go(func() error { - man, err := tmpl.New(ctx).Apply(manifest.ManifestTemplate) + name, err := manifestName(ctx, manifest) if err != nil { return err } - var imgs []string - for _, img := range manifest.ImageTemplates { - str, err := tmpl.New(ctx).Apply(img) - if err != nil { - return err - } - imgs = append(imgs, str) - } - if strings.TrimSpace(man) == "" { - return pipe.Skip("manifest name is empty") - } - if strings.TrimSpace(strings.Join(manifest.ImageTemplates, "")) == "" { - return pipe.Skip("manifest has no images") + images, err := manifestImages(ctx, manifest) + if err != nil { + return err } - if err := dockerManifestCreate(ctx, man, imgs, manifest.CreateFlags); err != nil { + if err := dockerManifestCreate(ctx, name, images, manifest.CreateFlags); err != nil { return err } - return dockerManifestPush(ctx, man, manifest.PushFlags) + return dockerManifestPush(ctx, name, manifest.PushFlags) }) } return g.Wait() } +func manifestName(ctx *context.Context, manifest config.DockerManifest) (string, error) { + name, err := tmpl.New(ctx).Apply(manifest.NameTemplate) + if err != nil { + return name, err + } + if strings.TrimSpace(name) == "" { + return name, pipe.Skip("manifest name is empty") + } + return name, nil +} + +func manifestImages(ctx *context.Context, manifest config.DockerManifest) ([]string, error) { + var imgs []string + for _, img := range manifest.ImageTemplates { + str, err := tmpl.New(ctx).Apply(img) + if err != nil { + return []string{}, err + } + imgs = append(imgs, str) + } + if strings.TrimSpace(strings.Join(manifest.ImageTemplates, "")) == "" { + return imgs, pipe.Skip("manifest has no images") + } + return imgs, nil +} + func dockerManifestCreate(ctx *context.Context, manifest string, images, flags []string) error { log.WithField("manifest", manifest).Info("creating docker manifest") var args = []string{"manifest", "create", manifest} diff --git a/pkg/config/config.go b/pkg/config/config.go index 32c67fd5cca..851721ccfab 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -492,10 +492,10 @@ type Docker struct { // DockerManifest config. type DockerManifest struct { - ManifestTemplate string `yaml:"manifest_template,omitempty"` - ImageTemplates []string `yaml:"image_templates,omitempty"` - CreateFlags []string `yaml:"create_flags,omitempty"` - PushFlags []string `yaml:"push_flags,omitempty"` + NameTemplate string `yaml:"name_template,omitempty"` + ImageTemplates []string `yaml:"image_templates,omitempty"` + CreateFlags []string `yaml:"create_flags,omitempty"` + PushFlags []string `yaml:"push_flags,omitempty"` } // Filters config. diff --git a/www/docs/customization/docker_manifest.md b/www/docs/customization/docker_manifest.md index 299688da13f..b35f4cbb27d 100644 --- a/www/docs/customization/docker_manifest.md +++ b/www/docs/customization/docker_manifest.md @@ -26,7 +26,7 @@ docker_manifests: - # Name template for the manifest. # Defaults to empty. - manifest_template: foo/bar:{{ .Version }} + name_template: foo/bar:{{ .Version }} # Image name templates to be added to this manifest. # Defaults to empty. @@ -106,7 +106,7 @@ dockers: - "--build-arg" - "ARCH=arm64v8" docker_manifests: -- manifest_template: foo/bar:{{ .Version }} +- name_template: foo/bar:{{ .Version }} image_templates: - foo/bar:{{ .Version }}-amd64 - foo/bar:{{ .Version }}-arm64v8