1
0
mirror of https://github.com/go-task/task.git synced 2025-11-23 22:24:45 +02:00

Reintroduce template evaluation in variables

With a recent commit, template evaluation for variables in tasks got
broken. This reindroudces temmplate evaluation in taks, and resolves
a series of issues that where previouisly present on master, such as:

- Taskvars did not get evaluated as templates.
- Taskvars would, in contrast to the documentation, _override_ task
  variables for the taks called directly via `Executor.Run(args
  ...string)`. This caused different behaviour in the "default" task
  v.s. other tasks.

This commit ensures:
 - Priority order for variables is now according to the documentation,
   also for the "default" task.
 - Variables gets resolved in a particular order to ensure logical
   access to varaibles on template compile time, and that template
   compilation finds place _before_ resolution of dynamic variables.

This change also allows the following to work:

    task:
      vars:
        A: "52"
        B: "{{.A}}"

However, the following will always replace C with the uncompiled
`{{.A}}`:

    task:
      vars:
        A: "52"
        C: "{{.B}}"
        B: "{{.A}}"

Several tests have also been added to prevent this feature from breaking
again. This should hopefully finally resolve issue #40.
This commit is contained in:
Sindre Røkenes Myren
2017-07-20 09:05:37 +02:00
committed by Sindre Røkenes Myren
parent 55672410cd
commit 31faf05c3a
7 changed files with 249 additions and 106 deletions

View File

@@ -14,6 +14,121 @@ import (
"github.com/stretchr/testify/assert"
)
// fileContentTest provides a basic reusable test-case for running a Taskfile
// and inspect generated files.
type fileContentTest struct {
Dir string
Target string
TrimSpace bool
Files map[string]string
}
func (fct fileContentTest) name(file string) string {
return fmt.Sprintf("target=%q,file=%q", fct.Target, file)
}
func (fct fileContentTest) Run(t *testing.T) {
for f := range fct.Files {
_ = os.Remove(filepath.Join(fct.Dir, f))
}
e := &task.Executor{
Dir: fct.Dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile(), "e.ReadTaskfile()")
assert.NoError(t, e.Run(fct.Target), "e.Run(target)")
for name, expectContent := range fct.Files {
t.Run(fct.name(name), func(t *testing.T) {
b, err := ioutil.ReadFile(filepath.Join(fct.Dir, name))
assert.NoError(t, err, "Error reading file")
s := string(b)
if fct.TrimSpace {
s = strings.TrimSpace(s)
}
assert.Equal(t, expectContent, s, "unexpected file content")
})
}
}
func TestVars(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/vars",
Target: "default",
TrimSpace: true,
Files: map[string]string{
// hello task:
"foo.txt": "foo",
"bar.txt": "bar",
"baz.txt": "baz",
"tmpl_foo.txt": "foo",
"tmpl_bar.txt": "<no value>",
"tmpl_foo2.txt": "foo2",
"tmpl_bar2.txt": "bar2",
"shtmpl_foo.txt": "foo",
"shtmpl_foo2.txt": "foo2",
"nestedtmpl_foo.txt": "{{.FOO}}",
"nestedtmpl_foo2.txt": "foo2",
"foo2.txt": "foo2",
"bar2.txt": "bar2",
"baz2.txt": "baz2",
"tmpl2_foo.txt": "<no value>",
"tmpl2_foo2.txt": "foo2",
"tmpl2_bar.txt": "<no value>",
"tmpl2_bar2.txt": "<no value>",
"shtmpl2_foo.txt": "<no value>",
"shtmpl2_foo2.txt": "foo2",
"nestedtmpl2_foo2.txt": "{{.FOO2}}",
"equal.txt": "foo=bar",
"override.txt": "bar",
},
}
tt.Run(t)
// Ensure identical results when running hello task directly.
tt.Target = "hello"
tt.Run(t)
}
func TestVarsInvalidTmpl(t *testing.T) {
const (
dir = "testdata/vars"
target = "invalid-var-tmpl"
expectError = "template: :1: unexpected EOF"
)
e := &task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile(), "e.ReadTaskfile()")
assert.EqualError(t, e.Run(target), expectError, "e.Run(target)")
}
func TestParams(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/params",
Target: "default",
TrimSpace: false,
Files: map[string]string{
"hello.txt": "Hello\n",
"world.txt": "World\n",
"exclamation.txt": "!\n",
"dep1.txt": "Dependence1\n",
"dep2.txt": "Dependence2\n",
"spanish.txt": "¡Holla mundo!\n",
"spanish-dep.txt": "¡Holla dependencia!\n",
"portuguese.txt": "Olá, mundo!\n",
"portuguese2.txt": "Olá, mundo!\n",
"german.txt": "Welt!\n",
},
}
tt.Run(t)
}
func TestDeps(t *testing.T) {
const dir = "testdata/deps"
@@ -52,48 +167,6 @@ func TestDeps(t *testing.T) {
}
}
func TestVars(t *testing.T) {
const dir = "testdata/vars"
files := []struct {
file string
content string
}{
{"foo.txt", "foo"},
{"bar.txt", "bar"},
{"baz.txt", "baz"},
{"foo2.txt", "foo2"},
{"bar2.txt", "bar2"},
{"baz2.txt", "baz2"},
{"equal.txt", "foo=bar"},
}
for _, f := range files {
_ = os.Remove(filepath.Join(dir, f.file))
}
e := &task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile())
assert.NoError(t, e.Run("default"))
for _, f := range files {
d, err := ioutil.ReadFile(filepath.Join(dir, f.file))
if err != nil {
t.Errorf("Error reading %s: %v", f.file, err)
}
s := string(d)
s = strings.TrimSpace(s)
if s != f.content {
t.Errorf("File content should be %s but is %s", f.content, s)
}
}
}
func TestTaskCall(t *testing.T) {
const dir = "testdata/task_call"
@@ -225,41 +298,6 @@ func TestInit(t *testing.T) {
}
}
func TestParams(t *testing.T) {
const dir = "testdata/params"
var files = []struct {
file string
content string
}{
{"hello.txt", "Hello\n"},
{"world.txt", "World\n"},
{"exclamation.txt", "!\n"},
{"dep1.txt", "Dependence1\n"},
{"dep2.txt", "Dependence2\n"},
{"spanish.txt", "¡Holla mundo!\n"},
{"spanish-dep.txt", "¡Holla dependencia!\n"},
{"portuguese.txt", "Olá, mundo!\n"},
}
for _, f := range files {
_ = os.Remove(filepath.Join(dir, f.file))
}
e := task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile())
assert.NoError(t, e.Run("default"))
for _, f := range files {
content, err := ioutil.ReadFile(filepath.Join(dir, f.file))
assert.NoError(t, err)
assert.Equal(t, f.content, string(content))
}
}
func TestCyclicDep(t *testing.T) {
const dir = "testdata/cyclic"