github.com/amane3/goreleaser@v0.182.0/internal/pipe/docker/manifest.go (about) 1 package docker 2 3 import ( 4 "fmt" 5 "os/exec" 6 "strings" 7 8 "github.com/amane3/goreleaser/internal/artifact" 9 "github.com/amane3/goreleaser/internal/pipe" 10 "github.com/amane3/goreleaser/internal/semerrgroup" 11 "github.com/amane3/goreleaser/internal/tmpl" 12 "github.com/amane3/goreleaser/pkg/config" 13 "github.com/amane3/goreleaser/pkg/context" 14 "github.com/apex/log" 15 ) 16 17 // ManifestPipe is beta implementation of for the docker manifest feature, 18 // allowing to publish multi-arch docker images. 19 type ManifestPipe struct{} 20 21 func (ManifestPipe) String() string { 22 return "docker manifests" 23 } 24 25 // Publish the docker manifests. 26 func (ManifestPipe) Publish(ctx *context.Context) error { 27 if ctx.SkipPublish { 28 return pipe.ErrSkipPublishEnabled 29 } 30 var g = semerrgroup.NewSkipAware(semerrgroup.New(ctx.Parallelism)) 31 for _, manifest := range ctx.Config.DockerManifests { 32 manifest := manifest 33 g.Go(func() error { 34 name, err := manifestName(ctx, manifest) 35 if err != nil { 36 return err 37 } 38 images, err := manifestImages(ctx, manifest) 39 if err != nil { 40 return err 41 } 42 if err := dockerManifestCreate(ctx, name, images, manifest.CreateFlags); err != nil { 43 return err 44 } 45 ctx.Artifacts.Add(&artifact.Artifact{ 46 Type: artifact.DockerManifest, 47 Name: name, 48 Path: name, 49 }) 50 return dockerManifestPush(ctx, name, manifest.PushFlags) 51 }) 52 } 53 return g.Wait() 54 } 55 56 func manifestName(ctx *context.Context, manifest config.DockerManifest) (string, error) { 57 name, err := tmpl.New(ctx).Apply(manifest.NameTemplate) 58 if err != nil { 59 return name, err 60 } 61 if strings.TrimSpace(name) == "" { 62 return name, pipe.Skip("manifest name is empty") 63 } 64 return name, nil 65 } 66 67 func manifestImages(ctx *context.Context, manifest config.DockerManifest) ([]string, error) { 68 var imgs = make([]string, 0, len(manifest.ImageTemplates)) 69 for _, img := range manifest.ImageTemplates { 70 str, err := tmpl.New(ctx).Apply(img) 71 if err != nil { 72 return []string{}, err 73 } 74 imgs = append(imgs, str) 75 } 76 if strings.TrimSpace(strings.Join(manifest.ImageTemplates, "")) == "" { 77 return imgs, pipe.Skip("manifest has no images") 78 } 79 return imgs, nil 80 } 81 82 func dockerManifestCreate(ctx *context.Context, manifest string, images, flags []string) error { 83 log.WithField("manifest", manifest).Info("creating docker manifest") 84 var args = []string{"manifest", "create", manifest} 85 for _, img := range images { 86 args = append(args, "--amend", img) 87 } 88 args = append(args, flags...) 89 /* #nosec */ 90 var cmd = exec.CommandContext(ctx, "docker", args...) 91 log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") 92 out, err := cmd.CombinedOutput() 93 if err != nil { 94 return fmt.Errorf("failed to create docker manifest: %s: \n%s: %w", manifest, string(out), err) 95 } 96 log.Debugf("docker manifest output: \n%s", string(out)) 97 return nil 98 } 99 100 func dockerManifestPush(ctx *context.Context, manifest string, flags []string) error { 101 log.WithField("manifest", manifest).Info("pushing docker manifest") 102 var args = []string{"manifest", "push", manifest} 103 args = append(args, flags...) 104 /* #nosec */ 105 var cmd = exec.CommandContext(ctx, "docker", args...) 106 log.WithField("cmd", cmd.Args).WithField("cwd", cmd.Dir).Debug("running") 107 out, err := cmd.CombinedOutput() 108 if err != nil { 109 return fmt.Errorf("failed to push docker manifest: %s: \n%s: %w", manifest, string(out), err) 110 } 111 log.Debugf("docker manifest output: \n%s", string(out)) 112 return nil 113 }