mirror of
https://github.com/go-task/task.git
synced 2025-01-04 03:48:02 +02:00
refactor: implement task list filtering
This commit is contained in:
parent
3a0c7a8c36
commit
fa105a8a93
@ -2,6 +2,8 @@
|
||||
|
||||
## 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
|
||||
just the namespace. For example: `docs:default` is now automatically
|
||||
aliased to `docs`
|
||||
|
@ -177,12 +177,16 @@ func main() {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if listAll {
|
||||
e.ListAllTasks()
|
||||
if ok := e.ListTasks(task.FilterOutInternal()); !ok {
|
||||
e.Logger.Outf(logger.Yellow, "task: No tasks available")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
58
help.go
58
help.go
@ -10,34 +10,15 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/go-task/task/v3/internal/logger"
|
||||
"github.com/go-task/task/v3/taskfile"
|
||||
)
|
||||
|
||||
// ListTasksWithDesc reports tasks that have a description spec.
|
||||
func (e *Executor) ListTasksWithDesc() {
|
||||
e.printTasks(false)
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// ListTasks prints a list of tasks.
|
||||
// Tasks that match the given filters will be excluded from the list.
|
||||
// The function returns a boolean indicating whether or not tasks were found.
|
||||
func (e *Executor) ListTasks(filters ...FilterFunc) bool {
|
||||
tasks := e.GetTaskList(filters...)
|
||||
if len(tasks) == 0 {
|
||||
if listAll {
|
||||
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
|
||||
return false
|
||||
}
|
||||
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")
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
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
|
||||
return true
|
||||
}
|
||||
|
||||
// ListTaskNames prints only the task names in a Taskfile.
|
||||
|
76
task.go
76
task.go
@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@ -70,12 +72,12 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
||||
for _, call := range calls {
|
||||
task, err := e.GetTask(call)
|
||||
if err != nil {
|
||||
e.ListTasksWithDesc()
|
||||
e.ListTasks(FilterOutInternal(), FilterOutNoDesc())
|
||||
return err
|
||||
}
|
||||
|
||||
if task.Internal {
|
||||
e.ListTasksWithDesc()
|
||||
e.ListTasks(FilterOutInternal(), FilterOutNoDesc())
|
||||
return &taskInternalError{taskName: call.Task}
|
||||
}
|
||||
}
|
||||
@ -374,3 +376,73 @@ func (e *Executor) GetTask(call taskfile.Call) (*taskfile.Task, error) {
|
||||
|
||||
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) {
|
||||
const dir = "testdata/alias"
|
||||
|
||||
data, err := os.ReadFile(filepathext.SmartJoin(dir, "alias-duplicate.txt"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
var buff bytes.Buffer
|
||||
e := task.Executor{
|
||||
Dir: dir,
|
||||
@ -508,7 +505,7 @@ func TestDuplicateAlias(t *testing.T) {
|
||||
}
|
||||
assert.NoError(t, e.Setup())
|
||||
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) {
|
||||
@ -609,7 +606,7 @@ func TestNoLabelInList(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
assert.NoError(t, e.Setup())
|
||||
e.ListTasksWithDesc()
|
||||
e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc())
|
||||
assert.Contains(t, buff.String(), "foo")
|
||||
}
|
||||
|
||||
@ -627,7 +624,7 @@ func TestListAllShowsNoDesc(t *testing.T) {
|
||||
assert.NoError(t, e.Setup())
|
||||
|
||||
var title string
|
||||
e.ListAllTasks()
|
||||
e.ListTasks(task.FilterOutInternal())
|
||||
for _, title = range []string{
|
||||
"foo",
|
||||
"voo",
|
||||
@ -649,7 +646,7 @@ func TestListCanListDescOnly(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.NoError(t, e.Setup())
|
||||
e.ListTasksWithDesc()
|
||||
e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc())
|
||||
|
||||
var title string
|
||||
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 dep", "task-2", false, "Hello, World!\n"},
|
||||
{
|
||||
"included internal direct",
|
||||
"included:task-3",
|
||||
true,
|
||||
"task: No tasks with description available. Try --list-all to list all tasks\n",
|
||||
},
|
||||
{"included internal direct", "included:task-3", true, ""},
|
||||
}
|
||||
|
||||
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 dep", "task-2", false, "Hello, World!\n"},
|
||||
{
|
||||
"internal direct",
|
||||
"task-3",
|
||||
true,
|
||||
"task: No tasks with description available. Try --list-all to list all tasks\n",
|
||||
},
|
||||
{"internal direct", "task-3", true, ""},
|
||||
}
|
||||
|
||||
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