github.com/sergiusens/goreleaser@v0.34.3-0.20171009111917-ae6f7c157c5c/pipeline/docker/docker.go (about)

     1  // Package docker provides a Pipe that creates and pushes a Docker image
     2  package docker
     3  
     4  import (
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  
    10  	"github.com/goreleaser/goreleaser/config"
    11  	"github.com/goreleaser/goreleaser/context"
    12  	"github.com/goreleaser/goreleaser/pipeline"
    13  
    14  	"github.com/apex/log"
    15  
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // ErrNoDocker is shown when docker cannot be found in $PATH
    20  var ErrNoDocker = errors.New("docker not present in $PATH")
    21  
    22  // Pipe for docker
    23  type Pipe struct{}
    24  
    25  // Description of the pipe
    26  func (Pipe) Description() string {
    27  	return "Creating Docker images"
    28  }
    29  
    30  // Run the pipe
    31  func (Pipe) Run(ctx *context.Context) error {
    32  	if len(ctx.Config.Dockers) == 0 || ctx.Config.Dockers[0].Image == "" {
    33  		return pipeline.Skip("docker section is not configured")
    34  	}
    35  	_, err := exec.LookPath("docker")
    36  	if err != nil {
    37  		return ErrNoDocker
    38  	}
    39  	return doRun(ctx)
    40  }
    41  
    42  func doRun(ctx *context.Context) error {
    43  	for _, docker := range ctx.Config.Dockers {
    44  		var imagePlatform = docker.Goos + docker.Goarch + docker.Goarm
    45  		for platform, groups := range ctx.Binaries {
    46  			if platform != imagePlatform {
    47  				continue
    48  			}
    49  			for folder, binaries := range groups {
    50  				for _, binary := range binaries {
    51  					if binary.Name != docker.Binary {
    52  						continue
    53  					}
    54  					var err = process(ctx, folder, docker, binary)
    55  					if err != nil && !pipeline.IsSkip(err) {
    56  						return err
    57  					}
    58  				}
    59  			}
    60  		}
    61  	}
    62  	return nil
    63  }
    64  
    65  func process(ctx *context.Context, folder string, docker config.Docker, binary context.Binary) error {
    66  	var root = filepath.Join(ctx.Config.Dist, folder)
    67  	var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile))
    68  	var image = fmt.Sprintf("%s:%s", docker.Image, ctx.Version)
    69  	var latest = fmt.Sprintf("%s:latest", docker.Image)
    70  
    71  	if err := os.Link(docker.Dockerfile, dockerfile); err != nil {
    72  		return errors.Wrap(err, "failed to link dockerfile")
    73  	}
    74  	for _, file := range docker.Files {
    75  		if err := os.Link(file, filepath.Join(root, filepath.Base(file))); err != nil {
    76  			return errors.Wrapf(err, "failed to link extra file '%s'", file)
    77  		}
    78  	}
    79  	if err := dockerBuild(root, dockerfile, image); err != nil {
    80  		return err
    81  	}
    82  	if docker.Latest {
    83  		if err := dockerTag(image, latest); err != nil {
    84  			return err
    85  		}
    86  	}
    87  
    88  	return publish(ctx, docker, image, latest)
    89  }
    90  
    91  func publish(ctx *context.Context, docker config.Docker, image, latest string) error {
    92  	// TODO: improve this so it can log it to stdout
    93  	if !ctx.Publish {
    94  		return pipeline.Skip("--skip-publish is set")
    95  	}
    96  	if ctx.Config.Release.Draft {
    97  		return pipeline.Skip("release is marked as draft")
    98  	}
    99  	if err := dockerPush(image); err != nil {
   100  		return err
   101  	}
   102  	ctx.AddDocker(image)
   103  	if !docker.Latest {
   104  		return nil
   105  	}
   106  	if err := dockerTag(image, latest); err != nil {
   107  		return err
   108  	}
   109  	return dockerPush(latest)
   110  }
   111  
   112  func dockerBuild(root, dockerfile, image string) error {
   113  	log.WithField("image", image).Info("building docker image")
   114  	var cmd = exec.Command("docker", "build", "-f", dockerfile, "-t", image, root)
   115  	log.WithField("cmd", cmd).Debug("executing")
   116  	out, err := cmd.CombinedOutput()
   117  	if err != nil {
   118  		return errors.Wrapf(err, "failed to build docker image: \n%s", string(out))
   119  	}
   120  	log.Debugf("docker build output: \n%s", string(out))
   121  	return nil
   122  }
   123  
   124  func dockerTag(image, tag string) error {
   125  	log.WithField("image", image).WithField("tag", tag).Info("tagging docker image")
   126  	var cmd = exec.Command("docker", "tag", image, tag)
   127  	log.WithField("cmd", cmd).Debug("executing")
   128  	out, err := cmd.CombinedOutput()
   129  	if err != nil {
   130  		return errors.Wrapf(err, "failed to tag docker image: \n%s", string(out))
   131  	}
   132  	log.Debugf("docker tag output: \n%s", string(out))
   133  	return nil
   134  }
   135  
   136  func dockerPush(image string) error {
   137  	log.WithField("image", image).Info("pushing docker image")
   138  	var cmd = exec.Command("docker", "push", image)
   139  	log.WithField("cmd", cmd).Debug("executing")
   140  	out, err := cmd.CombinedOutput()
   141  	if err != nil {
   142  		return errors.Wrapf(err, "failed to push docker image: \n%s", string(out))
   143  	}
   144  	log.Debugf("docker push output: \n%s", string(out))
   145  	return nil
   146  }