mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
c750695d83
This changes makes it so that we output the empty string for Format when there is no data. It turns out there are many places in the code where we have such handling, but in a way that would break the JSON formatter (since we'd output nothing on stdout or text rather than `[]`/`null`).
182 lines
4.0 KiB
Go
182 lines
4.0 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/coder/coder/v2/cli/cliui"
|
|
"github.com/coder/coder/v2/coderd/util/slice"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
"github.com/coder/serpent"
|
|
)
|
|
|
|
type taskListRow struct {
|
|
Task codersdk.Task `table:"t,recursive_inline"`
|
|
|
|
StateChangedAgo string `table:"state changed"`
|
|
}
|
|
|
|
func taskListRowFromTask(now time.Time, t codersdk.Task) taskListRow {
|
|
var stateAgo string
|
|
if t.CurrentState != nil {
|
|
stateAgo = now.UTC().Sub(t.CurrentState.Timestamp).Truncate(time.Second).String() + " ago"
|
|
}
|
|
|
|
return taskListRow{
|
|
Task: t,
|
|
|
|
StateChangedAgo: stateAgo,
|
|
}
|
|
}
|
|
|
|
func (r *RootCmd) taskList() *serpent.Command {
|
|
var (
|
|
statusFilter string
|
|
all bool
|
|
user string
|
|
quiet bool
|
|
|
|
formatter = cliui.NewOutputFormatter(
|
|
cliui.TableFormat(
|
|
[]taskListRow{},
|
|
[]string{
|
|
"name",
|
|
"status",
|
|
"state",
|
|
"state changed",
|
|
"message",
|
|
},
|
|
),
|
|
cliui.ChangeFormatterData(
|
|
cliui.JSONFormat(),
|
|
func(data any) (any, error) {
|
|
rows, ok := data.([]taskListRow)
|
|
if !ok {
|
|
return nil, xerrors.Errorf("expected []taskListRow, got %T", data)
|
|
}
|
|
out := make([]codersdk.Task, len(rows))
|
|
for i := range rows {
|
|
out[i] = rows[i].Task
|
|
}
|
|
return out, nil
|
|
},
|
|
),
|
|
)
|
|
)
|
|
|
|
cmd := &serpent.Command{
|
|
Use: "list",
|
|
Short: "List tasks",
|
|
Long: FormatExamples(
|
|
Example{
|
|
Description: "List tasks for the current user.",
|
|
Command: "coder task list",
|
|
},
|
|
Example{
|
|
Description: "List tasks for a specific user.",
|
|
Command: "coder task list --user someone-else",
|
|
},
|
|
Example{
|
|
Description: "List all tasks you can view.",
|
|
Command: "coder task list --all",
|
|
},
|
|
Example{
|
|
Description: "List all your running tasks.",
|
|
Command: "coder task list --status running",
|
|
},
|
|
Example{
|
|
Description: "As above, but only show IDs.",
|
|
Command: "coder task list --status running --quiet",
|
|
},
|
|
),
|
|
Aliases: []string{"ls"},
|
|
Middleware: serpent.Chain(
|
|
serpent.RequireNArgs(0),
|
|
),
|
|
Options: serpent.OptionSet{
|
|
{
|
|
Name: "status",
|
|
Description: "Filter by task status.",
|
|
Flag: "status",
|
|
Default: "",
|
|
Value: serpent.EnumOf(&statusFilter, slice.ToStrings(codersdk.AllTaskStatuses())...),
|
|
},
|
|
{
|
|
Name: "all",
|
|
Description: "List tasks for all users you can view.",
|
|
Flag: "all",
|
|
FlagShorthand: "a",
|
|
Default: "false",
|
|
Value: serpent.BoolOf(&all),
|
|
},
|
|
{
|
|
Name: "user",
|
|
Description: "List tasks for the specified user (username, \"me\").",
|
|
Flag: "user",
|
|
Default: "",
|
|
Value: serpent.StringOf(&user),
|
|
},
|
|
{
|
|
Name: "quiet",
|
|
Description: "Only display task IDs.",
|
|
Flag: "quiet",
|
|
FlagShorthand: "q",
|
|
Default: "false",
|
|
Value: serpent.BoolOf(&quiet),
|
|
},
|
|
},
|
|
Handler: func(inv *serpent.Invocation) error {
|
|
client, err := r.InitClient(inv)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx := inv.Context()
|
|
|
|
targetUser := strings.TrimSpace(user)
|
|
if targetUser == "" && !all {
|
|
targetUser = codersdk.Me
|
|
}
|
|
|
|
tasks, err := client.Tasks(ctx, &codersdk.TasksFilter{
|
|
Owner: targetUser,
|
|
Status: codersdk.TaskStatus(statusFilter),
|
|
})
|
|
if err != nil {
|
|
return xerrors.Errorf("list tasks: %w", err)
|
|
}
|
|
|
|
if quiet {
|
|
for _, task := range tasks {
|
|
_, _ = fmt.Fprintln(inv.Stdout, task.ID.String())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
rows := make([]taskListRow, len(tasks))
|
|
now := time.Now()
|
|
for i := range tasks {
|
|
rows[i] = taskListRowFromTask(now, tasks[i])
|
|
}
|
|
|
|
out, err := formatter.Format(ctx, rows)
|
|
if err != nil {
|
|
return xerrors.Errorf("format tasks: %w", err)
|
|
}
|
|
if out == "" {
|
|
cliui.Infof(inv.Stderr, "No tasks found.")
|
|
return nil
|
|
}
|
|
_, _ = fmt.Fprintln(inv.Stdout, out)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
formatter.AttachOptions(&cmd.Options)
|
|
return cmd
|
|
}
|