1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-10 03:47:03 +02:00
goreleaser/pipeline/docker/docker.go

208 lines
5.5 KiB
Go
Raw Normal View History

2017-09-12 04:31:00 +02:00
// Package docker provides a Pipe that creates and pushes a Docker image
package docker
import (
"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"
"text/template"
2017-09-12 04:31:00 +02:00
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/apex/log"
"github.com/pkg/errors"
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 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{}
func (Pipe) String() string {
return "creating Docker images"
2017-09-12 04:31:00 +02:00
}
// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
for i := range ctx.Config.Dockers {
if ctx.Config.Dockers[i].TagTemplate == "" {
ctx.Config.Dockers[i].TagTemplate = "{{ .Version }}"
}
}
// only set defaults if there is exacly 1 docker setup in the config file.
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
}
// Run the pipe
func (Pipe) Run(ctx *context.Context) error {
if len(ctx.Config.Dockers) == 0 || ctx.Config.Dockers[0].Image == "" {
return pipeline.Skip("docker section is not configured")
}
_, err := exec.LookPath("docker")
if err != nil {
return ErrNoDocker
}
return doRun(ctx)
}
2017-09-15 02:19:56 +02:00
func doRun(ctx *context.Context) error {
2017-12-17 22:01:58 +02:00
// TODO: could be done in parallel.
2017-09-12 04:42:36 +02:00
for _, docker := range ctx.Config.Dockers {
var binaries = ctx.Artifacts.Filter(
2017-12-17 20:59:54 +02:00
artifact.And(
artifact.ByGoos(docker.Goos),
artifact.ByGoarch(docker.Goarch),
artifact.ByGoarm(docker.Goarm),
2017-12-17 22:01:58 +02:00
// artifact.ByType(artifact.Binary),
2017-12-17 20:59:54 +02:00
func(a artifact.Artifact) bool {
2017-12-17 22:01:58 +02:00
return a.Extra["Binary"] == docker.Binary
2017-12-17 20:59:54 +02:00
},
),
).List()
2017-12-17 22:01:58 +02:00
if len(binaries) == 0 {
log.Warn("no binaries found")
}
for _, binary := range binaries {
var err = process(ctx, docker, binary)
if err != nil && !pipeline.IsSkip(err) {
return err
2017-09-12 04:31:00 +02:00
}
}
}
return nil
}
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
Env map[string]string
}{
Version: ctx.Version,
Tag: ctx.Git.CurrentTag,
Env: ctx.Env,
}
err = t.Execute(&out, data)
return out.String(), err
}
func process(ctx *context.Context, docker config.Docker, artifact artifact.Artifact) error {
var root = filepath.Dir(artifact.Path)
2017-09-12 15:04:56 +02:00
var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile))
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
}
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")
}
if err := dockerPush(ctx, image); err != nil {
2017-09-12 04:31:00 +02:00
return err
}
2017-09-22 14:26:19 +02:00
if !docker.Latest {
return nil
}
if err := dockerTag(image, latest); err != nil {
return err
}
return dockerPush(ctx, 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")
/* #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")
/* #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
}
func dockerPush(ctx *context.Context, image string) error {
2017-09-12 04:42:36 +02:00
log.WithField("image", image).Info("pushing docker image")
/* #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))
ctx.Artifacts.Add(artifact.Artifact{
Type: artifact.DockerImage,
Name: image,
// TODO: are the rest of the params relevant here?
})
2017-09-12 04:31:00 +02:00
return nil
}