2017-09-12 04:31:00 +02:00
|
|
|
// Package docker provides a Pipe that creates and pushes a Docker image
|
|
|
|
package docker
|
|
|
|
|
|
|
|
import (
|
2017-12-05 15:19:44 +02:00
|
|
|
"bytes"
|
2017-09-12 04:31:00 +02:00
|
|
|
"fmt"
|
2017-09-12 12:54:43 +02:00
|
|
|
"os"
|
2017-09-12 04:31:00 +02:00
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
2017-12-05 15:31:51 +02:00
|
|
|
"text/template"
|
2017-09-12 04:31:00 +02:00
|
|
|
|
2017-12-03 04:42:52 +02:00
|
|
|
"github.com/apex/log"
|
2017-09-12 04:31:00 +02:00
|
|
|
"github.com/goreleaser/goreleaser/config"
|
|
|
|
"github.com/goreleaser/goreleaser/context"
|
|
|
|
"github.com/goreleaser/goreleaser/pipeline"
|
2017-09-12 04:50:57 +02:00
|
|
|
"github.com/pkg/errors"
|
2017-09-12 04:31:00 +02:00
|
|
|
)
|
|
|
|
|
2017-09-12 05:22:02 +02:00
|
|
|
// ErrNoDocker is shown when docker cannot be found in $PATH
|
|
|
|
var ErrNoDocker = errors.New("docker not present in $PATH")
|
|
|
|
|
|
|
|
// Pipe for docker
|
2017-09-12 04:31:00 +02:00
|
|
|
type Pipe struct{}
|
|
|
|
|
2017-12-02 23:53:19 +02:00
|
|
|
func (Pipe) String() string {
|
|
|
|
return "creating Docker images"
|
2017-09-12 04:31:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the pipe
|
2017-09-12 05:22:02 +02:00
|
|
|
func (Pipe) Run(ctx *context.Context) error {
|
|
|
|
if len(ctx.Config.Dockers) == 0 || ctx.Config.Dockers[0].Image == "" {
|
2017-09-12 04:31:00 +02:00
|
|
|
return pipeline.Skip("docker section is not configured")
|
|
|
|
}
|
2017-09-12 05:22:02 +02:00
|
|
|
_, err := exec.LookPath("docker")
|
|
|
|
if err != nil {
|
|
|
|
return ErrNoDocker
|
|
|
|
}
|
2017-09-15 02:19:56 +02:00
|
|
|
return doRun(ctx)
|
|
|
|
}
|
|
|
|
|
2017-12-02 23:53:19 +02:00
|
|
|
// Default sets the pipe defaults
|
|
|
|
func (Pipe) Default(ctx *context.Context) error {
|
2017-12-05 15:19:44 +02:00
|
|
|
for i := range ctx.Config.Dockers {
|
|
|
|
if ctx.Config.Dockers[i].TagTemplate == "" {
|
|
|
|
ctx.Config.Dockers[i].TagTemplate = "{{ .Version }}"
|
|
|
|
}
|
|
|
|
}
|
2017-12-03 04:42:52 +02:00
|
|
|
// only set defaults if there is exacly 1 docker setup in the config file.
|
2017-12-02 23:53:19 +02:00
|
|
|
if len(ctx.Config.Dockers) != 1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if ctx.Config.Dockers[0].Goos == "" {
|
|
|
|
ctx.Config.Dockers[0].Goos = "linux"
|
|
|
|
}
|
|
|
|
if ctx.Config.Dockers[0].Goarch == "" {
|
|
|
|
ctx.Config.Dockers[0].Goarch = "amd64"
|
|
|
|
}
|
|
|
|
if ctx.Config.Dockers[0].Binary == "" {
|
|
|
|
ctx.Config.Dockers[0].Binary = ctx.Config.Builds[0].Binary
|
|
|
|
}
|
|
|
|
if ctx.Config.Dockers[0].Dockerfile == "" {
|
|
|
|
ctx.Config.Dockers[0].Dockerfile = "Dockerfile"
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-15 02:19:56 +02:00
|
|
|
func doRun(ctx *context.Context) error {
|
2017-09-12 04:42:36 +02:00
|
|
|
for _, docker := range ctx.Config.Dockers {
|
2017-09-12 04:31:00 +02:00
|
|
|
var imagePlatform = docker.Goos + docker.Goarch + docker.Goarm
|
|
|
|
for platform, groups := range ctx.Binaries {
|
|
|
|
if platform != imagePlatform {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for folder, binaries := range groups {
|
|
|
|
for _, binary := range binaries {
|
|
|
|
if binary.Name != docker.Binary {
|
|
|
|
continue
|
|
|
|
}
|
2017-09-15 02:19:56 +02:00
|
|
|
var err = process(ctx, folder, docker, binary)
|
2017-09-12 04:31:00 +02:00
|
|
|
if err != nil && !pipeline.IsSkip(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-12-05 15:19:44 +02:00
|
|
|
func tagName(ctx *context.Context, docker config.Docker) (string, error) {
|
|
|
|
var out bytes.Buffer
|
|
|
|
t, err := template.New("tag").Parse(docker.TagTemplate)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
data := struct {
|
|
|
|
Version, Tag string
|
2017-12-06 01:06:37 +02:00
|
|
|
Env map[string]string
|
2017-12-05 15:19:44 +02:00
|
|
|
}{
|
|
|
|
Version: ctx.Version,
|
|
|
|
Tag: ctx.Git.CurrentTag,
|
2017-12-06 01:06:37 +02:00
|
|
|
Env: ctx.Env,
|
2017-12-05 15:19:44 +02:00
|
|
|
}
|
|
|
|
err = t.Execute(&out, data)
|
|
|
|
return out.String(), err
|
|
|
|
}
|
|
|
|
|
2017-09-15 02:19:56 +02:00
|
|
|
func process(ctx *context.Context, folder string, docker config.Docker, binary context.Binary) error {
|
2017-09-12 04:31:00 +02:00
|
|
|
var root = filepath.Join(ctx.Config.Dist, folder)
|
2017-09-12 15:04:56 +02:00
|
|
|
var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile))
|
2017-12-05 15:19:44 +02:00
|
|
|
tag, err := tagName(ctx, docker)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var image = fmt.Sprintf("%s:%s", docker.Image, tag)
|
2017-09-15 01:16:49 +02:00
|
|
|
var latest = fmt.Sprintf("%s:latest", docker.Image)
|
2017-09-12 04:31:00 +02:00
|
|
|
|
2017-09-12 12:54:43 +02:00
|
|
|
if err := os.Link(docker.Dockerfile, dockerfile); err != nil {
|
|
|
|
return errors.Wrap(err, "failed to link dockerfile")
|
2017-09-12 04:31:00 +02:00
|
|
|
}
|
2017-09-26 00:10:04 +02:00
|
|
|
for _, file := range docker.Files {
|
|
|
|
if err := os.Link(file, filepath.Join(root, filepath.Base(file))); err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to link extra file '%s'", file)
|
|
|
|
}
|
|
|
|
}
|
2017-09-12 15:04:56 +02:00
|
|
|
if err := dockerBuild(root, dockerfile, image); err != nil {
|
2017-09-12 04:31:00 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-09-15 01:16:49 +02:00
|
|
|
if docker.Latest {
|
|
|
|
if err := dockerTag(image, latest); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2017-09-12 12:54:43 +02:00
|
|
|
|
2017-09-26 23:50:00 +02:00
|
|
|
return publish(ctx, docker, image, latest)
|
|
|
|
}
|
|
|
|
|
|
|
|
func publish(ctx *context.Context, docker config.Docker, image, latest string) error {
|
2017-09-22 14:26:19 +02:00
|
|
|
// TODO: improve this so it can log it to stdout
|
2017-09-12 04:31:00 +02:00
|
|
|
if !ctx.Publish {
|
|
|
|
return pipeline.Skip("--skip-publish is set")
|
|
|
|
}
|
2017-09-12 05:29:12 +02:00
|
|
|
if ctx.Config.Release.Draft {
|
|
|
|
return pipeline.Skip("release is marked as draft")
|
|
|
|
}
|
2017-09-12 04:31:00 +02:00
|
|
|
if err := dockerPush(image); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-09-16 20:31:20 +02:00
|
|
|
ctx.AddDocker(image)
|
2017-09-22 14:26:19 +02:00
|
|
|
if !docker.Latest {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := dockerTag(image, latest); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return dockerPush(latest)
|
2017-09-12 04:31:00 +02:00
|
|
|
}
|
|
|
|
|
2017-09-12 15:04:56 +02:00
|
|
|
func dockerBuild(root, dockerfile, image string) error {
|
2017-09-12 04:42:36 +02:00
|
|
|
log.WithField("image", image).Info("building docker image")
|
2017-11-26 16:09:12 +02:00
|
|
|
/* #nosec */
|
2017-09-12 15:04:56 +02:00
|
|
|
var cmd = exec.Command("docker", "build", "-f", dockerfile, "-t", image, root)
|
2017-09-12 04:31:00 +02:00
|
|
|
log.WithField("cmd", cmd).Debug("executing")
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to build docker image: \n%s", string(out))
|
|
|
|
}
|
|
|
|
log.Debugf("docker build output: \n%s", string(out))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-15 01:16:49 +02:00
|
|
|
func dockerTag(image, tag string) error {
|
2017-09-15 02:35:11 +02:00
|
|
|
log.WithField("image", image).WithField("tag", tag).Info("tagging docker image")
|
2017-11-26 16:09:12 +02:00
|
|
|
/* #nosec */
|
2017-09-15 01:16:49 +02:00
|
|
|
var cmd = exec.Command("docker", "tag", image, tag)
|
|
|
|
log.WithField("cmd", cmd).Debug("executing")
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to tag docker image: \n%s", string(out))
|
|
|
|
}
|
|
|
|
log.Debugf("docker tag output: \n%s", string(out))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-22 14:44:15 +02:00
|
|
|
func dockerPush(image string) error {
|
2017-09-12 04:42:36 +02:00
|
|
|
log.WithField("image", image).Info("pushing docker image")
|
2017-11-26 16:09:12 +02:00
|
|
|
/* #nosec */
|
2017-09-12 04:31:00 +02:00
|
|
|
var cmd = exec.Command("docker", "push", image)
|
|
|
|
log.WithField("cmd", cmd).Debug("executing")
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to push docker image: \n%s", string(out))
|
|
|
|
}
|
|
|
|
log.Debugf("docker push output: \n%s", string(out))
|
|
|
|
return nil
|
|
|
|
}
|