github.com/triarius/goreleaser@v1.12.5/internal/pipe/docker/manifest.go (about) 1 package docker 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/caarlos0/log" 9 "github.com/triarius/goreleaser/internal/artifact" 10 "github.com/triarius/goreleaser/internal/ids" 11 "github.com/triarius/goreleaser/internal/pipe" 12 "github.com/triarius/goreleaser/internal/semerrgroup" 13 "github.com/triarius/goreleaser/internal/tmpl" 14 "github.com/triarius/goreleaser/pkg/config" 15 "github.com/triarius/goreleaser/pkg/context" 16 ) 17 18 // ManifestPipe is an implementation for the docker manifest feature, 19 // allowing to publish multi-arch docker images. 20 type ManifestPipe struct{} 21 22 func (ManifestPipe) String() string { return "docker manifests" } 23 func (ManifestPipe) Skip(ctx *context.Context) bool { 24 return len(ctx.Config.DockerManifests) == 0 || ctx.SkipDocker 25 } 26 27 // Default sets the pipe defaults. 28 func (ManifestPipe) Default(ctx *context.Context) error { 29 ids := ids.New("docker_manifests") 30 for i := range ctx.Config.DockerManifests { 31 manifest := &ctx.Config.DockerManifests[i] 32 if manifest.ID != "" { 33 ids.Inc(manifest.ID) 34 } 35 if manifest.Use == "" { 36 manifest.Use = useDocker 37 } 38 if err := validateManifester(manifest.Use); err != nil { 39 return err 40 } 41 } 42 return ids.Validate() 43 } 44 45 // Publish the docker manifests. 46 func (ManifestPipe) Publish(ctx *context.Context) error { 47 g := semerrgroup.NewSkipAware(semerrgroup.New(1)) 48 for _, manifest := range ctx.Config.DockerManifests { 49 manifest := manifest 50 g.Go(func() error { 51 if strings.TrimSpace(manifest.SkipPush) == "true" { 52 return pipe.Skip("docker_manifest.skip_push is set") 53 } 54 55 if strings.TrimSpace(manifest.SkipPush) == "auto" && ctx.Semver.Prerelease != "" { 56 return pipe.Skip("prerelease detected with 'auto' push, skipping docker manifest") 57 } 58 59 name, err := manifestName(ctx, manifest) 60 if err != nil { 61 return err 62 } 63 64 images, err := manifestImages(ctx, manifest) 65 if err != nil { 66 return err 67 } 68 69 manifester := manifesters[manifest.Use] 70 71 log.WithField("manifest", name).WithField("images", images).Info("creating") 72 if err := manifester.Create(ctx, name, images, manifest.CreateFlags); err != nil { 73 return err 74 } 75 art := &artifact.Artifact{ 76 Type: artifact.DockerManifest, 77 Name: name, 78 Path: name, 79 Extra: map[string]interface{}{}, 80 } 81 if manifest.ID != "" { 82 art.Extra[artifact.ExtraID] = manifest.ID 83 } 84 ctx.Artifacts.Add(art) 85 86 log.WithField("manifest", name).Info("pushing") 87 return manifester.Push(ctx, name, manifest.PushFlags) 88 }) 89 } 90 return g.Wait() 91 } 92 93 func validateManifester(use string) error { 94 valid := make([]string, 0, len(manifesters)) 95 for k := range manifesters { 96 valid = append(valid, k) 97 } 98 for _, s := range valid { 99 if s == use { 100 return nil 101 } 102 } 103 sort.Strings(valid) 104 return fmt.Errorf("docker manifest: invalid use: %s, valid options are %v", use, valid) 105 } 106 107 func manifestName(ctx *context.Context, manifest config.DockerManifest) (string, error) { 108 name, err := tmpl.New(ctx).Apply(manifest.NameTemplate) 109 if err != nil { 110 return name, err 111 } 112 if strings.TrimSpace(name) == "" { 113 return name, pipe.Skip("manifest name is empty") 114 } 115 return name, nil 116 } 117 118 func manifestImages(ctx *context.Context, manifest config.DockerManifest) ([]string, error) { 119 imgs := make([]string, 0, len(manifest.ImageTemplates)) 120 for _, img := range manifest.ImageTemplates { 121 str, err := tmpl.New(ctx).Apply(img) 122 if err != nil { 123 return []string{}, err 124 } 125 imgs = append(imgs, str) 126 } 127 if strings.TrimSpace(strings.Join(manifest.ImageTemplates, "")) == "" { 128 return imgs, pipe.Skip("manifest has no images") 129 } 130 return imgs, nil 131 }