diff --git a/args/args.go b/args/args.go index bf01b75e..a680e27f 100644 --- a/args/args.go +++ b/args/args.go @@ -6,8 +6,8 @@ import ( "github.com/go-task/task/v3/taskfile" ) -// ParseV3 parses command line argument: tasks and global variables -func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) { +// Parse parses command line argument: tasks and global variables +func Parse(args ...string) ([]taskfile.Call, *taskfile.Vars) { calls := []taskfile.Call{} globals := &taskfile.Vars{} @@ -24,32 +24,6 @@ func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) { return calls, globals } -// ParseV2 parses command line argument: tasks and vars of each task -func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) { - calls := []taskfile.Call{} - globals := &taskfile.Vars{} - - for _, arg := range args { - if !strings.Contains(arg, "=") { - calls = append(calls, taskfile.Call{Task: arg, Direct: true}) - continue - } - - if len(calls) < 1 { - name, value := splitVar(arg) - globals.Set(name, taskfile.Var{Value: value}) - } else { - if calls[len(calls)-1].Vars == nil { - calls[len(calls)-1].Vars = &taskfile.Vars{} - } - name, value := splitVar(arg) - calls[len(calls)-1].Vars.Set(name, taskfile.Var{Value: value}) - } - } - - return calls, globals -} - func splitVar(s string) (string, string) { pair := strings.SplitN(s, "=", 2) return pair[0], pair[1] diff --git a/args/args_test.go b/args/args_test.go index 042e9a9d..d093c3b9 100644 --- a/args/args_test.go +++ b/args/args_test.go @@ -11,7 +11,7 @@ import ( "github.com/go-task/task/v3/taskfile" ) -func TestArgsV3(t *testing.T) { +func TestArgs(t *testing.T) { tests := []struct { Args []string ExpectedCalls []taskfile.Call @@ -97,7 +97,7 @@ func TestArgsV3(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) { - calls, globals := args.ParseV3(test.Args...) + calls, globals := args.Parse(test.Args...) assert.Equal(t, test.ExpectedCalls, calls) if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 { assert.Equal(t, test.ExpectedGlobals.Keys(), globals.Keys()) @@ -106,114 +106,3 @@ func TestArgsV3(t *testing.T) { }) } } - -func TestArgsV2(t *testing.T) { - tests := []struct { - Args []string - ExpectedCalls []taskfile.Call - ExpectedGlobals *taskfile.Vars - }{ - { - Args: []string{"task-a", "task-b", "task-c"}, - ExpectedCalls: []taskfile.Call{ - {Task: "task-a", Direct: true}, - {Task: "task-b", Direct: true}, - {Task: "task-c", Direct: true}, - }, - }, - { - Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"}, - ExpectedCalls: []taskfile.Call{ - { - Task: "task-a", - Direct: true, - Vars: &taskfile.Vars{ - OrderedMap: orderedmap.FromMapWithOrder( - map[string]taskfile.Var{ - "FOO": {Value: "bar"}, - }, - []string{"FOO"}, - ), - }, - }, - {Task: "task-b", Direct: true}, - { - Task: "task-c", - Direct: true, - Vars: &taskfile.Vars{ - OrderedMap: orderedmap.FromMapWithOrder( - map[string]taskfile.Var{ - "BAR": {Value: "baz"}, - "BAZ": {Value: "foo"}, - }, - []string{"BAR", "BAZ"}, - ), - }, - }, - }, - }, - { - Args: []string{"task-a", "CONTENT=with some spaces"}, - ExpectedCalls: []taskfile.Call{ - { - Task: "task-a", - Direct: true, - Vars: &taskfile.Vars{ - OrderedMap: orderedmap.FromMapWithOrder( - map[string]taskfile.Var{ - "CONTENT": {Value: "with some spaces"}, - }, - []string{"CONTENT"}, - ), - }, - }, - }, - }, - { - Args: []string{"FOO=bar", "task-a", "task-b"}, - ExpectedCalls: []taskfile.Call{ - {Task: "task-a", Direct: true}, - {Task: "task-b", Direct: true}, - }, - ExpectedGlobals: &taskfile.Vars{ - OrderedMap: orderedmap.FromMapWithOrder( - map[string]taskfile.Var{ - "FOO": {Value: "bar"}, - }, - []string{"FOO"}, - ), - }, - }, - { - Args: nil, - ExpectedCalls: []taskfile.Call{}, - }, - { - Args: []string{}, - ExpectedCalls: []taskfile.Call{}, - }, - { - Args: []string{"FOO=bar", "BAR=baz"}, - ExpectedCalls: []taskfile.Call{}, - ExpectedGlobals: &taskfile.Vars{ - OrderedMap: orderedmap.FromMapWithOrder( - map[string]taskfile.Var{ - "FOO": {Value: "bar"}, - "BAR": {Value: "baz"}, - }, - []string{"FOO", "BAR"}, - ), - }, - }, - } - - for i, test := range tests { - t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) { - calls, globals := args.ParseV2(test.Args...) - assert.Equal(t, test.ExpectedCalls, calls) - if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 { - assert.Equal(t, test.ExpectedGlobals, globals) - } - }) - } -} diff --git a/cmd/task/task.go b/cmd/task/task.go index f862d6b6..440e596f 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -300,11 +300,7 @@ func run() error { return err } - if e.Taskfile.Version.Compare(taskfile.V3) >= 0 { - calls, globals = args.ParseV3(tasksAndVars...) - } else { - calls, globals = args.ParseV2(tasksAndVars...) - } + calls, globals = args.Parse(tasksAndVars...) // If there are no calls, run the default task instead if len(calls) == 0 { diff --git a/docs/docs/deprecations/version_2_schema.md b/docs/docs/deprecations/version_2_schema.md index c6f5471a..7ed1b85d 100644 --- a/docs/docs/deprecations/version_2_schema.md +++ b/docs/docs/deprecations/version_2_schema.md @@ -13,18 +13,21 @@ This deprecation breaks the following functionality: ::: -The Taskfile v2 schema was introduced in March 2018 and replaced by version 3 in -August the following year. Users have had a long time to update and so we feel -that it is time to tidy up the codebase and focus on new functionality instead. +The Taskfile version 2 schema was introduced in March 2018 and replaced by +version 3 in August 2019. In May 2023 [we published a deprecation +notice][deprecation-notice] for the version 2 schema on the basis that the vast +majority of users had already upgraded to version 3 and removing support for +version 2 would allow us to tidy up the codebase and focus on new functionality +instead. -This notice does not mean that we are immediately removing support for version 2 -schemas. However, support will not be extended to future major releases and we -_strongly recommend_ that anybody still using a version 2 schema upgrades to -version 3 as soon as possible. - -A list of changes between version 2 and version 3 are available in the [Task v3 -Release Notes][version-3-release-notes]. +In December 2023, the final version of Task that supports the version 2 schema +([v3.33.0][v3.33.0]) was published and all legacy code was removed from Task's +main branch. To use a more recent version of Task, you will need to ensure that +your Taskfile uses the version 3 schema instead. A list of changes between +version 2 and version 3 are available in the [Task v3 Release Notes][v3.0.0]. -[version-3-release-notes]: https://github.com/go-task/task/releases/tag/v3.0.0 +[v3.0.0]: https://github.com/go-task/task/releases/tag/v3.0.0 +[v3.33.0]: https://github.com/go-task/task/releases/tag/v3.33.0 +[deprecation-notice]: https://github.com/go-task/task/issues/1197 diff --git a/docs/docs/taskfile_versions.md b/docs/docs/taskfile_versions.md index 0391d892..989e17c2 100644 --- a/docs/docs/taskfile_versions.md +++ b/docs/docs/taskfile_versions.md @@ -82,8 +82,8 @@ tasks: :::caution -v2 schema support is [deprecated][deprecate-version-2-schema] and will be -removed in a future release. +v2 schemas are [no longer supported by the latest version of +Task][deprecate-version-2-schema]. ::: @@ -106,8 +106,8 @@ Please check the [documentation][includes] :::caution -v2 schema support is [deprecated][deprecate-version-2-schema] and will be -removed in a future release. +v2 schemas are [no longer supported by the latest version of +Task][deprecate-version-2-schema]. ::: @@ -125,8 +125,8 @@ includes: :::caution -v2 schema support is [deprecated][deprecate-version-2-schema] and will be -removed in a future release. +v2 schemas are [no longer supported by the latest version of +Task][deprecate-version-2-schema]. ::: @@ -170,8 +170,8 @@ tasks: :::caution -v2 schema support is [deprecated][deprecate-version-2-schema] and will be -removed in a future release. +v2 schemas are [no longer supported by the latest version of +Task][deprecate-version-2-schema]. ::: @@ -256,7 +256,7 @@ The variable priority order was also different: 4. `Taskvars.yml` variables -[deprecate-version-2-schema]: https://github.com/go-task/task/issues/1197 +[deprecate-version-2-schema]: /deprecations/version-2-schema/ [output]: /usage#output-syntax [ignore_errors]: /usage#ignore-errors [includes]: /usage#including-other-taskfiles diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go index 14ec1be7..462ca9e6 100644 --- a/internal/compiler/compiler.go +++ b/internal/compiler/compiler.go @@ -1,15 +1,207 @@ package compiler import ( + "bytes" + "context" + "fmt" + "strings" + "sync" + + "github.com/go-task/task/v3/internal/execext" + "github.com/go-task/task/v3/internal/filepathext" + "github.com/go-task/task/v3/internal/logger" + "github.com/go-task/task/v3/internal/templater" + "github.com/go-task/task/v3/internal/version" "github.com/go-task/task/v3/taskfile" ) -// Compiler handles compilation of a task before its execution. -// E.g. variable merger, template processing, etc. -type Compiler interface { - GetTaskfileVariables() (*taskfile.Vars, error) - GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) - FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) - HandleDynamicVar(v taskfile.Var, dir string) (string, error) - ResetCache() +type Compiler struct { + Dir string + UserWorkingDir string + + TaskfileEnv *taskfile.Vars + TaskfileVars *taskfile.Vars + + Logger *logger.Logger + + dynamicCache map[string]string + muDynamicCache sync.Mutex +} + +func (c *Compiler) GetTaskfileVariables() (*taskfile.Vars, error) { + return c.getVariables(nil, nil, true) +} + +func (c *Compiler) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) { + return c.getVariables(t, &call, true) +} + +func (c *Compiler) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) { + return c.getVariables(t, &call, false) +} + +func (c *Compiler) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) { + result := GetEnviron() + if t != nil { + specialVars, err := c.getSpecialVars(t) + if err != nil { + return nil, err + } + for k, v := range specialVars { + result.Set(k, taskfile.Var{Value: v}) + } + } + + getRangeFunc := func(dir string) func(k string, v taskfile.Var) error { + return func(k string, v taskfile.Var) error { + tr := templater.Templater{Vars: result} + // Replace values + newVar := taskfile.Var{} + switch value := v.Value.(type) { + case string: + newVar.Value = tr.Replace(value) + default: + newVar.Value = value + } + newVar.Sh = tr.Replace(v.Sh) + newVar.Dir = v.Dir + // If the variable should not be evaluated, but is nil, set it to an empty string + // This stops empty interface errors when using the templater to replace values later + if !evaluateShVars && newVar.Value == nil { + result.Set(k, taskfile.Var{Value: ""}) + return nil + } + // If the variable should not be evaluated and it is set, we can set it and return + if !evaluateShVars { + result.Set(k, taskfile.Var{Value: newVar.Value}) + return nil + } + // Now we can check for errors since we've handled all the cases when we don't want to evaluate + if err := tr.Err(); err != nil { + return err + } + // If the variable is not dynamic, we can set it and return + if newVar.Value != nil || newVar.Sh == "" { + result.Set(k, taskfile.Var{Value: newVar.Value}) + return nil + } + // If the variable is dynamic, we need to resolve it first + static, err := c.HandleDynamicVar(newVar, dir) + if err != nil { + return err + } + result.Set(k, taskfile.Var{Value: static}) + return nil + } + } + rangeFunc := getRangeFunc(c.Dir) + + var taskRangeFunc func(k string, v taskfile.Var) error + if t != nil { + // NOTE(@andreynering): We're manually joining these paths here because + // this is the raw task, not the compiled one. + tr := templater.Templater{Vars: result} + dir := tr.Replace(t.Dir) + if err := tr.Err(); err != nil { + return nil, err + } + dir = filepathext.SmartJoin(c.Dir, dir) + taskRangeFunc = getRangeFunc(dir) + } + + if err := c.TaskfileEnv.Range(rangeFunc); err != nil { + return nil, err + } + if err := c.TaskfileVars.Range(rangeFunc); err != nil { + return nil, err + } + if t != nil { + if err := t.IncludedTaskfileVars.Range(taskRangeFunc); err != nil { + return nil, err + } + if err := t.IncludeVars.Range(rangeFunc); err != nil { + return nil, err + } + } + + if t == nil || call == nil { + return result, nil + } + + if err := call.Vars.Range(rangeFunc); err != nil { + return nil, err + } + if err := t.Vars.Range(taskRangeFunc); err != nil { + return nil, err + } + + return result, nil +} + +func (c *Compiler) HandleDynamicVar(v taskfile.Var, dir string) (string, error) { + c.muDynamicCache.Lock() + defer c.muDynamicCache.Unlock() + + if c.dynamicCache == nil { + c.dynamicCache = make(map[string]string, 30) + } + if result, ok := c.dynamicCache[v.Sh]; ok { + return result, nil + } + + // NOTE(@andreynering): If a var have a specific dir, use this instead + if v.Dir != "" { + dir = v.Dir + } + + var stdout bytes.Buffer + opts := &execext.RunCommandOptions{ + Command: v.Sh, + Dir: dir, + Stdout: &stdout, + Stderr: c.Logger.Stderr, + } + if err := execext.RunCommand(context.Background(), opts); err != nil { + return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err) + } + + // Trim a single trailing newline from the result to make most command + // output easier to use in shell commands. + result := strings.TrimSuffix(stdout.String(), "\r\n") + result = strings.TrimSuffix(result, "\n") + + c.dynamicCache[v.Sh] = result + c.Logger.VerboseErrf(logger.Magenta, "task: dynamic variable: %q result: %q\n", v.Sh, result) + + return result, nil +} + +// ResetCache clear the dymanic variables cache +func (c *Compiler) ResetCache() { + c.muDynamicCache.Lock() + defer c.muDynamicCache.Unlock() + + c.dynamicCache = nil +} + +func (c *Compiler) getSpecialVars(t *taskfile.Task) (map[string]string, error) { + taskfileDir, err := c.getTaskfileDir(t) + if err != nil { + return nil, err + } + + return map[string]string{ + "TASK": t.Task, + "ROOT_DIR": c.Dir, + "TASKFILE_DIR": taskfileDir, + "USER_WORKING_DIR": c.UserWorkingDir, + "TASK_VERSION": version.GetVersion(), + }, nil +} + +func (c *Compiler) getTaskfileDir(t *taskfile.Task) (string, error) { + if t.IncludedTaskfile != nil { + return t.IncludedTaskfile.FullDirPath() + } + return c.Dir, nil } diff --git a/internal/compiler/v2/compiler_v2.go b/internal/compiler/v2/compiler_v2.go deleted file mode 100644 index ee511d21..00000000 --- a/internal/compiler/v2/compiler_v2.go +++ /dev/null @@ -1,138 +0,0 @@ -package v2 - -import ( - "bytes" - "context" - "fmt" - "strings" - "sync" - - "github.com/go-task/task/v3/internal/compiler" - "github.com/go-task/task/v3/internal/execext" - "github.com/go-task/task/v3/internal/logger" - "github.com/go-task/task/v3/internal/templater" - "github.com/go-task/task/v3/taskfile" -) - -var _ compiler.Compiler = &CompilerV2{} - -type CompilerV2 struct { - Dir string - - Taskvars *taskfile.Vars - TaskfileVars *taskfile.Vars - - Expansions int - - Logger *logger.Logger - - dynamicCache map[string]string - muDynamicCache sync.Mutex -} - -func (c *CompilerV2) GetTaskfileVariables() (*taskfile.Vars, error) { - return &taskfile.Vars{}, nil -} - -// FastGetVariables is a no-op on v2 -func (c *CompilerV2) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) { - return c.GetVariables(t, call) -} - -// GetVariables returns fully resolved variables following the priority order: -// 1. Task variables -// 2. Call variables -// 3. Taskfile variables -// 4. Taskvars file variables -// 5. Environment variables -func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) { - vr := varResolver{ - c: c, - vars: compiler.GetEnviron(), - } - vr.vars.Set("TASK", taskfile.Var{Value: t.Task}) - - for _, vars := range []*taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} { - for i := 0; i < c.Expansions; i++ { - vr.merge(vars) - } - } - return vr.vars, vr.err -} - -type varResolver struct { - c *CompilerV2 - vars *taskfile.Vars - err error -} - -func (vr *varResolver) merge(vars *taskfile.Vars) { - if vr.err != nil { - return - } - tr := templater.Templater{Vars: vr.vars} - _ = vars.Range(func(k string, v taskfile.Var) error { - // Replace values - newVar := taskfile.Var{} - switch value := v.Value.(type) { - case string: - newVar.Value = tr.Replace(value) - default: - newVar.Value = value - } - newVar.Sh = tr.Replace(v.Sh) - // If the variable is not dynamic, we can set it and return - if newVar.Value != nil || newVar.Sh == "" { - vr.vars.Set(k, taskfile.Var{Value: newVar.Value}) - return nil - } - // If the variable is dynamic, we need to resolve it first - static, err := vr.c.HandleDynamicVar(newVar, "") - if err != nil { - vr.err = err - return err - } - vr.vars.Set(k, taskfile.Var{Value: static}) - return nil - }) - vr.err = tr.Err() -} - -func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, _ string) (string, error) { - c.muDynamicCache.Lock() - defer c.muDynamicCache.Unlock() - - if c.dynamicCache == nil { - c.dynamicCache = make(map[string]string, 30) - } - if result, ok := c.dynamicCache[v.Sh]; ok { - return result, nil - } - - var stdout bytes.Buffer - opts := &execext.RunCommandOptions{ - Command: v.Sh, - Stdout: &stdout, - Stderr: c.Logger.Stderr, - } - if err := execext.RunCommand(context.Background(), opts); err != nil { - return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err) - } - - // Trim a single trailing newline from the result to make most command - // output easier to use in shell commands. - result := strings.TrimSuffix(stdout.String(), "\n") - - c.dynamicCache[v.Sh] = result - c.Logger.VerboseErrf(logger.Magenta, "task: dynamic variable: %q result: %q\n", v.Sh, result) - - return result, nil -} - -// ResetCache clear the dymanic variables cache -func (c *CompilerV2) ResetCache() { - c.muDynamicCache.Lock() - defer c.muDynamicCache.Unlock() - - c.dynamicCache = nil -} diff --git a/internal/compiler/v3/compiler_v3.go b/internal/compiler/v3/compiler_v3.go deleted file mode 100644 index 164dd81f..00000000 --- a/internal/compiler/v3/compiler_v3.go +++ /dev/null @@ -1,210 +0,0 @@ -package v3 - -import ( - "bytes" - "context" - "fmt" - "strings" - "sync" - - "github.com/go-task/task/v3/internal/compiler" - "github.com/go-task/task/v3/internal/execext" - "github.com/go-task/task/v3/internal/filepathext" - "github.com/go-task/task/v3/internal/logger" - "github.com/go-task/task/v3/internal/templater" - "github.com/go-task/task/v3/internal/version" - "github.com/go-task/task/v3/taskfile" -) - -var _ compiler.Compiler = &CompilerV3{} - -type CompilerV3 struct { - Dir string - UserWorkingDir string - - TaskfileEnv *taskfile.Vars - TaskfileVars *taskfile.Vars - - Logger *logger.Logger - - dynamicCache map[string]string - muDynamicCache sync.Mutex -} - -func (c *CompilerV3) GetTaskfileVariables() (*taskfile.Vars, error) { - return c.getVariables(nil, nil, true) -} - -func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) { - return c.getVariables(t, &call, true) -} - -func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) { - return c.getVariables(t, &call, false) -} - -func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) { - result := compiler.GetEnviron() - if t != nil { - specialVars, err := c.getSpecialVars(t) - if err != nil { - return nil, err - } - for k, v := range specialVars { - result.Set(k, taskfile.Var{Value: v}) - } - } - - getRangeFunc := func(dir string) func(k string, v taskfile.Var) error { - return func(k string, v taskfile.Var) error { - tr := templater.Templater{Vars: result, RemoveNoValue: true} - // Replace values - newVar := taskfile.Var{} - switch value := v.Value.(type) { - case string: - newVar.Value = tr.Replace(value) - default: - newVar.Value = value - } - newVar.Sh = tr.Replace(v.Sh) - newVar.Dir = v.Dir - // If the variable should not be evaluated, but is nil, set it to an empty string - // This stops empty interface errors when using the templater to replace values later - if !evaluateShVars && newVar.Value == nil { - result.Set(k, taskfile.Var{Value: ""}) - return nil - } - // If the variable should not be evaluated and it is set, we can set it and return - if !evaluateShVars { - result.Set(k, taskfile.Var{Value: newVar.Value}) - return nil - } - // Now we can check for errors since we've handled all the cases when we don't want to evaluate - if err := tr.Err(); err != nil { - return err - } - // If the variable is not dynamic, we can set it and return - if newVar.Value != nil || newVar.Sh == "" { - result.Set(k, taskfile.Var{Value: newVar.Value}) - return nil - } - // If the variable is dynamic, we need to resolve it first - static, err := c.HandleDynamicVar(newVar, dir) - if err != nil { - return err - } - result.Set(k, taskfile.Var{Value: static}) - return nil - } - } - rangeFunc := getRangeFunc(c.Dir) - - var taskRangeFunc func(k string, v taskfile.Var) error - if t != nil { - // NOTE(@andreynering): We're manually joining these paths here because - // this is the raw task, not the compiled one. - tr := templater.Templater{Vars: result, RemoveNoValue: true} - dir := tr.Replace(t.Dir) - if err := tr.Err(); err != nil { - return nil, err - } - dir = filepathext.SmartJoin(c.Dir, dir) - taskRangeFunc = getRangeFunc(dir) - } - - if err := c.TaskfileEnv.Range(rangeFunc); err != nil { - return nil, err - } - if err := c.TaskfileVars.Range(rangeFunc); err != nil { - return nil, err - } - if t != nil { - if err := t.IncludedTaskfileVars.Range(taskRangeFunc); err != nil { - return nil, err - } - if err := t.IncludeVars.Range(rangeFunc); err != nil { - return nil, err - } - } - - if t == nil || call == nil { - return result, nil - } - - if err := call.Vars.Range(rangeFunc); err != nil { - return nil, err - } - if err := t.Vars.Range(taskRangeFunc); err != nil { - return nil, err - } - - return result, nil -} - -func (c *CompilerV3) HandleDynamicVar(v taskfile.Var, dir string) (string, error) { - c.muDynamicCache.Lock() - defer c.muDynamicCache.Unlock() - - if c.dynamicCache == nil { - c.dynamicCache = make(map[string]string, 30) - } - if result, ok := c.dynamicCache[v.Sh]; ok { - return result, nil - } - - // NOTE(@andreynering): If a var have a specific dir, use this instead - if v.Dir != "" { - dir = v.Dir - } - - var stdout bytes.Buffer - opts := &execext.RunCommandOptions{ - Command: v.Sh, - Dir: dir, - Stdout: &stdout, - Stderr: c.Logger.Stderr, - } - if err := execext.RunCommand(context.Background(), opts); err != nil { - return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err) - } - - // Trim a single trailing newline from the result to make most command - // output easier to use in shell commands. - result := strings.TrimSuffix(stdout.String(), "\r\n") - result = strings.TrimSuffix(result, "\n") - - c.dynamicCache[v.Sh] = result - c.Logger.VerboseErrf(logger.Magenta, "task: dynamic variable: %q result: %q\n", v.Sh, result) - - return result, nil -} - -// ResetCache clear the dymanic variables cache -func (c *CompilerV3) ResetCache() { - c.muDynamicCache.Lock() - defer c.muDynamicCache.Unlock() - - c.dynamicCache = nil -} - -func (c *CompilerV3) getSpecialVars(t *taskfile.Task) (map[string]string, error) { - taskfileDir, err := c.getTaskfileDir(t) - if err != nil { - return nil, err - } - - return map[string]string{ - "TASK": t.Task, - "ROOT_DIR": c.Dir, - "TASKFILE_DIR": taskfileDir, - "USER_WORKING_DIR": c.UserWorkingDir, - "TASK_VERSION": version.GetVersion(), - }, nil -} - -func (c *CompilerV3) getTaskfileDir(t *taskfile.Task) (string, error) { - if t.IncludedTaskfile != nil { - return t.IncludedTaskfile.FullDirPath() - } - return c.Dir, nil -} diff --git a/internal/templater/templater.go b/internal/templater/templater.go index c40a6a31..14421f4a 100644 --- a/internal/templater/templater.go +++ b/internal/templater/templater.go @@ -15,8 +15,7 @@ import ( // happen will be assigned to r.err, and consecutive calls to funcs will just // return the zero value. type Templater struct { - Vars *taskfile.Vars - RemoveNoValue bool + Vars *taskfile.Vars cacheMap map[string]any err error @@ -62,10 +61,7 @@ func (r *Templater) replace(str string, extra map[string]any) string { r.err = err return "" } - if r.RemoveNoValue { - return strings.ReplaceAll(b.String(), "", "") - } - return b.String() + return strings.ReplaceAll(b.String(), "", "") } func (r *Templater) ReplaceSlice(strs []string) []string { diff --git a/setup.go b/setup.go index ba53a151..17dbbe51 100644 --- a/setup.go +++ b/setup.go @@ -12,8 +12,7 @@ import ( "github.com/Masterminds/semver/v3" "github.com/sajari/fuzzy" - compilerv2 "github.com/go-task/task/v3/internal/compiler/v2" - compilerv3 "github.com/go-task/task/v3/internal/compiler/v3" + "github.com/go-task/task/v3/internal/compiler" "github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/filepathext" "github.com/go-task/task/v3/internal/logger" @@ -179,38 +178,21 @@ func (e *Executor) setupOutput() error { } func (e *Executor) setupCompiler() error { - if e.Taskfile.Version.LessThan(taskfile.V3) { + if e.UserWorkingDir == "" { var err error - e.taskvars, err = read.Taskvars(e.Dir) + e.UserWorkingDir, err = os.Getwd() if err != nil { return err } - - e.Compiler = &compilerv2.CompilerV2{ - Dir: e.Dir, - Taskvars: e.taskvars, - TaskfileVars: e.Taskfile.Vars, - Expansions: e.Taskfile.Expansions, - Logger: e.Logger, - } - } else { - if e.UserWorkingDir == "" { - var err error - e.UserWorkingDir, err = os.Getwd() - if err != nil { - return err - } - } - - e.Compiler = &compilerv3.CompilerV3{ - Dir: e.Dir, - UserWorkingDir: e.UserWorkingDir, - TaskfileEnv: e.Taskfile.Env, - TaskfileVars: e.Taskfile.Vars, - Logger: e.Logger, - } } + e.Compiler = &compiler.Compiler{ + Dir: e.Dir, + UserWorkingDir: e.UserWorkingDir, + TaskfileEnv: e.Taskfile.Env, + TaskfileVars: e.Taskfile.Vars, + Logger: e.Logger, + } return nil } @@ -234,19 +216,9 @@ func (e *Executor) readDotEnvFiles() error { } func (e *Executor) setupDefaults() { - // Color available only on v3 - if e.Taskfile.Version.LessThan(taskfile.V3) { - e.Logger.Color = false - } - if e.Taskfile.Method == "" { - if e.Taskfile.Version.Compare(taskfile.V3) >= 0 { - e.Taskfile.Method = "checksum" - } else { - e.Taskfile.Method = "timestamp" - } + e.Taskfile.Method = "checksum" } - if e.Taskfile.Run == "" { e.Taskfile.Run = "always" } @@ -272,18 +244,11 @@ func (e *Executor) doVersionChecks() error { v := &semver.Version{} *v = *e.Taskfile.Version - if v.LessThan(taskfile.V2) { - return fmt.Errorf(`task: version 1 schemas are no longer supported`) - } - if v.LessThan(taskfile.V3) { - e.Logger.Errf(logger.Yellow, "task: version 2 schemas are deprecated and will be removed in a future release\nSee https://github.com/go-task/task/issues/1197 for more details\n") + return fmt.Errorf(`task: Taskfile schemas prior to v3 are no longer supported`) } // consider as equal to the greater version if round - if v.Equal(taskfile.V2) { - v = semver.MustParse("2.6") - } if v.Equal(taskfile.V3) { v = semver.MustParse("3.8") } @@ -292,54 +257,10 @@ func (e *Executor) doVersionChecks() error { return fmt.Errorf(`task: Taskfile versions greater than v3.8 not implemented in the version of Task`) } - if v.LessThan(semver.MustParse("2.1")) && !e.Taskfile.Output.IsSet() { - return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`) - } - if v.LessThan(semver.MustParse("2.2")) && e.Taskfile.Includes.Len() > 0 { - return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`) - } - if v.Compare(taskfile.V3) >= 0 && e.Taskfile.Expansions > 2 { - return fmt.Errorf(`task: The "expansions" setting is not available anymore on v3.0`) - } if v.LessThan(semver.MustParse("3.8")) && e.Taskfile.Output.Group.IsSet() { return fmt.Errorf(`task: Taskfile option "output.group" is only available starting on Taskfile version v3.8`) } - if v.Compare(semver.MustParse("2.1")) <= 0 { - err := errors.New(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`) - - for _, task := range e.Taskfile.Tasks.Values() { - if task.IgnoreError { - return err - } - for _, cmd := range task.Cmds { - if cmd.IgnoreError { - return err - } - } - } - } - - if v.LessThan(semver.MustParse("2.6")) { - for _, task := range e.Taskfile.Tasks.Values() { - if len(task.Preconditions) > 0 { - return errors.New(`task: Task option "preconditions" is only available starting on Taskfile version v2.6`) - } - } - } - - if v.LessThan(taskfile.V3) { - err := e.Taskfile.Includes.Range(func(_ string, taskfile taskfile.IncludedTaskfile) error { - if taskfile.AdvancedImport { - return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`) - } - return nil - }) - if err != nil { - return err - } - } - if v.LessThan(semver.MustParse("3.7")) { if e.Taskfile.Run != "" { return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`) diff --git a/task.go b/task.go index 1ec9adfd..e6c239ec 100644 --- a/task.go +++ b/task.go @@ -64,13 +64,12 @@ type Executor struct { Stderr io.Writer Logger *logger.Logger - Compiler compiler.Compiler + Compiler *compiler.Compiler Output output.Output OutputStyle taskfile.Output TaskSorter sort.TaskSorter UserWorkingDir string - taskvars *taskfile.Vars fuzzyModel *fuzzy.Model concurrencySemaphore chan struct{} @@ -349,7 +348,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi outputWrapper = output.Interleaved{} } vars, err := e.Compiler.FastGetVariables(t, call) - outputTemplater := &templater.Templater{Vars: vars, RemoveNoValue: true} + outputTemplater := &templater.Templater{Vars: vars} if err != nil { return fmt.Errorf("task: failed to get variables: %w", err) } diff --git a/task_test.go b/task_test.go index d6e63bb4..2549d63e 100644 --- a/task_test.go +++ b/task_test.go @@ -91,47 +91,9 @@ func TestEnv(t *testing.T) { tt.Run(t) } -func TestVarsV2(t *testing.T) { +func TestVars(t *testing.T) { tt := fileContentTest{ - Dir: "testdata/vars/v2", - Target: "default", - TrimSpace: true, - Files: map[string]string{ - "foo.txt": "foo", - "bar.txt": "bar", - "baz.txt": "baz", - "tmpl_foo.txt": "foo", - "tmpl_bar.txt": "bar", - "tmpl_foo2.txt": "foo2", - "tmpl_bar2.txt": "bar2", - "shtmpl_foo.txt": "foo", - "shtmpl_foo2.txt": "foo2", - "nestedtmpl_foo.txt": "", - "nestedtmpl_foo2.txt": "foo2", - "foo2.txt": "foo2", - "bar2.txt": "bar2", - "baz2.txt": "baz2", - "tmpl2_foo.txt": "", - "tmpl2_foo2.txt": "foo2", - "tmpl2_bar.txt": "", - "tmpl2_bar2.txt": "bar2", - "shtmpl2_foo.txt": "", - "shtmpl2_foo2.txt": "foo2", - "nestedtmpl2_foo2.txt": "", - "override.txt": "bar", - "nested.txt": "Taskvars-TaskfileVars-TaskVars", - "task_name.txt": "hello", - }, - } - tt.Run(t) - // Ensure identical results when running hello task directly. - tt.Target = "hello" - tt.Run(t) -} - -func TestVarsV3(t *testing.T) { - tt := fileContentTest{ - Dir: "testdata/vars/v3", + Dir: "testdata/vars", Target: "default", Files: map[string]string{ "missing-var.txt": "\n", @@ -144,29 +106,6 @@ func TestVarsV3(t *testing.T) { tt.Run(t) } -func TestMultilineVars(t *testing.T) { - for _, dir := range []string{"testdata/vars/v2/multiline"} { - tt := fileContentTest{ - Dir: dir, - Target: "default", - TrimSpace: false, - Files: map[string]string{ - // Note: - // - task does not strip a trailing newline from var entries - // - task strips one trailing newline from shell output - // - the cat command adds a trailing newline - "echo_foobar.txt": "foo\nbar\n", - "echo_n_foobar.txt": "foo\nbar\n", - "echo_n_multiline.txt": "\n\nfoo\n bar\nfoobar\n\nbaz\n\n", - "var_multiline.txt": "\n\nfoo\n bar\nfoobar\n\nbaz\n\n\n", - "var_catlines.txt": " foo bar foobar baz \n", - "var_enumfile.txt": "0:\n1:\n2:foo\n3: bar\n4:foobar\n5:\n6:baz\n7:\n8:\n", - }, - } - tt.Run(t) - } -} - func TestSpecialVars(t *testing.T) { const dir = "testdata/special_vars" const target = "default" @@ -202,22 +141,6 @@ func TestSpecialVars(t *testing.T) { assert.Contains(t, output, "included/TASK_VERSION=unknown") } -func TestVarsInvalidTmpl(t *testing.T) { - const ( - dir = "testdata/vars/v2" - target = "invalid-var-tmpl" - expectError = "template: :1: unexpected EOF" - ) - - e := &task.Executor{ - Dir: dir, - Stdout: io.Discard, - Stderr: io.Discard, - } - require.NoError(t, e.Setup(), "e.Setup()") - assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)") -} - func TestConcurrency(t *testing.T) { const ( dir = "testdata/concurrency" @@ -910,8 +833,11 @@ func TestTaskVersion(t *testing.T) { tests := []struct { Dir string Version *semver.Version + wantErr bool }{ - {"testdata/version/v2", semver.MustParse("2")}, + {"testdata/version/v1", semver.MustParse("1"), true}, + {"testdata/version/v2", semver.MustParse("2"), true}, + {"testdata/version/v3", semver.MustParse("3"), false}, } for _, test := range tests { @@ -921,7 +847,12 @@ func TestTaskVersion(t *testing.T) { Stdout: io.Discard, Stderr: io.Discard, } - require.NoError(t, e.Setup()) + err := e.Setup() + if test.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) assert.Equal(t, test.Version, e.Taskfile.Version) assert.Equal(t, 2, e.Taskfile.Tasks.Len()) }) @@ -1061,21 +992,6 @@ func TestIncludeCycle(t *testing.T) { assert.Contains(t, err.Error(), "task: include cycle detected between") } -func TestIncorrectVersionIncludes(t *testing.T) { - const dir = "testdata/incorrect_includes" - expectedError := "task: Import with additional parameters is only available starting on Taskfile version v3" - - var buff bytes.Buffer - e := task.Executor{ - Dir: dir, - Stdout: &buff, - Stderr: &buff, - Silent: true, - } - - assert.EqualError(t, e.Setup(), expectedError) -} - func TestIncludesIncorrect(t *testing.T) { const dir = "testdata/includes_incorrect" @@ -1500,10 +1416,10 @@ func TestDisplaysErrorOnVersion1Schema(t *testing.T) { } err := e.Setup() require.Error(t, err) - assert.Equal(t, "task: version 1 schemas are no longer supported", err.Error()) + assert.Equal(t, "task: Taskfile schemas prior to v3 are no longer supported", err.Error()) } -func TestDisplaysWarningOnVersion2Schema(t *testing.T) { +func TestDisplaysErrorOnVersion2Schema(t *testing.T) { var buff bytes.Buffer e := task.Executor{ Dir: "testdata/version/v2", @@ -1511,8 +1427,8 @@ func TestDisplaysWarningOnVersion2Schema(t *testing.T) { Stderr: &buff, } err := e.Setup() - require.NoError(t, err) - assert.Equal(t, "task: version 2 schemas are deprecated and will be removed in a future release\nSee https://github.com/go-task/task/issues/1197 for more details\n", buff.String()) + require.Error(t, err) + assert.Equal(t, "task: Taskfile schemas prior to v3 are no longer supported", err.Error()) } func TestShortTaskNotation(t *testing.T) { diff --git a/taskfile/merge.go b/taskfile/merge.go index 866ba7ae..797dc3da 100644 --- a/taskfile/merge.go +++ b/taskfile/merge.go @@ -13,10 +13,6 @@ func Merge(t1, t2 *Taskfile, includedTaskfile *IncludedTaskfile, namespaces ...s if !t1.Version.Equal(t2.Version) { return fmt.Errorf(`task: Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version) } - - if t2.Expansions != 0 && t2.Expansions != 2 { - t1.Expansions = t2.Expansions - } if t2.Output.IsSet() { t1.Output = t2.Output } diff --git a/taskfile/read/dotenv.go b/taskfile/read/dotenv.go index 9828e6d4..54477e14 100644 --- a/taskfile/read/dotenv.go +++ b/taskfile/read/dotenv.go @@ -11,7 +11,7 @@ import ( "github.com/go-task/task/v3/taskfile" ) -func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.Vars, error) { +func Dotenv(c *compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.Vars, error) { if len(tf.Dotenv) == 0 { return nil, nil } @@ -23,7 +23,7 @@ func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.V env := &taskfile.Vars{} - tr := templater.Templater{Vars: vars, RemoveNoValue: true} + tr := templater.Templater{Vars: vars} for _, dotEnvPath := range tf.Dotenv { dotEnvPath = tr.Replace(dotEnvPath) diff --git a/taskfile/read/taskfile.go b/taskfile/read/taskfile.go index 63694486..05664ac4 100644 --- a/taskfile/read/taskfile.go +++ b/taskfile/read/taskfile.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "runtime" "time" "gopkg.in/yaml.v3" @@ -174,21 +173,19 @@ func Taskfile( } err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error { - if t.Version.Compare(taskfile.V3) >= 0 { - tr := templater.Templater{Vars: t.Vars, RemoveNoValue: true} - includedTask = taskfile.IncludedTaskfile{ - Taskfile: tr.Replace(includedTask.Taskfile), - Dir: tr.Replace(includedTask.Dir), - Optional: includedTask.Optional, - Internal: includedTask.Internal, - Aliases: includedTask.Aliases, - AdvancedImport: includedTask.AdvancedImport, - Vars: includedTask.Vars, - BaseDir: includedTask.BaseDir, - } - if err := tr.Err(); err != nil { - return err - } + tr := templater.Templater{Vars: t.Vars} + includedTask = taskfile.IncludedTaskfile{ + Taskfile: tr.Replace(includedTask.Taskfile), + Dir: tr.Replace(includedTask.Dir), + Optional: includedTask.Optional, + Internal: includedTask.Internal, + Aliases: includedTask.Aliases, + AdvancedImport: includedTask.AdvancedImport, + Vars: includedTask.Vars, + BaseDir: includedTask.BaseDir, + } + if err := tr.Err(); err != nil { + return err } uri, err := includedTask.FullTaskfilePath() @@ -219,7 +216,7 @@ func Taskfile( return err } - if t.Version.Compare(taskfile.V3) >= 0 && len(includedTaskfile.Dotenv) > 0 { + if len(includedTaskfile.Dotenv) > 0 { return ErrIncludedTaskfilesCantHaveDotenvs } @@ -273,31 +270,6 @@ func Taskfile( return nil, err } - if t.Version.Compare(taskfile.V3) < 0 { - if node, isFileNode := node.(*FileNode); isFileNode { - path := filepathext.SmartJoin(node.Dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS)) - if _, err = os.Stat(path); err == nil { - osNode := &FileNode{ - BaseNode: NewBaseNode(WithParent(node)), - Entrypoint: path, - Dir: node.Dir, - } - b, err := osNode.Read(context.Background()) - if err != nil { - return nil, err - } - var osTaskfile *taskfile.Taskfile - if err := yaml.Unmarshal(b, &osTaskfile); err != nil { - return nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(node.Location()), Err: err} - } - t.Location = node.Location() - if err = taskfile.Merge(t, osTaskfile, nil); err != nil { - return nil, err - } - } - } - } - for _, task := range t.Tasks.Values() { // If the task is not defined, create a new one if task == nil { diff --git a/taskfile/read/taskvars.go b/taskfile/read/taskvars.go deleted file mode 100644 index f920384e..00000000 --- a/taskfile/read/taskvars.go +++ /dev/null @@ -1,45 +0,0 @@ -package read - -import ( - "fmt" - "os" - "runtime" - - "gopkg.in/yaml.v3" - - "github.com/go-task/task/v3/internal/filepathext" - "github.com/go-task/task/v3/taskfile" -) - -// Taskvars reads a Taskvars for a given directory -func Taskvars(dir string) (*taskfile.Vars, error) { - vars := &taskfile.Vars{} - - path := filepathext.SmartJoin(dir, "Taskvars.yml") - if _, err := os.Stat(path); err == nil { - vars, err = readTaskvars(path) - if err != nil { - return nil, err - } - } - - path = filepathext.SmartJoin(dir, fmt.Sprintf("Taskvars_%s.yml", runtime.GOOS)) - if _, err := os.Stat(path); err == nil { - osVars, err := readTaskvars(path) - if err != nil { - return nil, err - } - vars.Merge(osVars) - } - - return vars, nil -} - -func readTaskvars(file string) (*taskfile.Vars, error) { - f, err := os.Open(file) - if err != nil { - return nil, err - } - var vars taskfile.Vars - return &vars, yaml.NewDecoder(f).Decode(&vars) -} diff --git a/taskfile/taskfile.go b/taskfile/taskfile.go index 344d00eb..691b4a76 100644 --- a/taskfile/taskfile.go +++ b/taskfile/taskfile.go @@ -10,54 +10,48 @@ import ( "github.com/go-task/task/v3/errors" ) -var ( - V3 = semver.MustParse("3") - V2 = semver.MustParse("2") -) +var V3 = semver.MustParse("3") // Taskfile represents a Taskfile.yml type Taskfile struct { - Location string - Version *semver.Version - Expansions int - Output Output - Method string - Includes *IncludedTaskfiles - Set []string - Shopt []string - Vars *Vars - Env *Vars - Tasks Tasks - Silent bool - Dotenv []string - Run string - Interval time.Duration + Location string + Version *semver.Version + Output Output + Method string + Includes *IncludedTaskfiles + Set []string + Shopt []string + Vars *Vars + Env *Vars + Tasks Tasks + Silent bool + Dotenv []string + Run string + Interval time.Duration } func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error { switch node.Kind { case yaml.MappingNode: var taskfile struct { - Version *semver.Version - Expansions int - Output Output - Method string - Includes *IncludedTaskfiles - Set []string - Shopt []string - Vars *Vars - Env *Vars - Tasks Tasks - Silent bool - Dotenv []string - Run string - Interval time.Duration + Version *semver.Version + Output Output + Method string + Includes *IncludedTaskfiles + Set []string + Shopt []string + Vars *Vars + Env *Vars + Tasks Tasks + Silent bool + Dotenv []string + Run string + Interval time.Duration } if err := node.Decode(&taskfile); err != nil { return err } tf.Version = taskfile.Version - tf.Expansions = taskfile.Expansions tf.Output = taskfile.Output tf.Method = taskfile.Method tf.Includes = taskfile.Includes @@ -70,9 +64,6 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error { tf.Dotenv = taskfile.Dotenv tf.Run = taskfile.Run tf.Interval = taskfile.Interval - if tf.Expansions <= 0 { - tf.Expansions = 2 - } if tf.Version == nil { return errors.New("task: 'version' is required") } diff --git a/testdata/concurrency/Taskfile.yml b/testdata/concurrency/Taskfile.yml index 6a6e6c4f..5a9a7012 100644 --- a/testdata/concurrency/Taskfile.yml +++ b/testdata/concurrency/Taskfile.yml @@ -1,4 +1,4 @@ -version: '2' +version: "3" tasks: default: diff --git a/testdata/incorrect_includes/Taskfile.yml b/testdata/incorrect_includes/Taskfile.yml deleted file mode 100644 index 63f4745b..00000000 --- a/testdata/incorrect_includes/Taskfile.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: '2.6' - -includes: - included: - taskfile: ./included - -tasks: - default: - cmds: - - task: gen diff --git a/testdata/incorrect_includes/included/Taskfile.yml b/testdata/incorrect_includes/included/Taskfile.yml deleted file mode 100644 index a50cc0ba..00000000 --- a/testdata/incorrect_includes/included/Taskfile.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: '2.6' - -tasks: - gen: - cmds: - - echo incorrect includes test diff --git a/testdata/vars/v3/.env b/testdata/vars/.env similarity index 100% rename from testdata/vars/v3/.env rename to testdata/vars/.env diff --git a/testdata/vars/v2/.gitignore b/testdata/vars/.gitignore similarity index 100% rename from testdata/vars/v2/.gitignore rename to testdata/vars/.gitignore diff --git a/testdata/vars/v3/Taskfile.yml b/testdata/vars/Taskfile.yml similarity index 100% rename from testdata/vars/v3/Taskfile.yml rename to testdata/vars/Taskfile.yml diff --git a/testdata/vars/v2/Taskfile.yml b/testdata/vars/v2/Taskfile.yml deleted file mode 100644 index 7f533c5d..00000000 --- a/testdata/vars/v2/Taskfile.yml +++ /dev/null @@ -1,58 +0,0 @@ -version: '2' - -vars: - NESTED2: "{{.NESTED1}}-TaskfileVars" - -tasks: - default: - deps: [hello] - - hello: - cmds: - - echo {{.FOO}} > foo.txt - - echo {{.BAR}} > bar.txt - - echo {{.BAZ}} > baz.txt - - echo '{{.TMPL_FOO}}' > tmpl_foo.txt - - echo '{{.TMPL_BAR}}' > tmpl_bar.txt - - echo '{{.TMPL_FOO2}}' > tmpl_foo2.txt - - echo '{{.TMPL_BAR2}}' > tmpl_bar2.txt - - echo '{{.SHTMPL_FOO}}' > shtmpl_foo.txt - - echo '{{.SHTMPL_FOO2}}' > shtmpl_foo2.txt - - echo '{{.NESTEDTMPL_FOO}}' > nestedtmpl_foo.txt - - echo '{{.NESTEDTMPL_FOO2}}' > nestedtmpl_foo2.txt - - echo {{.FOO2}} > foo2.txt - - echo {{.BAR2}} > bar2.txt - - echo {{.BAZ2}} > baz2.txt - - echo '{{.TMPL2_FOO}}' > tmpl2_foo.txt - - echo '{{.TMPL2_BAR}}' > tmpl2_bar.txt - - echo '{{.TMPL2_FOO2}}' > tmpl2_foo2.txt - - echo '{{.TMPL2_BAR2}}' > tmpl2_bar2.txt - - echo '{{.SHTMPL2_FOO}}' > shtmpl2_foo.txt - - echo '{{.SHTMPL2_FOO2}}' > shtmpl2_foo2.txt - - echo '{{.NESTEDTMPL2_FOO2}}' > nestedtmpl2_foo2.txt - - echo {{.OVERRIDE}} > override.txt - - echo '{{.NESTED3}}' > nested.txt - - echo '{{.TASK}}' > task_name.txt - vars: - FOO: foo - BAR: - sh: echo bar - BAZ: - sh: echo baz - TMPL_FOO: "{{.FOO}}" - TMPL_BAR: "{{.BAR}}" - TMPL_FOO2: "{{.FOO2}}" - TMPL_BAR2: "{{.BAR2}}" - SHTMPL_FOO: - sh: "echo '{{.FOO}}'" - SHTMPL_FOO2: - sh: "echo '{{.FOO2}}'" - NESTEDTMPL_FOO: "{{.TMPL_FOO}}" - NESTEDTMPL_FOO2: "{{.TMPL2_FOO2}}" - OVERRIDE: "bar" - NESTED3: "{{.NESTED2}}-TaskVars" - - invalid-var-tmpl: - vars: - CHARS: "abcd" - INVALID: "{{range .CHARS}}no end" diff --git a/testdata/vars/v2/Taskvars.yml b/testdata/vars/v2/Taskvars.yml deleted file mode 100644 index 576f988e..00000000 --- a/testdata/vars/v2/Taskvars.yml +++ /dev/null @@ -1,14 +0,0 @@ -FOO2: foo2 -BAR2: - sh: echo bar2 -BAZ2: - sh: echo baz2 -TMPL2_FOO: "{{.FOO}}" -TMPL2_BAR: "{{.BAR}}" -TMPL2_FOO2: "{{.FOO2}}" -TMPL2_BAR2: "{{.BAR2}}" -SHTMPL2_FOO2: - sh: "echo '{{.FOO2}}'" -NESTEDTMPL2_FOO2: "{{.TMPL2_FOO2}}" -OVERRIDE: "foo" -NESTED1: "Taskvars" diff --git a/testdata/vars/v2/multiline/Taskfile.yml b/testdata/vars/v2/multiline/Taskfile.yml deleted file mode 100644 index 0ecdb7a2..00000000 --- a/testdata/vars/v2/multiline/Taskfile.yml +++ /dev/null @@ -1,45 +0,0 @@ -version: '2' -tasks: - default: - vars: - MULTILINE: "\n\nfoo\n bar\nfoobar\n\nbaz\n\n" - cmds: - - task: file - vars: - CONTENT: - sh: "echo 'foo\nbar'" - FILE: "echo_foobar.txt" - - task: file - vars: - CONTENT: - sh: "echo -n 'foo\nbar'" - FILE: "echo_n_foobar.txt" - - task: file - vars: - CONTENT: - sh: echo -n "{{.MULTILINE}}" - FILE: "echo_n_multiline.txt" - - task: file - vars: - CONTENT: "{{.MULTILINE}}" - FILE: "var_multiline.txt" - - task: file - vars: - CONTENT: "{{.MULTILINE | catLines}}" - FILE: "var_catlines.txt" - - task: enumfile - vars: - LINES: "{{.MULTILINE}}" - FILE: "var_enumfile.txt" - file: - cmds: - - | - cat << EOF > '{{.FILE}}' - {{.CONTENT}} - EOF - enumfile: - cmds: - - | - cat << EOF > '{{.FILE}}' - {{range $i, $line := .LINES| splitLines}}{{$i}}:{{$line}} - {{end}}EOF diff --git a/testdata/vars/v3/.gitignore b/testdata/vars/v3/.gitignore deleted file mode 100644 index 2211df63..00000000 --- a/testdata/vars/v3/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.txt diff --git a/testdata/version/v1/Taskfile.yml b/testdata/version/v1/Taskfile.yml index 5eff2c71..cef72fe9 100644 --- a/testdata/version/v1/Taskfile.yml +++ b/testdata/version/v1/Taskfile.yml @@ -1,4 +1,4 @@ -version: 1 +version: "1" tasks: foo: cmds: diff --git a/testdata/version/v2/Taskfile.yml b/testdata/version/v2/Taskfile.yml index 9f47f2df..d9a4cd16 100644 --- a/testdata/version/v2/Taskfile.yml +++ b/testdata/version/v2/Taskfile.yml @@ -1,4 +1,4 @@ -version: '2' +version: "2" tasks: foo: diff --git a/testdata/version/v3/Taskfile.yml b/testdata/version/v3/Taskfile.yml new file mode 100644 index 00000000..b6148faf --- /dev/null +++ b/testdata/version/v3/Taskfile.yml @@ -0,0 +1,9 @@ +version: "3" + +tasks: + foo: + cmds: + - echo "Foo" + bar: + cmds: + - echo "Bar" diff --git a/variables.go b/variables.go index 9e7a4b0d..84313fb2 100644 --- a/variables.go +++ b/variables.go @@ -42,7 +42,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf return nil, err } - r := templater.Templater{Vars: vars, RemoveNoValue: e.Taskfile.Version.Compare(taskfile.V3) >= 0} + r := templater.Templater{Vars: vars} new := taskfile.Task{ Task: origTask.Task,