You've already forked goreleaser
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:
committed by
Carlos Alexandro Becker
parent
2bc9d6751b
commit
92231bc930
@@ -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"`
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user