mirror of
https://github.com/go-task/task.git
synced 2024-12-04 10:24:45 +02:00
Add --json flag to be used by editor extensions (#936)
This commit is contained in:
parent
41a9316523
commit
321f7b59d8
@ -59,6 +59,7 @@ func main() {
|
||||
init bool
|
||||
list bool
|
||||
listAll bool
|
||||
listJson bool
|
||||
status bool
|
||||
force bool
|
||||
watch bool
|
||||
@ -81,6 +82,7 @@ func main() {
|
||||
pflag.BoolVarP(&init, "init", "i", false, "creates a new Taskfile.yaml in the current folder")
|
||||
pflag.BoolVarP(&list, "list", "l", false, "lists tasks with description of current Taskfile")
|
||||
pflag.BoolVarP(&listAll, "list-all", "a", false, "lists tasks with or without a description")
|
||||
pflag.BoolVarP(&listJson, "json", "j", false, "formats task list as json")
|
||||
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
|
||||
pflag.BoolVarP(&force, "force", "f", false, "forces execution even when the task is up-to-date")
|
||||
pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task")
|
||||
@ -162,7 +164,12 @@ func main() {
|
||||
OutputStyle: output,
|
||||
}
|
||||
|
||||
if (list || listAll) && silent {
|
||||
var listOptions = task.NewListOptions(list, listAll, listJson)
|
||||
if err := listOptions.Validate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if (listOptions.ShouldListTasks()) && silent {
|
||||
e.ListTaskNames(listAll)
|
||||
return
|
||||
}
|
||||
@ -176,16 +183,9 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if list {
|
||||
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 {
|
||||
if ok := e.ListTasks(task.FilterOutInternal()); !ok {
|
||||
e.Logger.Outf(logger.Yellow, "task: No tasks available")
|
||||
if listOptions.ShouldListTasks() {
|
||||
if foundTasks, err := e.ListTasks(listOptions); !foundTasks || err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
103
help.go
103
help.go
@ -1,7 +1,10 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-task/task/v3/taskfile"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
@ -9,16 +12,81 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/go-task/task/v3/internal/editors"
|
||||
"github.com/go-task/task/v3/internal/logger"
|
||||
)
|
||||
|
||||
// ListOptions collects list-related options
|
||||
type ListOptions struct {
|
||||
ListOnlyTasksWithDescriptions bool
|
||||
ListAllTasks bool
|
||||
FormatTaskListAsJSON bool
|
||||
}
|
||||
|
||||
// NewListOptions creates a new ListOptions instance
|
||||
func NewListOptions(list, listAll, listAsJson bool) ListOptions {
|
||||
return ListOptions{
|
||||
ListOnlyTasksWithDescriptions: list,
|
||||
ListAllTasks: listAll,
|
||||
FormatTaskListAsJSON: listAsJson,
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldListTasks returns true if one of the options to list tasks has been set to true
|
||||
func (o ListOptions) ShouldListTasks() bool {
|
||||
return o.ListOnlyTasksWithDescriptions || o.ListAllTasks
|
||||
}
|
||||
|
||||
// Validate validates that the collection of list-related options are in a valid configuration
|
||||
func (o ListOptions) Validate() error {
|
||||
if o.ListOnlyTasksWithDescriptions && o.ListAllTasks {
|
||||
return fmt.Errorf("task: cannot use --list and --list-all at the same time")
|
||||
}
|
||||
if o.FormatTaskListAsJSON && !o.ShouldListTasks() {
|
||||
return fmt.Errorf("task: --json only applies to --list or --list-all")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filters returns the slice of FilterFunc which filters a list
|
||||
// of taskfile.Task according to the given ListOptions
|
||||
func (o ListOptions) Filters() []FilterFunc {
|
||||
filters := []FilterFunc{FilterOutInternal()}
|
||||
|
||||
if o.ListOnlyTasksWithDescriptions {
|
||||
filters = append(filters, FilterOutNoDesc())
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
||||
|
||||
// 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...)
|
||||
// The function returns a boolean indicating whether tasks were found
|
||||
// and an error if one was encountered while preparing the output.
|
||||
func (e *Executor) ListTasks(o ListOptions) (bool, error) {
|
||||
tasks := e.GetTaskList(o.Filters()...)
|
||||
if o.FormatTaskListAsJSON {
|
||||
output, err := e.ToEditorOutput(tasks)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(e.Stdout)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(output); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return len(tasks) > 0, nil
|
||||
}
|
||||
if len(tasks) == 0 {
|
||||
return false
|
||||
if o.ListOnlyTasksWithDescriptions {
|
||||
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks")
|
||||
} else if o.ListAllTasks {
|
||||
e.Logger.Outf(logger.Yellow, "task: No tasks available")
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
|
||||
|
||||
@ -31,10 +99,12 @@ func (e *Executor) ListTasks(filters ...FilterFunc) bool {
|
||||
if len(task.Aliases) > 0 {
|
||||
e.Logger.FOutf(w, logger.Cyan, "\t(aliases: %s)", strings.Join(task.Aliases, ", "))
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
_, _ = fmt.Fprint(w, "\n")
|
||||
}
|
||||
w.Flush()
|
||||
return true
|
||||
if err := w.Flush(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ListTaskNames prints only the task names in a Taskfile.
|
||||
@ -69,3 +139,22 @@ func (e *Executor) ListTaskNames(allTasks bool) {
|
||||
fmt.Fprintln(w, t)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Executor) ToEditorOutput(tasks []*taskfile.Task) (*editors.Output, error) {
|
||||
o := &editors.Output{
|
||||
Tasks: make([]editors.Task, len(tasks)),
|
||||
}
|
||||
for i, t := range tasks {
|
||||
upToDate, err := e.isTaskUpToDate(context.Background(), t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o.Tasks[i] = editors.Task{
|
||||
Name: t.Name(),
|
||||
Desc: t.Desc,
|
||||
Summary: t.Summary,
|
||||
UpToDate: upToDate,
|
||||
}
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
14
internal/editors/output.go
Normal file
14
internal/editors/output.go
Normal file
@ -0,0 +1,14 @@
|
||||
package editors
|
||||
|
||||
// Output wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
||||
type Output struct {
|
||||
Tasks []Task `json:"tasks"`
|
||||
}
|
||||
|
||||
// Task describes a single task
|
||||
type Task struct {
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
Summary string `json:"summary"`
|
||||
UpToDate bool `json:"up_to_date"`
|
||||
}
|
4
task.go
4
task.go
@ -72,12 +72,10 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
||||
for _, call := range calls {
|
||||
task, err := e.GetTask(call)
|
||||
if err != nil {
|
||||
e.ListTasks(FilterOutInternal(), FilterOutNoDesc())
|
||||
return err
|
||||
}
|
||||
|
||||
if task.Internal {
|
||||
e.ListTasks(FilterOutInternal(), FilterOutNoDesc())
|
||||
return &taskInternalError{taskName: call.Task}
|
||||
}
|
||||
}
|
||||
@ -396,7 +394,7 @@ func (e *Executor) GetTaskList(filters ...FilterFunc) []*taskfile.Task {
|
||||
tasks = filter(tasks)
|
||||
}
|
||||
|
||||
// Sort the 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 {
|
||||
|
12
task_test.go
12
task_test.go
@ -606,7 +606,9 @@ func TestNoLabelInList(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
assert.NoError(t, e.Setup())
|
||||
e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc())
|
||||
if _, err := e.ListTasks(task.ListOptions{ListOnlyTasksWithDescriptions: true}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Contains(t, buff.String(), "foo")
|
||||
}
|
||||
|
||||
@ -624,7 +626,9 @@ func TestListAllShowsNoDesc(t *testing.T) {
|
||||
assert.NoError(t, e.Setup())
|
||||
|
||||
var title string
|
||||
e.ListTasks(task.FilterOutInternal())
|
||||
if _, err := e.ListTasks(task.ListOptions{ListAllTasks: true}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
for _, title = range []string{
|
||||
"foo",
|
||||
"voo",
|
||||
@ -646,7 +650,9 @@ func TestListCanListDescOnly(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.NoError(t, e.Setup())
|
||||
e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc())
|
||||
if _, err := e.ListTasks(task.ListOptions{ListOnlyTasksWithDescriptions: true}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var title string
|
||||
assert.Contains(t, buff.String(), "foo")
|
||||
|
Loading…
Reference in New Issue
Block a user