1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-17 20:47:50 +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
type Docker struct {
Binary string `yaml:",omitempty"`
Goos string `yaml:",omitempty"`
Goarch string `yaml:",omitempty"`
Goarm string `yaml:",omitempty"`
Image string `yaml:",omitempty"`
Dockerfile string `yaml:",omitempty"`
Latest bool `yaml:",omitempty"`
TagTemplate string `yaml:"tag_template,omitempty"`
Files []string `yaml:"extra_files,omitempty"`
Binary string `yaml:",omitempty"`
Goos string `yaml:",omitempty"`
Goarch string `yaml:",omitempty"`
Goarm string `yaml:",omitempty"`
Image string `yaml:",omitempty"`
Dockerfile string `yaml:",omitempty"`
Latest bool `yaml:",omitempty"`
OldTagTemplate string `yaml:"tag_template,omitempty"`
TagTemplates []string `yaml:"tag_templates,omitempty"`
Files []string `yaml:"extra_files,omitempty"`
// Capture all undefined fields and should be empty after loading
XXX map[string]interface{} `yaml:",inline"`

View File

@ -34,8 +34,12 @@ func (Pipe) String() string {
func (Pipe) Default(ctx *context.Context) error {
for i := range ctx.Config.Dockers {
var docker = &ctx.Config.Dockers[i]
if docker.TagTemplate == "" {
docker.TagTemplate = "{{ .Version }}"
if docker.OldTagTemplate != "" {
// 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 == "" {
docker.Goos = "linux"
@ -43,6 +47,10 @@ func (Pipe) Default(ctx *context.Context) error {
if docker.Goarch == "" {
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.
if len(ctx.Config.Dockers) != 1 {
@ -105,11 +113,9 @@ func doRun(ctx *context.Context) error {
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
t, err := template.New("tag").
Option("missingkey=error").
Parse(docker.TagTemplate)
t, err := template.New("tag").Option("missingkey=error").Parse(tagTemplate)
if err != nil {
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 {
var root = filepath.Dir(artifact.Path)
var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile))
tag, err := tagName(ctx, docker)
if err != nil {
return err
var images []string
for _, tagTemplate := range docker.TagTemplates {
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 {
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)
}
}
if err := dockerBuild(ctx, root, dockerfile, image); err != nil {
if err := dockerBuild(ctx, root, dockerfile, images[0]); err != nil {
return err
}
if docker.Latest {
if err := dockerTag(ctx, image, latest); err != nil {
for _, img := range images[1:] {
if err := dockerTag(ctx, images[0], img); err != nil {
return err
}
}
return publish(ctx, docker, image, latest)
return publish(ctx, docker, images)
}
// 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 {
log.Warn("skipping push because --skip-publish is set")
return nil
}
if err := dockerPush(ctx, docker, image); err != nil {
return err
for _, image := range images {
if err := dockerPush(ctx, docker, image); err != nil {
return err
}
}
if !docker.Latest {
return nil
}
if err := dockerTag(ctx, image, latest); err != nil {
return err
}
return dockerPush(ctx, docker, latest)
return nil
}
func dockerBuild(ctx *context.Context, root, dockerfile, image string) error {

View File

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