github.com/ahmet2mir/goreleaser@v0.180.3-0.20210927151101-8e5ee5a9b8c5/internal/pipe/docker/manifest.go (about)

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