From 37f32cc705d6d98a7b2f504c3a58325851bc88bf Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Fri, 10 May 2024 17:41:39 +0000 Subject: [PATCH] fix: included variable merging --- task_test.go | 28 +++++++++++++++++++ taskfile/ast/include.go | 11 ++++++++ taskfile/ast/taskfile.go | 21 ++++++++++++++ taskfile/ast/tasks.go | 13 ++++++++- .../Taskfile.yaml | 12 ++++++++ .../bar/Taskfile.yaml | 11 ++++++++ .../foo/Taskfile.yaml | 11 ++++++++ 7 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 testdata/included_taskfile_var_merging/Taskfile.yaml create mode 100644 testdata/included_taskfile_var_merging/bar/Taskfile.yaml create mode 100644 testdata/included_taskfile_var_merging/foo/Taskfile.yaml diff --git a/task_test.go b/task_test.go index debbf7b8af..17128abb63 100644 --- a/task_test.go +++ b/task_test.go @@ -1228,6 +1228,34 @@ func TestIncludesInterpolation(t *testing.T) { } } +func TestIncludedTaskfileVarMerging(t *testing.T) { + const dir = "testdata/included_taskfile_var_merging" + tests := []struct { + name string + task string + expectedOutput string + }{ + {"foo", "foo:pwd", "included_taskfile_var_merging/foo\n"}, + {"bar", "bar:pwd", "included_taskfile_var_merging/bar\n"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var buff bytes.Buffer + e := task.Executor{ + Dir: dir, + Stdout: &buff, + Stderr: &buff, + Silent: true, + } + require.NoError(t, e.Setup()) + + err := e.Run(context.Background(), &ast.Call{Task: test.task}) + require.NoError(t, err) + assert.Contains(t, buff.String(), test.expectedOutput) + }) + } +} + func TestInternalTask(t *testing.T) { const dir = "testdata/internal_task" tests := []struct { diff --git a/taskfile/ast/include.go b/taskfile/ast/include.go index 5b04bc2790..7db38bf0d3 100644 --- a/taskfile/ast/include.go +++ b/taskfile/ast/include.go @@ -65,6 +65,17 @@ func (includes *Includes) Range(f func(k string, v *Include) error) error { return includes.OrderedMap.Range(f) } +// DeepCopy creates a new instance of Includes and copies +// data by value from the source struct. +func (includes *Includes) DeepCopy() *Includes { + if includes == nil { + return nil + } + return &Includes{ + OrderedMap: includes.OrderedMap.DeepCopy(), + } +} + func (include *Include) UnmarshalYAML(node *yaml.Node) error { switch node.Kind { diff --git a/taskfile/ast/taskfile.go b/taskfile/ast/taskfile.go index f895722065..1897f7400b 100644 --- a/taskfile/ast/taskfile.go +++ b/taskfile/ast/taskfile.go @@ -7,6 +7,8 @@ import ( "github.com/Masterminds/semver/v3" "gopkg.in/yaml.v3" + + "github.com/go-task/task/v3/internal/deepcopy" ) // NamespaceSeparator contains the character that separates namespaces @@ -103,3 +105,22 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error { return fmt.Errorf("yaml: line %d: cannot unmarshal %s into taskfile", node.Line, node.ShortTag()) } + +func (tf *Taskfile) DeepCopy() *Taskfile { + return &Taskfile{ + Location: tf.Location, + Version: semver.New(tf.Version.Major(), tf.Version.Minor(), tf.Version.Patch(), tf.Version.Prerelease(), tf.Version.Metadata()), + Output: tf.Output, + Method: tf.Method, + Includes: tf.Includes.DeepCopy(), + Set: deepcopy.Slice(tf.Set), + Shopt: deepcopy.Slice(tf.Shopt), + Vars: tf.Vars.DeepCopy(), + Env: tf.Env.DeepCopy(), + Tasks: *tf.Tasks.DeepCopy(), + Silent: tf.Silent, + Dotenv: deepcopy.Slice(tf.Dotenv), + Run: tf.Run, + Interval: tf.Interval, + } +} diff --git a/taskfile/ast/tasks.go b/taskfile/ast/tasks.go index 79d7606d43..c427025d43 100644 --- a/taskfile/ast/tasks.go +++ b/taskfile/ast/tasks.go @@ -90,7 +90,7 @@ func (t1 *Tasks) Merge(t2 Tasks, include *Include, includedTaskfileVars *Vars) { task.IncludeVars = &Vars{} } task.IncludeVars.Merge(include.Vars, nil) - task.IncludedTaskfileVars = includedTaskfileVars + task.IncludedTaskfileVars = includedTaskfileVars.DeepCopy() } // Add the task to the merged taskfile @@ -153,6 +153,17 @@ func (t *Tasks) UnmarshalYAML(node *yaml.Node) error { return fmt.Errorf("yaml: line %d: cannot unmarshal %s into tasks", node.Line, node.ShortTag()) } +// DeepCopy creates a new instance of Tasks and copies +// data by value from the source struct. +func (tasks *Tasks) DeepCopy() *Tasks { + if tasks == nil { + return nil + } + return &Tasks{ + OrderedMap: tasks.OrderedMap.DeepCopy(), + } +} + func taskNameWithNamespace(taskName string, namespace string) string { if strings.HasPrefix(taskName, NamespaceSeparator) { return strings.TrimPrefix(taskName, NamespaceSeparator) diff --git a/testdata/included_taskfile_var_merging/Taskfile.yaml b/testdata/included_taskfile_var_merging/Taskfile.yaml new file mode 100644 index 0000000000..6ea01adaac --- /dev/null +++ b/testdata/included_taskfile_var_merging/Taskfile.yaml @@ -0,0 +1,12 @@ +version: "3" + +includes: + foo: + taskfile: ./foo/Taskfile.yaml + bar: + taskfile: ./bar/Taskfile.yaml + +tasks: + stub: + cmds: + - echo 0 diff --git a/testdata/included_taskfile_var_merging/bar/Taskfile.yaml b/testdata/included_taskfile_var_merging/bar/Taskfile.yaml new file mode 100644 index 0000000000..afc5a94bdc --- /dev/null +++ b/testdata/included_taskfile_var_merging/bar/Taskfile.yaml @@ -0,0 +1,11 @@ +version: "3" + +vars: + DIR: bar + +tasks: + pwd: + dir: ./{{ .DIR }} + cmds: + - echo "{{ .DIR }}" + - pwd diff --git a/testdata/included_taskfile_var_merging/foo/Taskfile.yaml b/testdata/included_taskfile_var_merging/foo/Taskfile.yaml new file mode 100644 index 0000000000..691755756c --- /dev/null +++ b/testdata/included_taskfile_var_merging/foo/Taskfile.yaml @@ -0,0 +1,11 @@ +version: "3" + +vars: + DIR: foo + +tasks: + pwd: + dir: ./{{ .DIR }} + cmds: + - echo "{{ .DIR }}" + - pwd