1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-09-16 09:26:52 +02:00

feat: support multiple tag_templates for docker

This commit is contained in:
Carlos Alexandro Becker
2018-01-18 17:40:44 -02:00
committed by Carlos Alexandro Becker
parent 2bc9d6751b
commit 92231bc930
3 changed files with 116 additions and 94 deletions

View File

@@ -198,15 +198,16 @@ type Checksum struct {
// Docker image config // Docker image config
type Docker struct { type Docker struct {
Binary string `yaml:",omitempty"` Binary string `yaml:",omitempty"`
Goos string `yaml:",omitempty"` Goos string `yaml:",omitempty"`
Goarch string `yaml:",omitempty"` Goarch string `yaml:",omitempty"`
Goarm string `yaml:",omitempty"` Goarm string `yaml:",omitempty"`
Image string `yaml:",omitempty"` Image string `yaml:",omitempty"`
Dockerfile string `yaml:",omitempty"` Dockerfile string `yaml:",omitempty"`
Latest bool `yaml:",omitempty"` Latest bool `yaml:",omitempty"`
TagTemplate string `yaml:"tag_template,omitempty"` OldTagTemplate string `yaml:"tag_template,omitempty"`
Files []string `yaml:"extra_files,omitempty"` TagTemplates []string `yaml:"tag_templates,omitempty"`
Files []string `yaml:"extra_files,omitempty"`
// Capture all undefined fields and should be empty after loading // Capture all undefined fields and should be empty after loading
XXX map[string]interface{} `yaml:",inline"` XXX map[string]interface{} `yaml:",inline"`

View File

@@ -34,8 +34,12 @@ func (Pipe) String() string {
func (Pipe) Default(ctx *context.Context) error { func (Pipe) Default(ctx *context.Context) error {
for i := range ctx.Config.Dockers { for i := range ctx.Config.Dockers {
var docker = &ctx.Config.Dockers[i] var docker = &ctx.Config.Dockers[i]
if docker.TagTemplate == "" { if docker.OldTagTemplate != "" {
docker.TagTemplate = "{{ .Version }}" // TODO: deprecate docker.tag_template in favor of docker.tag_templates
docker.TagTemplates = append(docker.TagTemplates, docker.OldTagTemplate)
}
if len(docker.TagTemplates) == 0 {
docker.TagTemplates = append(docker.TagTemplates, "{{ .Version }}")
} }
if docker.Goos == "" { if docker.Goos == "" {
docker.Goos = "linux" docker.Goos = "linux"
@@ -43,6 +47,10 @@ func (Pipe) Default(ctx *context.Context) error {
if docker.Goarch == "" { if docker.Goarch == "" {
docker.Goarch = "amd64" docker.Goarch = "amd64"
} }
if docker.Latest {
// TODO: deprecate docker.Latest in favor of multiple tags?
docker.TagTemplates = append(docker.TagTemplates, "latest")
}
} }
// only set defaults if there is exacly 1 docker setup in the config file. // only set defaults if there is exacly 1 docker setup in the config file.
if len(ctx.Config.Dockers) != 1 { if len(ctx.Config.Dockers) != 1 {
@@ -105,11 +113,9 @@ func doRun(ctx *context.Context) error {
return g.Wait() return g.Wait()
} }
func tagName(ctx *context.Context, docker config.Docker) (string, error) { func tagName(ctx *context.Context, tagTemplate string) (string, error) {
var out bytes.Buffer var out bytes.Buffer
t, err := template.New("tag"). t, err := template.New("tag").Option("missingkey=error").Parse(tagTemplate)
Option("missingkey=error").
Parse(docker.TagTemplate)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -128,13 +134,14 @@ func tagName(ctx *context.Context, docker config.Docker) (string, error) {
func process(ctx *context.Context, docker config.Docker, artifact artifact.Artifact) error { func process(ctx *context.Context, docker config.Docker, artifact artifact.Artifact) error {
var root = filepath.Dir(artifact.Path) var root = filepath.Dir(artifact.Path)
var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile)) var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile))
tag, err := tagName(ctx, docker) var images []string
if err != nil { for _, tagTemplate := range docker.TagTemplates {
return err tag, err := tagName(ctx, tagTemplate)
if err != nil {
return errors.Wrapf(err, "failed to execute tag template '%s'", tagTemplate)
}
images = append(images, fmt.Sprintf("%s:%s", docker.Image, tag))
} }
var image = fmt.Sprintf("%s:%s", docker.Image, tag)
var latest = fmt.Sprintf("%s:latest", docker.Image)
if err := os.Link(docker.Dockerfile, dockerfile); err != nil { if err := os.Link(docker.Dockerfile, dockerfile); err != nil {
return errors.Wrap(err, "failed to link dockerfile") return errors.Wrap(err, "failed to link dockerfile")
} }
@@ -143,16 +150,15 @@ func process(ctx *context.Context, docker config.Docker, artifact artifact.Artif
return errors.Wrapf(err, "failed to link extra file '%s'", file) return errors.Wrapf(err, "failed to link extra file '%s'", file)
} }
} }
if err := dockerBuild(ctx, root, dockerfile, image); err != nil { if err := dockerBuild(ctx, root, dockerfile, images[0]); err != nil {
return err return err
} }
if docker.Latest { for _, img := range images[1:] {
if err := dockerTag(ctx, image, latest); err != nil { if err := dockerTag(ctx, images[0], img); err != nil {
return err return err
} }
} }
return publish(ctx, docker, images)
return publish(ctx, docker, image, latest)
} }
// walks the src, recreating dirs and hard-linking files // walks the src, recreating dirs and hard-linking files
@@ -178,21 +184,17 @@ func link(src, dest string) error {
}) })
} }
func publish(ctx *context.Context, docker config.Docker, image, latest string) error { func publish(ctx *context.Context, docker config.Docker, images []string) error {
if !ctx.Publish { if !ctx.Publish {
log.Warn("skipping push because --skip-publish is set") log.Warn("skipping push because --skip-publish is set")
return nil return nil
} }
if err := dockerPush(ctx, docker, image); err != nil { for _, image := range images {
return err if err := dockerPush(ctx, docker, image); err != nil {
return err
}
} }
if !docker.Latest { return nil
return nil
}
if err := dockerTag(ctx, image, latest); err != nil {
return err
}
return dockerPush(ctx, docker, latest)
} }
func dockerBuild(ctx *context.Context, root, dockerfile, image string) error { func dockerBuild(ctx *context.Context, root, dockerfile, image string) error {

View File

@@ -58,13 +58,15 @@ func TestRunPipe(t *testing.T) {
"valid": { "valid": {
publish: true, publish: true,
docker: config.Docker{ docker: config.Docker{
Image: registry + "goreleaser/test_run_pipe", Image: registry + "goreleaser/test_run_pipe",
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Dockerfile: "testdata/Dockerfile", Dockerfile: "testdata/Dockerfile",
Binary: "mybin", Binary: "mybin",
Latest: true, TagTemplates: []string{
TagTemplate: "{{.Tag}}-{{.Env.FOO}}", "{{.Tag}}-{{.Env.FOO}}",
"latest",
},
Files: []string{ Files: []string{
"testdata/extra_file.txt", "testdata/extra_file.txt",
}, },
@@ -78,13 +80,14 @@ func TestRunPipe(t *testing.T) {
"valid_no_latest": { "valid_no_latest": {
publish: true, publish: true,
docker: config.Docker{ docker: config.Docker{
Image: registry + "goreleaser/test_run_pipe", Image: registry + "goreleaser/test_run_pipe",
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Dockerfile: "testdata/Dockerfile", Dockerfile: "testdata/Dockerfile",
Binary: "mybin", Binary: "mybin",
Latest: false, TagTemplates: []string{
TagTemplate: "{{.Version}}", "{{.Version}}",
},
Files: []string{ Files: []string{
"testdata/extra_file.txt", "testdata/extra_file.txt",
}, },
@@ -97,13 +100,15 @@ func TestRunPipe(t *testing.T) {
"valid_dont_publish": { "valid_dont_publish": {
publish: false, publish: false,
docker: config.Docker{ docker: config.Docker{
Image: registry + "goreleaser/test_run_pipe", Image: registry + "goreleaser/test_run_pipe",
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Dockerfile: "testdata/Dockerfile", Dockerfile: "testdata/Dockerfile",
Binary: "mybin", Binary: "mybin",
Latest: true, TagTemplates: []string{
TagTemplate: "{{.Tag}}-{{.Env.FOO}}", "{{.Tag}}-{{.Env.FOO}}",
"latest",
},
Files: []string{ Files: []string{
"testdata/extra_file.txt", "testdata/extra_file.txt",
}, },
@@ -117,51 +122,58 @@ func TestRunPipe(t *testing.T) {
"bad_dockerfile": { "bad_dockerfile": {
publish: true, publish: true,
docker: config.Docker{ docker: config.Docker{
Image: registry + "goreleaser/test_run_pipe", Image: registry + "goreleaser/test_run_pipe",
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Dockerfile: "testdata/Dockerfile.bad", Dockerfile: "testdata/Dockerfile.bad",
Binary: "mybin", Binary: "mybin",
TagTemplate: "{{.Version}}", TagTemplates: []string{
"{{.Version}}",
},
}, },
err: "pull access denied for nope, repository does not exist", err: "pull access denied for nope, repository does not exist",
}, },
"template_error": { "template_error": {
publish: true, publish: true,
docker: config.Docker{ docker: config.Docker{
Image: registry + "goreleaser/test_run_pipe", Image: registry + "goreleaser/test_run_pipe",
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Dockerfile: "testdata/Dockerfile", Dockerfile: "testdata/Dockerfile",
Binary: "mybin", Binary: "mybin",
Latest: true, TagTemplates: []string{
TagTemplate: "{{.Tag}", "{{.Tag}",
},
}, },
err: `template: tag:1: unexpected "}" in operand`, err: `template: tag:1: unexpected "}" in operand`,
}, },
"missing_env_on_template": { "missing_env_on_template": {
publish: true, publish: true,
docker: config.Docker{ docker: config.Docker{
Image: registry + "goreleaser/test_run_pipe", Image: registry + "goreleaser/test_run_pipe",
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Dockerfile: "testdata/Dockerfile", Dockerfile: "testdata/Dockerfile",
Binary: "mybin", Binary: "mybin",
Latest: true, TagTemplates: []string{
TagTemplate: "{{.Env.NOPE}}", "{{.Env.NOPE}}",
},
}, },
err: `template: tag:1:6: executing "tag" at <.Env.NOPE>: map has no entry for key "NOPE"`, err: `template: tag:1:6: executing "tag" at <.Env.NOPE>: map has no entry for key "NOPE"`,
}, },
"no_permissions": { "no_permissions": {
publish: true, publish: true,
docker: config.Docker{ docker: config.Docker{
Image: "docker.io/nope", Image: "docker.io/nope",
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Binary: "mybin", Binary: "mybin",
Dockerfile: "testdata/Dockerfile", Dockerfile: "testdata/Dockerfile",
TagTemplate: "{{.Tag}}", TagTemplates: []string{
Latest: true, "{{.Tag}}",
"latest",
},
Latest: true,
}, },
expect: []string{ expect: []string{
"docker.io/nope:latest", "docker.io/nope:latest",
@@ -172,12 +184,14 @@ func TestRunPipe(t *testing.T) {
"dockerfile_doesnt_exist": { "dockerfile_doesnt_exist": {
publish: true, publish: true,
docker: config.Docker{ docker: config.Docker{
Image: "whatever", Image: "whatever",
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Binary: "mybin", Binary: "mybin",
Dockerfile: "testdata/Dockerfilezzz", Dockerfile: "testdata/Dockerfilezzz",
TagTemplate: "{{.Tag}}", TagTemplates: []string{
"{{.Tag}}",
},
}, },
err: `failed to link dockerfile`, err: `failed to link dockerfile`,
}, },
@@ -191,8 +205,10 @@ func TestRunPipe(t *testing.T) {
Files: []string{ Files: []string{
"testdata/nope.txt", "testdata/nope.txt",
}, },
Dockerfile: "testdata/Dockerfile", Dockerfile: "testdata/Dockerfile",
TagTemplate: "{{.Tag}}", TagTemplates: []string{
"{{.Tag}}",
},
}, },
err: `failed to link extra file 'testdata/nope.txt'`, err: `failed to link extra file 'testdata/nope.txt'`,
}, },
@@ -339,7 +355,9 @@ func TestDefault(t *testing.T) {
assert.Equal(t, "amd64", docker.Goarch) assert.Equal(t, "amd64", docker.Goarch)
assert.Equal(t, ctx.Config.Builds[0].Binary, docker.Binary) assert.Equal(t, ctx.Config.Builds[0].Binary, docker.Binary)
assert.Equal(t, "Dockerfile", docker.Dockerfile) assert.Equal(t, "Dockerfile", docker.Dockerfile)
assert.Equal(t, "{{ .Version }}", docker.TagTemplate) assert.Empty(t, docker.OldTagTemplate)
assert.Equal(t, []string{"{{ .Version }}", "latest"}, docker.TagTemplates)
} }
func TestDefaultNoDockers(t *testing.T) { func TestDefaultNoDockers(t *testing.T) {
@@ -371,7 +389,8 @@ func TestDefaultSet(t *testing.T) {
assert.Equal(t, "windows", docker.Goos) assert.Equal(t, "windows", docker.Goos)
assert.Equal(t, "i386", docker.Goarch) assert.Equal(t, "i386", docker.Goarch)
assert.Equal(t, "bar", docker.Binary) assert.Equal(t, "bar", docker.Binary)
assert.Equal(t, "{{ .Version }}", docker.TagTemplate) assert.Empty(t, docker.OldTagTemplate)
assert.Equal(t, []string{"{{ .Version }}"}, docker.TagTemplates)
assert.Equal(t, "Dockerfile.foo", docker.Dockerfile) assert.Equal(t, "Dockerfile.foo", docker.Dockerfile)
} }