mirror of
https://github.com/go-task/task.git
synced 2025-01-06 03:53:54 +02:00
refactor: implement task list filtering
This commit is contained in:
parent
3a0c7a8c36
commit
fa105a8a93
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- Tasks in the root Taskfile will now be displayed first in `--list`/`--list-all`
|
||||||
|
output ([#806](https://github.com/go-task/task/pull/806), [#890](https://github.com/go-task/task/pull/890)).
|
||||||
- It's now possible to call a `default` task in an included Taskfile by using
|
- It's now possible to call a `default` task in an included Taskfile by using
|
||||||
just the namespace. For example: `docs:default` is now automatically
|
just the namespace. For example: `docs:default` is now automatically
|
||||||
aliased to `docs`
|
aliased to `docs`
|
||||||
|
@ -177,12 +177,16 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if list {
|
if list {
|
||||||
e.ListTasksWithDesc()
|
if ok := e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc()); !ok {
|
||||||
|
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if listAll {
|
if listAll {
|
||||||
e.ListAllTasks()
|
if ok := e.ListTasks(task.FilterOutInternal()); !ok {
|
||||||
|
e.Logger.Outf(logger.Yellow, "task: No tasks available")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
58
help.go
58
help.go
@ -10,34 +10,15 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListTasksWithDesc reports tasks that have a description spec.
|
// ListTasks prints a list of tasks.
|
||||||
func (e *Executor) ListTasksWithDesc() {
|
// Tasks that match the given filters will be excluded from the list.
|
||||||
e.printTasks(false)
|
// The function returns a boolean indicating whether or not tasks were found.
|
||||||
}
|
func (e *Executor) ListTasks(filters ...FilterFunc) bool {
|
||||||
|
tasks := e.GetTaskList(filters...)
|
||||||
// ListAllTasks reports all tasks, with or without a description spec.
|
|
||||||
func (e *Executor) ListAllTasks() {
|
|
||||||
e.printTasks(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) printTasks(listAll bool) {
|
|
||||||
var tasks []*taskfile.Task
|
|
||||||
if listAll {
|
|
||||||
tasks = e.allTaskNames()
|
|
||||||
} else {
|
|
||||||
tasks = e.tasksWithDesc()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tasks) == 0 {
|
if len(tasks) == 0 {
|
||||||
if listAll {
|
return false
|
||||||
e.Logger.Outf(logger.Yellow, "task: No tasks available")
|
|
||||||
} else {
|
|
||||||
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
|
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
|
||||||
|
|
||||||
@ -53,32 +34,7 @@ func (e *Executor) printTasks(listAll bool) {
|
|||||||
fmt.Fprint(w, "\n")
|
fmt.Fprint(w, "\n")
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
return true
|
||||||
|
|
||||||
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
|
|
||||||
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
|
||||||
for _, task := range e.Taskfile.Tasks {
|
|
||||||
if !task.Internal {
|
|
||||||
tasks = append(tasks, task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
|
|
||||||
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
|
||||||
for _, task := range e.Taskfile.Tasks {
|
|
||||||
if !task.Internal && task.Desc != "" {
|
|
||||||
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
|
|
||||||
if err == nil {
|
|
||||||
task = compiledTask
|
|
||||||
}
|
|
||||||
tasks = append(tasks, task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTaskNames prints only the task names in a Taskfile.
|
// ListTaskNames prints only the task names in a Taskfile.
|
||||||
|
76
task.go
76
task.go
@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@ -70,12 +72,12 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
for _, call := range calls {
|
for _, call := range calls {
|
||||||
task, err := e.GetTask(call)
|
task, err := e.GetTask(call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.ListTasksWithDesc()
|
e.ListTasks(FilterOutInternal(), FilterOutNoDesc())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if task.Internal {
|
if task.Internal {
|
||||||
e.ListTasksWithDesc()
|
e.ListTasks(FilterOutInternal(), FilterOutNoDesc())
|
||||||
return &taskInternalError{taskName: call.Task}
|
return &taskInternalError{taskName: call.Task}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,3 +376,73 @@ func (e *Executor) GetTask(call taskfile.Call) (*taskfile.Task, error) {
|
|||||||
|
|
||||||
return matchingTask, nil
|
return matchingTask, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FilterFunc func(tasks []*taskfile.Task) []*taskfile.Task
|
||||||
|
|
||||||
|
func (e *Executor) GetTaskList(filters ...FilterFunc) []*taskfile.Task {
|
||||||
|
tasks := make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
||||||
|
|
||||||
|
// Fetch and compile the list of tasks
|
||||||
|
for _, task := range e.Taskfile.Tasks {
|
||||||
|
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
|
||||||
|
if err == nil {
|
||||||
|
task = compiledTask
|
||||||
|
}
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter the tasks
|
||||||
|
for _, filter := range filters {
|
||||||
|
tasks = filter(tasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the tasks
|
||||||
|
// Tasks that are not namespaced should be listed before tasks that are.
|
||||||
|
// We detect this by searching for a ':' in the task name.
|
||||||
|
sort.Slice(tasks, func(i, j int) bool {
|
||||||
|
iContainsColon := strings.Contains(tasks[i].Task, ":")
|
||||||
|
jContainsColon := strings.Contains(tasks[j].Task, ":")
|
||||||
|
if iContainsColon == jContainsColon {
|
||||||
|
return tasks[i].Task < tasks[j].Task
|
||||||
|
}
|
||||||
|
if !iContainsColon && jContainsColon {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
return tasks
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter is a generic task filtering function. It will remove each task in the
|
||||||
|
// slice where the result of the given function is true.
|
||||||
|
func Filter(f func(task *taskfile.Task) bool) FilterFunc {
|
||||||
|
return func(tasks []*taskfile.Task) []*taskfile.Task {
|
||||||
|
shift := 0
|
||||||
|
for _, task := range tasks {
|
||||||
|
if !f(task) {
|
||||||
|
tasks[shift] = task
|
||||||
|
shift++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This loop stops any memory leaks
|
||||||
|
for j := shift; j < len(tasks); j++ {
|
||||||
|
tasks[j] = nil
|
||||||
|
}
|
||||||
|
return slices.Clip(tasks[:shift])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterOutNoDesc removes all tasks that do not contain a description.
|
||||||
|
func FilterOutNoDesc() FilterFunc {
|
||||||
|
return Filter(func(task *taskfile.Task) bool {
|
||||||
|
return task.Desc == ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterOutInternal removes all tasks that are marked as internal.
|
||||||
|
func FilterOutInternal() FilterFunc {
|
||||||
|
return Filter(func(task *taskfile.Task) bool {
|
||||||
|
return task.Internal
|
||||||
|
})
|
||||||
|
}
|
||||||
|
25
task_test.go
25
task_test.go
@ -497,9 +497,6 @@ func TestAlias(t *testing.T) {
|
|||||||
func TestDuplicateAlias(t *testing.T) {
|
func TestDuplicateAlias(t *testing.T) {
|
||||||
const dir = "testdata/alias"
|
const dir = "testdata/alias"
|
||||||
|
|
||||||
data, err := os.ReadFile(filepathext.SmartJoin(dir, "alias-duplicate.txt"))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
@ -508,7 +505,7 @@ func TestDuplicateAlias(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "x"}))
|
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "x"}))
|
||||||
assert.Equal(t, string(data), buff.String())
|
assert.Equal(t, "", buff.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAliasSummary(t *testing.T) {
|
func TestAliasSummary(t *testing.T) {
|
||||||
@ -609,7 +606,7 @@ func TestNoLabelInList(t *testing.T) {
|
|||||||
Stderr: &buff,
|
Stderr: &buff,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
e.ListTasksWithDesc()
|
e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc())
|
||||||
assert.Contains(t, buff.String(), "foo")
|
assert.Contains(t, buff.String(), "foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,7 +624,7 @@ func TestListAllShowsNoDesc(t *testing.T) {
|
|||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
var title string
|
var title string
|
||||||
e.ListAllTasks()
|
e.ListTasks(task.FilterOutInternal())
|
||||||
for _, title = range []string{
|
for _, title = range []string{
|
||||||
"foo",
|
"foo",
|
||||||
"voo",
|
"voo",
|
||||||
@ -649,7 +646,7 @@ func TestListCanListDescOnly(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
e.ListTasksWithDesc()
|
e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc())
|
||||||
|
|
||||||
var title string
|
var title string
|
||||||
assert.Contains(t, buff.String(), "foo")
|
assert.Contains(t, buff.String(), "foo")
|
||||||
@ -1037,12 +1034,7 @@ func TestIncludesInternal(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"included internal task via task", "task-1", false, "Hello, World!\n"},
|
{"included internal task via task", "task-1", false, "Hello, World!\n"},
|
||||||
{"included internal task via dep", "task-2", false, "Hello, World!\n"},
|
{"included internal task via dep", "task-2", false, "Hello, World!\n"},
|
||||||
{
|
{"included internal direct", "included:task-3", true, ""},
|
||||||
"included internal direct",
|
|
||||||
"included:task-3",
|
|
||||||
true,
|
|
||||||
"task: No tasks with description available. Try --list-all to list all tasks\n",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -1077,12 +1069,7 @@ func TestInternalTask(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"internal task via task", "task-1", false, "Hello, World!\n"},
|
{"internal task via task", "task-1", false, "Hello, World!\n"},
|
||||||
{"internal task via dep", "task-2", false, "Hello, World!\n"},
|
{"internal task via dep", "task-2", false, "Hello, World!\n"},
|
||||||
{
|
{"internal direct", "task-3", true, ""},
|
||||||
"internal direct",
|
|
||||||
"task-3",
|
|
||||||
true,
|
|
||||||
"task: No tasks with description available. Try --list-all to list all tasks\n",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
1
testdata/alias/alias-duplicate.txt
vendored
1
testdata/alias/alias-duplicate.txt
vendored
@ -1 +0,0 @@
|
|||||||
task: No tasks with description available. Try --list-all to list all tasks
|
|
Loading…
Reference in New Issue
Block a user