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  }