1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-06 03:13:48 +02:00

refactor: global single name templating package

This commit is contained in:
Carlos Alexandro Becker 2018-07-08 20:47:30 -07:00
parent 8f675d8d91
commit fc80e6b799
No known key found for this signature in database
GPG Key ID: E61E2F7DC14AB940
13 changed files with 269 additions and 282 deletions

View File

@ -9,7 +9,7 @@ import (
"github.com/google/go-github/github"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/nametemplate"
"github.com/goreleaser/goreleaser/internal/tmpl"
"golang.org/x/oauth2"
)
@ -90,7 +90,7 @@ func (c *githubClient) CreateFile(
func (c *githubClient) CreateRelease(ctx *context.Context, body string) (int64, error) {
var release *github.RepositoryRelease
title, err := nametemplate.Apply(ctx, ctx.Config.Release.NameTemplate)
title, err := tmpl.New(ctx).Apply(ctx.Config.Release.NameTemplate)
if err != nil {
return 0, err
}

View File

@ -1,62 +0,0 @@
// Package filenametemplate contains the code used to template names of
// goreleaser's packages and archives.
package filenametemplate
import (
"bytes"
"text/template"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
)
// Fields contains all accepted fields in the template string
type Fields struct {
Version string
Tag string
ProjectName string
Env map[string]string
Os string
Arch string
Arm string
Binary string
}
// NewFields returns a Fields instances filled with the data provided
func NewFields(ctx *context.Context, replacements map[string]string, artifacts ...artifact.Artifact) Fields {
// This will fail if artifacts is empty - should never be though...
var binary = artifacts[0].Extra["Binary"]
if len(artifacts) > 1 {
binary = ctx.Config.ProjectName
}
return Fields{
Env: ctx.Env,
Version: ctx.Version,
Tag: ctx.Git.CurrentTag,
ProjectName: ctx.Config.ProjectName,
Os: replace(replacements, artifacts[0].Goos),
Arch: replace(replacements, artifacts[0].Goarch),
Arm: replace(replacements, artifacts[0].Goarm),
Binary: binary,
}
}
// Apply applies the given fields to the given template and returns the
// evaluation and any error that might occur.
func Apply(tmpl string, fields Fields) (string, error) {
t, err := template.New(tmpl).Option("missingkey=error").Parse(tmpl)
if err != nil {
return "", err
}
var out bytes.Buffer
err = t.Execute(&out, fields)
return out.String(), err
}
func replace(replacements map[string]string, original string) string {
result := replacements[original]
if result == "" {
return original
}
return result
}

View File

@ -1,85 +0,0 @@
package filenametemplate
import (
"testing"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/stretchr/testify/assert"
)
func TestTemplate(t *testing.T) {
var ctx = context.New(config.Project{
ProjectName: "proj",
})
ctx.Env = map[string]string{
"FOO": "bar",
}
ctx.Version = "1.0.0"
ctx.Git.CurrentTag = "v1.0.0"
var artifact = artifact.Artifact{
Name: "not-this-binary",
Goarch: "amd64",
Goos: "linux",
Goarm: "6",
Extra: map[string]string{
"Binary": "binary",
},
}
var fields = NewFields(ctx, map[string]string{"linux": "Linux"}, artifact)
for expect, tmpl := range map[string]string{
"bar": "{{.Env.FOO}}",
"Linux": "{{.Os}}",
"amd64": "{{.Arch}}",
"6": "{{.Arm}}",
"1.0.0": "{{.Version}}",
"v1.0.0": "{{.Tag}}",
"binary": "{{.Binary}}",
"proj": "{{.ProjectName}}",
} {
tmpl := tmpl
expect := expect
t.Run(expect, func(tt *testing.T) {
tt.Parallel()
result, err := Apply(tmpl, fields)
assert.NoError(tt, err)
assert.Equal(tt, expect, result)
})
}
}
func TestNewFields(t *testing.T) {
var ctx = context.New(config.Project{
ProjectName: "proj",
})
ctx.Version = "1.0.0"
ctx.Git.CurrentTag = "v1.0.0"
var artifact = artifact.Artifact{
Name: "not-this-binary",
Goarch: "amd64",
Goos: "linux",
Goarm: "6",
Extra: map[string]string{
"Binary": "binary",
},
}
var fields = NewFields(ctx, map[string]string{}, artifact, artifact)
assert.Equal(t, "proj", fields.Binary)
}
func TestInvalidTemplate(t *testing.T) {
var ctx = context.New(config.Project{})
var fields = NewFields(ctx, map[string]string{}, artifact.Artifact{})
result, err := Apply("{{.Foo}", fields)
assert.Empty(t, result)
assert.EqualError(t, err, `template: {{.Foo}:1: unexpected "}" in operand`)
}
func TestEnvNotFound(t *testing.T) {
var ctx = context.New(config.Project{})
var fields = NewFields(ctx, map[string]string{}, artifact.Artifact{})
result, err := Apply("{{.Env.FOO}}", fields)
assert.Empty(t, result)
assert.EqualError(t, err, `template: {{.Env.FOO}}:1:6: executing "{{.Env.FOO}}" at <.Env.FOO>: map has no entry for key "FOO"`)
}

View File

@ -1,38 +0,0 @@
// Package nametemplate provides common template function for releases and etc.
package nametemplate
import (
"bytes"
"text/template"
"time"
"github.com/goreleaser/goreleaser/context"
)
// Apply applies the given name template using the context as source.
func Apply(ctx *context.Context, tmpl string) (string, error) {
var out bytes.Buffer
t, err := template.New("release").
Option("missingkey=error").
Funcs(template.FuncMap{
"time": func(s string) string {
return time.Now().UTC().Format(s)
},
}).
Parse(tmpl)
if err != nil {
return "", err
}
err = t.Execute(&out, struct {
ProjectName string
Tag string
Version string
Env map[string]string
}{
ProjectName: ctx.Config.ProjectName,
Tag: ctx.Git.CurrentTag,
Version: ctx.Version,
Env: ctx.Env,
})
return out.String(), err
}

View File

@ -1,70 +0,0 @@
package nametemplate
import (
"testing"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/stretchr/testify/assert"
)
func TestEnv(t *testing.T) {
testCases := []struct {
desc string
in string
out string
}{
{
desc: "with env",
in: "{{ .Env.FOO }}",
out: "BAR",
},
{
desc: "with env",
in: "{{ .Env.BAR }}",
out: "",
},
}
var ctx = context.New(config.Project{})
ctx.Env = map[string]string{
"FOO": "BAR",
}
for _, tC := range testCases {
t.Run(tC.desc, func(t *testing.T) {
out, _ := Apply(ctx, tC.in)
assert.Equal(t, tC.out, out)
})
}
}
func TestFuncMap(t *testing.T) {
var ctx = context.New(config.Project{
ProjectName: "proj",
})
for _, tc := range []struct {
Template string
Name string
}{
{
Template: `{{ time "2006-01-02" }}`,
Name: "YYYY-MM-DD",
},
{
Template: `{{ time "01/02/2006" }}`,
Name: "MM/DD/YYYY",
},
{
Template: `{{ time "01/02/2006" }}`,
Name: "MM/DD/YYYY",
},
} {
out, err := Apply(ctx, tc.Template)
assert.NoError(t, err)
assert.NotEmpty(t, out)
}
}
func TestInvalidTemplate(t *testing.T) {
_, err := Apply(context.New(config.Project{}), "{{{.Foo}")
assert.EqualError(t, err, "template: release:1: unexpected \"{\" in command")
}

92
internal/tmpl/tmpl.go Normal file
View File

@ -0,0 +1,92 @@
// Package tmpl provides templating utilities for goreleser
package tmpl
import (
"bytes"
"text/template"
"time"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/masterminds/semver"
"github.com/pkg/errors"
)
type Template struct {
fields fields
}
type fields struct {
ProjectName string
Version string
Tag string
Commit string
Major int64
Minor int64
Patch int64
Env map[string]string
// artifact-only fields
Os string
Arch string
Arm string
Binary string
}
func New(ctx *context.Context) *Template {
return &Template{
fields: fields{
ProjectName: ctx.Config.ProjectName,
Version: ctx.Version,
Tag: ctx.Git.CurrentTag,
Commit: ctx.Git.Commit,
Env: ctx.Env,
},
}
}
func (t *Template) WithArtifact(a artifact.Artifact, replacements map[string]string) *Template {
var binary = a.Extra["Binary"]
if binary == "" {
binary = t.fields.ProjectName
}
t.fields.Os = replace(replacements, a.Goos)
t.fields.Arch = replace(replacements, a.Goarch)
t.fields.Arm = replace(replacements, a.Goarm)
t.fields.Binary = binary
return t
}
func (t *Template) Apply(s string) (string, error) {
var out bytes.Buffer
tmpl, err := template.New("tmpl").
Option("missingkey=error").
Funcs(template.FuncMap{
"time": func(s string) string {
return time.Now().UTC().Format(s)
},
}).
Parse(s)
if err != nil {
return "", err
}
sv, err := semver.NewVersion(t.fields.Tag)
if err != nil {
return "", errors.Wrap(err, "tmpl")
}
t.fields.Major = sv.Major()
t.fields.Minor = sv.Minor()
t.fields.Patch = sv.Patch()
err = tmpl.Execute(&out, t.fields)
return out.String(), err
}
func replace(replacements map[string]string, original string) string {
result := replacements[original]
if result == "" {
return original
}
return result
}

146
internal/tmpl/tmpl_test.go Normal file
View File

@ -0,0 +1,146 @@
package tmpl
import (
"testing"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/stretchr/testify/assert"
)
func TestWithArtifact(t *testing.T) {
var ctx = context.New(config.Project{
ProjectName: "proj",
})
ctx.Env = map[string]string{
"FOO": "bar",
}
ctx.Version = "1.0.0"
ctx.Git.CurrentTag = "v1.0.0"
var instance = New(ctx).WithArtifact(
artifact.Artifact{
Name: "not-this-binary",
Goarch: "amd64",
Goos: "linux",
Goarm: "6",
Extra: map[string]string{
"Binary": "binary",
},
},
map[string]string{"linux": "Linux"},
)
for expect, tmpl := range map[string]string{
"bar": "{{.Env.FOO}}",
"Linux": "{{.Os}}",
"amd64": "{{.Arch}}",
"6": "{{.Arm}}",
"1.0.0": "{{.Version}}",
"v1.0.0": "{{.Tag}}",
"binary": "{{.Binary}}",
"proj": "{{.ProjectName}}",
} {
tmpl := tmpl
expect := expect
t.Run(expect, func(tt *testing.T) {
tt.Parallel()
result, err := instance.Apply(tmpl)
assert.NoError(tt, err)
assert.Equal(tt, expect, result)
})
}
t.Run("artifact without binary name", func(tt *testing.T) {
tt.Parallel()
result, err := New(ctx).WithArtifact(
artifact.Artifact{
Name: "another-binary",
Goarch: "amd64",
Goos: "linux",
Goarm: "6",
}, map[string]string{},
).Apply("{{ .Binary }}")
assert.NoError(tt, err)
assert.Equal(tt, ctx.Config.ProjectName, result)
})
}
func TestEnv(t *testing.T) {
testCases := []struct {
desc string
in string
out string
}{
{
desc: "with env",
in: "{{ .Env.FOO }}",
out: "BAR",
},
{
desc: "with env",
in: "{{ .Env.BAR }}",
out: "",
},
}
var ctx = context.New(config.Project{})
ctx.Env = map[string]string{
"FOO": "BAR",
}
ctx.Git.CurrentTag = "v1.2.3"
for _, tC := range testCases {
t.Run(tC.desc, func(t *testing.T) {
out, _ := New(ctx).Apply(tC.in)
assert.Equal(t, tC.out, out)
})
}
}
func TestFuncMap(t *testing.T) {
var ctx = context.New(config.Project{
ProjectName: "proj",
})
ctx.Git.CurrentTag = "v1.2.4"
for _, tc := range []struct {
Template string
Name string
}{
{
Template: `{{ time "2006-01-02" }}`,
Name: "YYYY-MM-DD",
},
{
Template: `{{ time "01/02/2006" }}`,
Name: "MM/DD/YYYY",
},
{
Template: `{{ time "01/02/2006" }}`,
Name: "MM/DD/YYYY",
},
} {
out, err := New(ctx).Apply(tc.Template)
assert.NoError(t, err)
assert.NotEmpty(t, out)
}
}
func TestInvalidTemplate(t *testing.T) {
_, err := New(context.New(config.Project{})).Apply("{{{.Foo}")
assert.EqualError(t, err, "template: tmpl:1: unexpected \"{\" in command")
}
func TestEnvNotFound(t *testing.T) {
var ctx = context.New(config.Project{})
ctx.Git.CurrentTag = "v1.2.4"
result, err := New(ctx).Apply("{{.Env.FOO}}")
assert.Empty(t, result)
assert.EqualError(t, err, `template: tmpl:1:6: executing "tmpl" at <.Env.FOO>: map has no entry for key "FOO"`)
}
// This should actually never happen...
func TestInvalidSemver(t *testing.T) {
var ctx = context.New(config.Project{})
ctx.Git.CurrentTag = "v1_2_3"
result, err := New(ctx).Apply("{{.Major}}")
assert.Empty(t, result)
assert.EqualError(t, err, `tmpl: Invalid Semantic Version`)
}

View File

@ -17,7 +17,7 @@ import (
"github.com/goreleaser/archive"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/filenametemplate"
"github.com/goreleaser/goreleaser/internal/tmpl"
)
const (
@ -77,10 +77,9 @@ func (Pipe) Run(ctx *context.Context) error {
func create(ctx *context.Context, binaries []artifact.Artifact) error {
var format = packageFormat(ctx, binaries[0].Goos)
folder, err := filenametemplate.Apply(
ctx.Config.Archive.NameTemplate,
filenametemplate.NewFields(ctx, ctx.Config.Archive.Replacements, binaries...),
)
folder, err := tmpl.New(ctx).
WithArtifact(binaries[0], ctx.Config.Archive.Replacements).
Apply(ctx.Config.Archive.NameTemplate)
if err != nil {
return err
}
@ -125,8 +124,9 @@ func create(ctx *context.Context, binaries []artifact.Artifact) error {
func skip(ctx *context.Context, binaries []artifact.Artifact) error {
for _, binary := range binaries {
log.WithField("binary", binary.Name).Info("skip archiving")
var fields = filenametemplate.NewFields(ctx, ctx.Config.Archive.Replacements, binary)
name, err := filenametemplate.Apply(ctx.Config.Archive.NameTemplate, fields)
name, err := tmpl.New(ctx).
WithArtifact(binary, ctx.Config.Archive.Replacements).
Apply(ctx.Config.Archive.NameTemplate)
if err != nil {
return err
}

View File

@ -20,8 +20,8 @@ import (
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/filenametemplate"
"github.com/goreleaser/goreleaser/internal/linux"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pipeline"
)
@ -101,10 +101,9 @@ func create(ctx *context.Context, format, arch string, binaries []artifact.Artif
if err != nil {
return err
}
name, err := filenametemplate.Apply(
overrided.NameTemplate,
filenametemplate.NewFields(ctx, overrided.Replacements, binaries...),
)
name, err := tmpl.New(ctx).
WithArtifact(binaries[0], overrided.Replacements).
Apply(overrided.NameTemplate)
if err != nil {
return err
}

View File

@ -143,7 +143,7 @@ func TestInvalidNameTemplate(t *testing.T) {
Goarch: "amd64",
Type: artifact.Binary,
})
assert.Contains(t, Pipe{}.Run(ctx).Error(), `template: {{.Foo}:1: unexpected "}" in operand`)
assert.Contains(t, Pipe{}.Run(ctx).Error(), `template: tmpl:1: unexpected "}" in operand`)
}
func TestCreateFileDoesntExist(t *testing.T) {
@ -189,6 +189,8 @@ func TestInvalidConfig(t *testing.T) {
Formats: []string{"deb"},
},
})
ctx.Git.CurrentTag = "v1.2.3"
ctx.Version = "v1.2.3"
ctx.Artifacts.Add(artifact.Artifact{
Name: "mybin",
Path: filepath.Join(dist, "mybin", "mybin"),

View File

@ -13,7 +13,7 @@ import (
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/nametemplate"
"github.com/goreleaser/goreleaser/internal/tmpl"
"golang.org/x/sync/errgroup"
)
@ -74,7 +74,7 @@ func upload(ctx *context.Context, conf config.S3) error {
svc := s3.New(sess, &aws.Config{
Region: aws.String(conf.Region),
})
folder, err := nametemplate.Apply(ctx, conf.Folder)
folder, err := tmpl.New(ctx).Apply(conf.Folder)
if err != nil {
return err
}

View File

@ -16,8 +16,8 @@ import (
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/filenametemplate"
"github.com/goreleaser/goreleaser/internal/linux"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pipeline"
)
@ -110,10 +110,9 @@ func (Pipe) Run(ctx *context.Context) error {
func create(ctx *context.Context, arch string, binaries []artifact.Artifact) error {
var log = log.WithField("arch", arch)
folder, err := filenametemplate.Apply(
ctx.Config.Snapcraft.NameTemplate,
filenametemplate.NewFields(ctx, ctx.Config.Snapcraft.Replacements, binaries...),
)
folder, err := tmpl.New(ctx).
WithArtifact(binaries[0], ctx.Config.Snapcraft.Replacements).
Apply(ctx.Config.Snapcraft.NameTemplate)
if err != nil {
return err
}

View File

@ -55,7 +55,8 @@ func TestRunPipe(t *testing.T) {
Description: "test description",
},
})
ctx.Version = "testversion"
ctx.Git.CurrentTag = "v1.2.3"
ctx.Version = "v1.2.3"
addBinaries(t, ctx, "mybin", dist)
assert.NoError(t, Pipe{}.Run(ctx))
}
@ -75,9 +76,10 @@ func TestRunPipeInvalidNameTemplate(t *testing.T) {
Description: "test description",
},
})
ctx.Version = "testversion"
ctx.Git.CurrentTag = "v1.2.3"
ctx.Version = "v1.2.3"
addBinaries(t, ctx, "mybin", dist)
assert.EqualError(t, Pipe{}.Run(ctx), `template: foo_{{.Arch}:1: unexpected "}" in operand`)
assert.EqualError(t, Pipe{}.Run(ctx), `template: tmpl:1: unexpected "}" in operand`)
}
func TestRunPipeWithName(t *testing.T) {
@ -96,7 +98,8 @@ func TestRunPipeWithName(t *testing.T) {
Description: "test description",
},
})
ctx.Version = "testversion"
ctx.Git.CurrentTag = "v1.2.3"
ctx.Version = "v1.2.3"
addBinaries(t, ctx, "testprojectname", dist)
assert.NoError(t, Pipe{}.Run(ctx))
yamlFile, err := ioutil.ReadFile(filepath.Join(dist, "foo_amd64", "prime", "meta", "snap.yaml"))
@ -129,7 +132,8 @@ func TestRunPipeMetadata(t *testing.T) {
},
},
})
ctx.Version = "testversion"
ctx.Git.CurrentTag = "v1.2.3"
ctx.Version = "v1.2.3"
addBinaries(t, ctx, "mybin", dist)
assert.NoError(t, Pipe{}.Run(ctx))
yamlFile, err := ioutil.ReadFile(filepath.Join(dist, "foo_amd64", "prime", "meta", "snap.yaml"))