Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 2cc7d2d

Browse files
committed
Add table output to envs ls
1 parent 5c75570 commit 2cc7d2d

File tree

8 files changed

+77
-7
lines changed

8 files changed

+77
-7
lines changed

ci/integration/integration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestCoderCLI(t *testing.T) {
4949
headlessLogin(ctx, t, c)
5050

5151
c.Run(ctx, "coder envs").Assert(t,
52-
tcli.Success(),
52+
tcli.Error(),
5353
)
5454

5555
c.Run(ctx, "coder envs ls").Assert(t,

cmd/coder/envs.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"fmt"
55

6+
"cdr.dev/coder-cli/internal/x/xtabwriter"
67
"github.com/urfave/cli"
78
)
89

@@ -11,6 +12,7 @@ func makeEnvsCommand() cli.Command {
1112
Name: "envs",
1213
Usage: "Interact with Coder environments",
1314
Description: "Perform operations on the Coder environments owned by the active user.",
15+
Action: exitHelp,
1416
Subcommands: []cli.Command{
1517
{
1618
Name: "ls",
@@ -21,9 +23,17 @@ func makeEnvsCommand() cli.Command {
2123
entClient := requireAuth()
2224
envs := getEnvs(entClient)
2325

26+
w := xtabwriter.NewWriter()
27+
if len(envs) > 0 {
28+
_, err := fmt.Fprintln(w, xtabwriter.StructFieldNames(envs[0]))
29+
requireSuccess(err, "failed to write header: %v", err)
30+
}
2431
for _, env := range envs {
25-
fmt.Println(env.Name)
32+
_, err := fmt.Fprintln(w, xtabwriter.StructValues(env))
33+
requireSuccess(err, "failed to write row: %v", err)
2634
}
35+
err := w.Flush()
36+
requireSuccess(err, "failed to flush tab writer: %v", err)
2737
},
2838
Flags: nil,
2939
},

cmd/coder/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func main() {
4040
flog.Fatal("command %q not found", s)
4141
}
4242
app.Email = "support@coder.com"
43+
app.Action = exitHelp
4344

4445
app.Commands = []cli.Command{
4546
makeLoginCmd(),
@@ -63,3 +64,7 @@ func requireSuccess(err error, msg string, args ...interface{}) {
6364
flog.Fatal(msg, args...)
6465
}
6566
}
67+
68+
func exitHelp(c *cli.Context) {
69+
cli.ShowCommandHelpAndExit(c, c.Command.FullName(), 1)
70+
}

cmd/coder/secrets.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func makeSecretsCmd() cli.Command {
1919
Name: "secrets",
2020
Usage: "Interact with Coder Secrets",
2121
Description: "Interact with secrets objects owned by the active user.",
22+
Action: exitHelp,
2223
Subcommands: []cli.Command{
2324
{
2425
Name: "ls",

cmd/coder/users.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import (
1414
func makeUsersCmd() cli.Command {
1515
var output string
1616
return cli.Command{
17-
Name: "users",
18-
Usage: "Interact with Coder user accounts",
17+
Name: "users",
18+
Usage: "Interact with Coder user accounts",
19+
Action: exitHelp,
1920
Subcommands: []cli.Command{
2021
{
2122
Name: "ls",

internal/entclient/env.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,33 @@ import (
44
"context"
55
"time"
66

7+
"cdr.dev/coder-cli/internal/x/xjson"
78
"nhooyr.io/websocket"
89
)
910

1011
// Environment describes a Coder environment
1112
type Environment struct {
12-
Name string `json:"name"`
13-
ID string `json:"id"`
13+
ID string `json:"id" tab:"-"`
14+
Name string `json:"name"`
15+
ImageID string `json:"image_id" tab:"-"`
16+
ImageTag string `json:"image_tag"`
17+
OrganizationID string `json:"organization_id" tab:"-"`
18+
UserID string `json:"user_id" tab:"-"`
19+
LastBuiltAt time.Time `json:"last_built_at" tab:"-"`
20+
CPUCores float32 `json:"cpu_cores"`
21+
MemoryGB int `json:"memory_gb"`
22+
DiskGB int `json:"disk_gb"`
23+
GPUs int `json:"gpus"`
24+
Updating bool `json:"updating"`
25+
RebuildMessages []struct {
26+
Text string `json:"text"`
27+
Required bool `json:"required"`
28+
} `json:"rebuild_messages" tab:"-"`
29+
CreatedAt time.Time `json:"created_at" tab:"-"`
30+
UpdatedAt time.Time `json:"updated_at" tab:"-"`
31+
LastOpenedAt time.Time `json:"last_opened_at" tab:"-"`
32+
LastConnectionAt time.Time `json:"last_connection_at" tab:"-"`
33+
AutoOffThreshold xjson.Duration `json:"auto_off_threshold" tab:"-"`
1434
}
1535

1636
// Envs gets the list of environments owned by the authenticated user

internal/x/xjson/duration.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package xjson
2+
3+
import (
4+
"encoding/json"
5+
"strconv"
6+
"time"
7+
)
8+
9+
// Duration is a time.Duration that marshals to millisecond precision.
10+
// Most javascript applications expect durations to be in milliseconds.
11+
type Duration time.Duration
12+
13+
// MarshalJSON marshals the duration to millisecond precision.
14+
func (d Duration) MarshalJSON() ([]byte, error) {
15+
du := time.Duration(d)
16+
return json.Marshal(du.Milliseconds())
17+
}
18+
19+
// UnmarshalJSON unmarshals a millisecond-precision integer to
20+
// a time.Duration.
21+
func (d *Duration) UnmarshalJSON(b []byte) error {
22+
i, err := strconv.ParseInt(string(b), 10, 64)
23+
if err != nil {
24+
return err
25+
}
26+
27+
*d = Duration(time.Duration(i) * time.Millisecond)
28+
return nil
29+
}
30+
31+
func (d Duration) String() string {
32+
return time.Duration(d).String()
33+
}

internal/x/xtabwriter/tabwriter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func StructValues(data interface{}) string {
2525
if shouldHideField(v.Type().Field(i)) {
2626
continue
2727
}
28-
s.WriteString(fmt.Sprintf("%s\t", v.Field(i).Interface()))
28+
s.WriteString(fmt.Sprintf("%v\t", v.Field(i).Interface()))
2929
}
3030
return s.String()
3131
}

0 commit comments

Comments
 (0)