Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1642,13 +1642,46 @@ func TestIncludesWithExclude(t *testing.T) {
require.Error(t, err)
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "included:foo:child"})
require.NoError(t, err)
assert.Equal(t, "foo:child\n", buff.String())
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "included:namespace"})
require.NoError(t, err)
assert.Equal(t, "namespace\n", buff.String())
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "included:namespace:one"})
require.Error(t, err)
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "included:namespace-other:one"})
require.NoError(t, err)
assert.Equal(t, "namespace-other:one\n", buff.String())
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "bar"})
require.Error(t, err)
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "foo"})
require.NoError(t, err)
assert.Equal(t, "foo\n", buff.String())
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "namespace"})
require.NoError(t, err)
assert.Equal(t, "namespace\n", buff.String())
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "namespace:two"})
require.Error(t, err)
buff.Reset()

err = e.Run(t.Context(), &task.Call{Task: "namespace-other:one"})
require.NoError(t, err)
assert.Equal(t, "namespace-other:one\n", buff.String())
}

func TestIncludedTaskfileVarMerging(t *testing.T) {
Expand Down
21 changes: 21 additions & 0 deletions taskfile/ast/include.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ type (
IncludeElement orderedmap.Element[string, *Include]
)

func (include *Include) isTaskExcluded(name string) bool {
if include == nil {
return false
}

for _, exclude := range include.Excludes {
namespace, ok := strings.CutSuffix(exclude, NamespaceSeparator+"*")
if ok {
if namespace != "" && strings.HasPrefix(name, namespace+NamespaceSeparator) {
return true
}
continue
}

if name == exclude {
return true
}
}
return false
}

// NewIncludes creates a new instance of Includes and initializes it with the
// provided set of elements, if any. The elements are added in the order they
// are passed.
Expand Down
5 changes: 3 additions & 2 deletions taskfile/ast/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ func (t1 *Tasks) Merge(t2 *Tasks, include *Include, includedTaskfileVars *Vars)
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
if slices.Contains(include.Excludes, name) {
// If the task or its namespace is in the exclude list, don't add it to
// the merged taskfile.
if include.isTaskExcluded(name) {
continue
}

Expand Down
4 changes: 3 additions & 1 deletion testdata/includes_with_excludes/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ includes:
included:
taskfile: ./included/Taskfile.yml
excludes:
- foo
- foo
- namespace:*
included_flatten:
taskfile: ./included/Taskfile.yml
flatten: true
excludes:
- bar
- namespace:*

tasks:
default:
Expand Down
5 changes: 5 additions & 0 deletions testdata/includes_with_excludes/included/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@ version: '3'

tasks:
foo: echo foo
foo:child: echo foo:child
bar: echo bar
namespace: echo namespace
namespace:one: echo namespace:one
namespace:two: echo namespace:two
namespace-other:one: echo namespace-other:one
13 changes: 9 additions & 4 deletions website/src/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,10 @@ You can do this by using the

### 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.
You can exclude tasks or entire namespaces from being included by using the
`excludes` option. This option takes the list of tasks or namespaces to be
excluded from this include. Task names are matched exactly. To exclude a
namespace, append `:*` to its name.

::: code-group

Expand All @@ -423,7 +425,7 @@ version: '3'
includes:
included:
taskfile: ./Included.yml
excludes: [foo]
excludes: [foo, 'internal:*', 'debug:*']
```

```yaml [Included.yml]
Expand All @@ -432,11 +434,14 @@ version: '3'
tasks:
foo: echo "Foo"
bar: echo "Bar"
internal:setup: echo "Internal setup"
debug:status: echo "Debug status"
```

:::

`task included:foo` will throw an error because the `foo` task is excluded but
`task included:foo`, `task included:internal:setup`, and
`task included:debug:status` will throw errors because they are excluded, but
`task included:bar` will work and display `Bar`.

It's compatible with the `flatten` option.
Expand Down
5 changes: 3 additions & 2 deletions website/src/docs/reference/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,14 @@ includes:
### `excludes`

- **Type**: `[]string`
- **Description**: Tasks to exclude from inclusion
- **Description**: Task names or namespace patterns ending in `:*` to exclude
from inclusion

```yaml
includes:
shared:
taskfile: ./shared.yml
excludes: [internal-setup, debug-only]
excludes: [internal-setup, 'debug:*', 'experimental:*']
```

### `vars`
Expand Down
2 changes: 1 addition & 1 deletion website/src/public/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@
}
},
"excludes": {
"description": "A list of tasks to be excluded from inclusion.",
"description": "A list of task names or namespace patterns ending in `:*` to be excluded from inclusion.",
"type": "array",
"items": {
"type": "string"
Expand Down