From bf0ebfcabb8de746268af02f7b113a4bc3af16a5 Mon Sep 17 00:00:00 2001 From: Valentin Maerten Date: Tue, 24 Sep 2024 08:32:28 +0200 Subject: [PATCH] feat: exclude task from being included --- task_test.go | 28 +++++++++ taskfile/ast/include.go | 5 ++ taskfile/ast/tasks.go | 14 ++++- taskfile/reader.go | 1 + testdata/includes_with_excludes/Taskfile.yml | 17 +++++ .../included/Taskfile.yml | 5 ++ website/docs/usage.mdx | 62 +++++++++++++++---- website/static/schema.json | 7 +++ 8 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 testdata/includes_with_excludes/Taskfile.yml create mode 100644 testdata/includes_with_excludes/included/Taskfile.yml diff --git a/task_test.go b/task_test.go index 4e31de42..7750da1e 100644 --- a/task_test.go +++ b/task_test.go @@ -1305,6 +1305,34 @@ func TestIncludesInterpolation(t *testing.T) { } } +func TestIncludesWithExclude(t *testing.T) { + var buff bytes.Buffer + e := task.Executor{ + Dir: "testdata/includes_with_excludes", + Silent: true, + Stdout: &buff, + Stderr: &buff, + } + require.NoError(t, e.Setup()) + + err := e.Run(context.Background(), &ast.Call{Task: "included:bar"}) + require.NoError(t, err) + assert.Equal(t, "bar\n", buff.String()) + buff.Reset() + + err = e.Run(context.Background(), &ast.Call{Task: "included:foo"}) + require.Error(t, err) + buff.Reset() + + err = e.Run(context.Background(), &ast.Call{Task: "bar"}) + require.Error(t, err) + buff.Reset() + + err = e.Run(context.Background(), &ast.Call{Task: "foo"}) + require.NoError(t, err) + assert.Equal(t, "foo\n", buff.String()) +} + func TestIncludedTaskfileVarMerging(t *testing.T) { const dir = "testdata/included_taskfile_var_merging" tests := []struct { diff --git a/taskfile/ast/include.go b/taskfile/ast/include.go index 659070de..a190154d 100644 --- a/taskfile/ast/include.go +++ b/taskfile/ast/include.go @@ -4,6 +4,7 @@ import ( "gopkg.in/yaml.v3" "github.com/go-task/task/v3/errors" + "github.com/go-task/task/v3/internal/deepcopy" omap "github.com/go-task/task/v3/internal/omap" ) @@ -15,6 +16,7 @@ type Include struct { Optional bool Internal bool Aliases []string + Excludes []string AdvancedImport bool Vars *Vars Flatten bool @@ -84,6 +86,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error { Internal bool Flatten bool Aliases []string + Excludes []string Vars *Vars } if err := node.Decode(&includedTaskfile); err != nil { @@ -94,6 +97,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error { include.Optional = includedTaskfile.Optional include.Internal = includedTaskfile.Internal include.Aliases = includedTaskfile.Aliases + include.Excludes = includedTaskfile.Excludes include.AdvancedImport = true include.Vars = includedTaskfile.Vars include.Flatten = includedTaskfile.Flatten @@ -115,6 +119,7 @@ func (include *Include) DeepCopy() *Include { Dir: include.Dir, Optional: include.Optional, Internal: include.Internal, + Excludes: deepcopy.Slice(include.Excludes), AdvancedImport: include.AdvancedImport, Vars: include.Vars.DeepCopy(), Flatten: include.Flatten, diff --git a/taskfile/ast/tasks.go b/taskfile/ast/tasks.go index cfe29a8d..0ea0aecf 100644 --- a/taskfile/ast/tasks.go +++ b/taskfile/ast/tasks.go @@ -56,6 +56,12 @@ func (t1 *Tasks) Merge(t2 Tasks, include *Include, includedTaskfileVars *Vars) e // taskfile are marked as internal task.Internal = task.Internal || (include != nil && include.Internal) taskName := name + + // if the task is in the exclude list, don't add it to the merged taskfile and early return + if slices.Contains(include.Excludes, name) { + return nil + } + if !include.Flatten { // Add namespaces to task dependencies for _, dep := range task.Deps { @@ -106,8 +112,12 @@ func (t1 *Tasks) Merge(t2 Tasks, include *Include, includedTaskfileVars *Vars) e Include: include.Namespace, } } - // Add the task to the merged taskfile - t1.Set(taskName, task) + + if !slices.Contains(include.Excludes, taskName) { + println("taskName: ", taskName) + // Add the task to the merged taskfile + t1.Set(taskName, task) + } return nil }) diff --git a/taskfile/reader.go b/taskfile/reader.go index 1f1fefe2..f896e9f5 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -112,6 +112,7 @@ func (r *Reader) include(node Node) error { Flatten: include.Flatten, Aliases: include.Aliases, AdvancedImport: include.AdvancedImport, + Excludes: include.Excludes, Vars: include.Vars, } if err := cache.Err(); err != nil { diff --git a/testdata/includes_with_excludes/Taskfile.yml b/testdata/includes_with_excludes/Taskfile.yml new file mode 100644 index 00000000..6548aab5 --- /dev/null +++ b/testdata/includes_with_excludes/Taskfile.yml @@ -0,0 +1,17 @@ +version: '3' + +includes: + included: + taskfile: ./included/Taskfile.yml + excludes: + - foo + included_flatten: + taskfile: ./included/Taskfile.yml + flatten: true + excludes: + - bar + +tasks: + default: + cmds: + - echo "called_dep" > called_dep.txt diff --git a/testdata/includes_with_excludes/included/Taskfile.yml b/testdata/includes_with_excludes/included/Taskfile.yml new file mode 100644 index 00000000..6de33b01 --- /dev/null +++ b/testdata/includes_with_excludes/included/Taskfile.yml @@ -0,0 +1,5 @@ +version: '3' + +tasks: + foo: echo foo + bar: echo bar diff --git a/website/docs/usage.mdx b/website/docs/usage.mdx index e05f7268..56de4c2a 100644 --- a/website/docs/usage.mdx +++ b/website/docs/usage.mdx @@ -393,6 +393,8 @@ You can run `task foo` directly without the namespace. You can also reference the task in other tasks without the namespace. So if you run `task greet` it will run `greet` and `foo` tasks and the output will be : ```text +Greet +Foo ``` If multiple tasks have the same name, an error will be thrown: @@ -409,14 +411,14 @@ If multiple tasks have the same name, an error will be thrown: version: '3' includes: lib: - taskfile: ./Included.yml - flatten: true + taskfile: ./Included.yml + flatten: true - tasks: - greet: - cmds: - - echo "Greet" - - task: foo + tasks: + greet: + cmds: + - echo "Greet" + - task: foo ``` @@ -427,9 +429,9 @@ If multiple tasks have the same name, an error will be thrown: version: '3' tasks: - greet: - cmds: - - echo "Foo" + greet: + cmds: + - echo "Foo" ``` @@ -438,12 +440,50 @@ If multiple tasks have the same name, an error will be thrown: If you run `task -a` it will print: ```text task: Found multiple tasks (greet) included by "lib" - ``` +If you the included Taskfile has a task with the same name as a task in the main Taskfile, +you may want to exclude it from the flattened tasks. + +You can do this by using the [`excludes` option](#exclude-tasks-from-being-included). + +### Exclude tasks from being included + +You can exclude tasks from being included by using the `excludes` option. This option takes the list of tasks to be excluded from this include. + + + + + ```yaml + version: '3' + includes: + included: + taskfile: ./Included.yml + excludes: [foo] + ``` + + + ```yaml + version: '3' + + tasks: + foo: echo "Foo" + bar: echo "Bar" + ``` + + + + +`task included:foo` will throw an error because the `foo` task is excluded but `task included:bar` will work and display `Bar`. + +It's compatible with the `flatten` option. ### Vars of included Taskfiles diff --git a/website/static/schema.json b/website/static/schema.json index f87511f5..804fbeb8 100644 --- a/website/static/schema.json +++ b/website/static/schema.json @@ -634,6 +634,13 @@ "type": "string" } }, + "excludes": { + "description": "A list of tasks to be excluded from inclusion.", + "type": "array", + "items": { + "type": "string" + } + }, "vars": { "description": "A set of variables to apply to the included Taskfile.", "$ref": "#/definitions/vars"