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

Commit 74a130a

Browse files
committed
Migrate to cobra
1 parent 1261969 commit 74a130a

14 files changed

+361
-340
lines changed

ci/integration/integration_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,21 @@ func TestCoderCLI(t *testing.T) {
3939

4040
c.Run(ctx, "coder --help").Assert(t,
4141
tcli.Success(),
42-
tcli.StdoutMatches("COMMANDS:"),
43-
tcli.StdoutMatches("USAGE:"),
42+
tcli.StdoutMatches("Available Commands"),
4443
)
4544

4645
headlessLogin(ctx, t, c)
4746

4847
c.Run(ctx, "coder envs").Assert(t,
49-
tcli.Error(),
48+
tcli.Success(),
5049
)
5150

5251
c.Run(ctx, "coder envs ls").Assert(t,
5352
tcli.Success(),
5453
)
5554

5655
c.Run(ctx, "coder urls").Assert(t,
57-
tcli.Error(),
56+
tcli.Success(),
5857
)
5958

6059
c.Run(ctx, "coder sync").Assert(t,

ci/integration/secrets_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func TestSecrets(t *testing.T) {
3939
)
4040

4141
// this tests the "Value:" prompt fallback
42-
c.Run(ctx, fmt.Sprintf("echo %s | coder secrets create --from-prompt %s", value, name)).Assert(t,
42+
c.Run(ctx, fmt.Sprintf("echo %s | coder secrets create %s --from-prompt", value, name)).Assert(t,
4343
tcli.Success(),
4444
tcli.StderrEmpty(),
4545
)
@@ -70,7 +70,7 @@ func TestSecrets(t *testing.T) {
7070

7171
name, value = randString(8), randString(8)
7272

73-
c.Run(ctx, fmt.Sprintf("coder secrets create --from-literal %s %s", value, name)).Assert(t,
73+
c.Run(ctx, fmt.Sprintf("coder secrets create %s --from-literal %s", name, value)).Assert(t,
7474
tcli.Success(),
7575
tcli.StderrEmpty(),
7676
)
@@ -84,7 +84,7 @@ func TestSecrets(t *testing.T) {
8484
c.Run(ctx, fmt.Sprintf("echo %s > ~/secret.json", value)).Assert(t,
8585
tcli.Success(),
8686
)
87-
c.Run(ctx, fmt.Sprintf("coder secrets create --from-file ~/secret.json %s", name)).Assert(t,
87+
c.Run(ctx, fmt.Sprintf("coder secrets create %s --from-file ~/secret.json", name)).Assert(t,
8888
tcli.Success(),
8989
)
9090
//

cmd/coder/configssh.go

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,29 @@ import (
1313

1414
"cdr.dev/coder-cli/internal/config"
1515
"cdr.dev/coder-cli/internal/entclient"
16-
"github.com/urfave/cli/v2"
16+
"github.com/spf13/cobra"
1717
"golang.org/x/xerrors"
1818
)
1919

20-
func makeConfigSSHCmd() *cli.Command {
20+
func makeConfigSSHCmd() *cobra.Command {
2121
var (
2222
configpath string
2323
remove = false
2424
)
2525

26-
return &cli.Command{
27-
Name: "config-ssh",
28-
Usage: "Configure SSH to access Coder environments",
29-
Description: "Inject the proper OpenSSH configuration into your local SSH config file.",
30-
Action: configSSH(&configpath, &remove),
31-
Flags: []cli.Flag{
32-
&cli.StringFlag{
33-
Name: "filepath",
34-
Usage: "overide the default path of your ssh config file",
35-
Value: filepath.Join(os.Getenv("HOME"), ".ssh", "config"),
36-
TakesFile: true,
37-
Destination: &configpath,
38-
},
39-
&cli.BoolFlag{
40-
Name: "remove",
41-
Usage: "remove the auto-generated Coder Enterprise ssh config",
42-
Destination: &remove,
43-
},
44-
},
26+
cmd := &cobra.Command{
27+
Use: "config-ssh",
28+
Short: "Configure SSH to access Coder environments",
29+
Long: "Inject the proper OpenSSH configuration into your local SSH config file.",
30+
RunE: configSSH(&configpath, &remove),
4531
}
32+
cmd.Flags().StringVar(&configpath, "filepath", filepath.Join(os.Getenv("HOME"), ".ssh", "config"), "overide the default path of your ssh config file")
33+
cmd.Flags().BoolVar(&remove, "remove", false, "remove the auto-generated Coder Enterprise ssh config")
34+
35+
return cmd
4636
}
4737

48-
func configSSH(filepath *string, remove *bool) func(c *cli.Context) error {
38+
func configSSH(filepath *string, remove *bool) func(cmd *cobra.Command, _ []string) error {
4939
startToken := "# ------------START-CODER-ENTERPRISE-----------"
5040
startMessage := `# The following has been auto-generated by "coder config-ssh"
5141
# to make accessing your Coder Enterprise environments easier.
@@ -57,7 +47,7 @@ func configSSH(filepath *string, remove *bool) func(c *cli.Context) error {
5747
# You should not hand-edit this section, unless you are deleting it.`
5848
endToken := "# ------------END-CODER-ENTERPRISE------------"
5949

60-
return func(c *cli.Context) error {
50+
return func(cmd *cobra.Command, _ []string) error {
6151
ctx, cancel := context.WithCancel(context.Background())
6252
defer cancel()
6353

cmd/coder/envs.go

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,50 @@ import (
55
"os"
66

77
"cdr.dev/coder-cli/internal/x/xtabwriter"
8-
"github.com/urfave/cli/v2"
8+
"github.com/spf13/cobra"
99
"golang.org/x/xerrors"
1010
)
1111

12-
func makeEnvsCommand() *cli.Command {
12+
func makeEnvsCommand() *cobra.Command {
1313
var outputFmt string
14-
return &cli.Command{
15-
Name: "envs",
16-
Usage: "Interact with Coder environments",
17-
Description: "Perform operations on the Coder environments owned by the active user.",
18-
Action: exitHelp,
19-
Subcommands: []*cli.Command{
20-
{
21-
Name: "ls",
22-
Usage: "list all environments owned by the active user",
23-
Description: "List all Coder environments owned by the active user.",
24-
ArgsUsage: "[...flags]",
25-
Action: func(c *cli.Context) error {
26-
entClient := requireAuth()
27-
envs, err := getEnvs(entClient)
28-
if err != nil {
29-
return err
30-
}
14+
cmd := &cobra.Command{
15+
Use: "envs",
16+
Short: "Interact with Coder environments",
17+
Long: "Perform operations on the Coder environments owned by the active user.",
18+
}
19+
20+
lsCmd := &cobra.Command{
21+
Use: "ls",
22+
Short: "list all environments owned by the active user",
23+
Long: "List all Coder environments owned by the active user.",
24+
RunE: func(cmd *cobra.Command, args []string) error {
25+
entClient := requireAuth()
26+
envs, err := getEnvs(entClient)
27+
if err != nil {
28+
return err
29+
}
3130

32-
switch outputFmt {
33-
case "human":
34-
err := xtabwriter.WriteTable(len(envs), func(i int) interface{} {
35-
return envs[i]
36-
})
37-
if err != nil {
38-
return xerrors.Errorf("write table: %w", err)
39-
}
40-
case "json":
41-
err := json.NewEncoder(os.Stdout).Encode(envs)
42-
if err != nil {
43-
return xerrors.Errorf("write environments as JSON: %w", err)
44-
}
45-
default:
46-
return xerrors.Errorf("unknown --output value %q", outputFmt)
47-
}
48-
return nil
49-
},
50-
Flags: []cli.Flag{
51-
&cli.StringFlag{
52-
Name: "output",
53-
Aliases: []string{"o"},
54-
Usage: "json | human",
55-
Value: "human",
56-
Destination: &outputFmt,
57-
},
58-
},
59-
},
31+
switch outputFmt {
32+
case "human":
33+
err := xtabwriter.WriteTable(len(envs), func(i int) interface{} {
34+
return envs[i]
35+
})
36+
if err != nil {
37+
return xerrors.Errorf("write table: %w", err)
38+
}
39+
case "json":
40+
err := json.NewEncoder(os.Stdout).Encode(envs)
41+
if err != nil {
42+
return xerrors.Errorf("write environments as JSON: %w", err)
43+
}
44+
default:
45+
return xerrors.Errorf("unknown --output value %q", outputFmt)
46+
}
47+
return nil
6048
},
6149
}
50+
lsCmd.Flags().StringVarP(&outputFmt, "output", "o", "human", "human | json")
51+
cmd.AddCommand(lsCmd)
52+
53+
return cmd
6254
}

cmd/coder/login.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,24 @@ import (
1010
"cdr.dev/coder-cli/internal/config"
1111
"cdr.dev/coder-cli/internal/loginsrv"
1212
"github.com/pkg/browser"
13-
"github.com/urfave/cli/v2"
13+
"github.com/spf13/cobra"
1414
"golang.org/x/xerrors"
1515

1616
"go.coder.com/flog"
1717
)
1818

19-
func makeLoginCmd() *cli.Command {
20-
return &cli.Command{
21-
Name: "login",
22-
Usage: "Authenticate this client for future operations",
23-
ArgsUsage: "[Coder Enterprise URL eg. http://my.coder.domain/]",
24-
Action: login,
19+
func makeLoginCmd() *cobra.Command {
20+
cmd := &cobra.Command{
21+
Use: "login [Coder Enterprise URL eg. http://my.coder.domain/]",
22+
Short: "Authenticate this client for future operations",
23+
Args: cobra.ExactArgs(1),
24+
RunE: login,
2525
}
26+
return cmd
2627
}
2728

28-
func login(c *cli.Context) error {
29-
rawURL := c.Args().First()
29+
func login(cmd *cobra.Command, args []string) error {
30+
rawURL := args[0]
3031
if rawURL == "" || !strings.HasPrefix(rawURL, "http") {
3132
return xerrors.Errorf("invalid URL")
3233
}

cmd/coder/logout.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ import (
44
"os"
55

66
"cdr.dev/coder-cli/internal/config"
7-
"github.com/urfave/cli/v2"
7+
"github.com/spf13/cobra"
88
"golang.org/x/xerrors"
99

1010
"go.coder.com/flog"
1111
)
1212

13-
func makeLogoutCmd() *cli.Command {
14-
return &cli.Command{
15-
Name: "logout",
16-
Usage: "Remove local authentication credentials if any exist",
17-
Action: logout,
13+
func makeLogoutCmd() *cobra.Command {
14+
return &cobra.Command{
15+
Use: "logout",
16+
Short: "Remove local authentication credentials if any exist",
17+
RunE: logout,
1818
}
1919
}
2020

21-
func logout(_ *cli.Context) error {
21+
func logout(_ *cobra.Command, _ []string) error {
2222
err := config.Session.Delete()
2323
if err != nil {
2424
if os.IsNotExist(err) {

cmd/coder/main.go

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"runtime"
1010

1111
"cdr.dev/coder-cli/internal/x/xterminal"
12-
"github.com/urfave/cli/v2"
12+
"github.com/spf13/cobra"
1313

1414
"go.coder.com/flog"
1515
)
@@ -31,16 +31,13 @@ func main() {
3131
}
3232
defer xterminal.Restore(os.Stdout.Fd(), stdoutState)
3333

34-
app := cli.NewApp()
35-
app.Name = "coder"
36-
app.Usage = "coder provides a CLI for working with an existing Coder Enterprise installation"
37-
app.Version = fmt.Sprintf("%s %s %s/%s", version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
38-
app.CommandNotFound = func(c *cli.Context, s string) {
39-
flog.Fatal("command %q not found", s)
34+
app := &cobra.Command{
35+
Use: "coder",
36+
Short: "coder provides a CLI for working with an existing Coder Enterprise installation",
37+
Version: fmt.Sprintf("%s %s %s/%s", version, runtime.Version(), runtime.GOOS, runtime.GOARCH),
4038
}
41-
app.Action = exitHelp
4239

43-
app.Commands = []*cli.Command{
40+
app.AddCommand(
4441
makeLoginCmd(),
4542
makeLogoutCmd(),
4643
makeShellCmd(),
@@ -50,14 +47,62 @@ func main() {
5047
makeEnvsCommand(),
5148
makeSyncCmd(),
5249
makeURLCmd(),
53-
}
54-
err = app.Run(os.Args)
50+
completionCmd,
51+
)
52+
err = app.Execute()
5553
if err != nil {
56-
flog.Fatal("%v", err)
54+
os.Exit(1)
5755
}
5856
}
5957

60-
func exitHelp(c *cli.Context) error {
61-
cli.ShowCommandHelpAndExit(c, c.Command.FullName(), 1)
62-
return nil
58+
// reference: https://github.com/spf13/cobra/blob/master/shell_completions.md
59+
var completionCmd = &cobra.Command{
60+
Use: "completion [bash|zsh|fish|powershell]",
61+
Short: "Generate completion script",
62+
Long: `To load completions:
63+
64+
Bash:
65+
66+
$ source <(yourprogram completion bash)
67+
68+
# To load completions for each session, execute once:
69+
Linux:
70+
$ yourprogram completion bash > /etc/bash_completion.d/yourprogram
71+
MacOS:
72+
$ yourprogram completion bash > /usr/local/etc/bash_completion.d/yourprogram
73+
74+
Zsh:
75+
76+
# If shell completion is not already enabled in your environment you will need
77+
# to enable it. You can execute the following once:
78+
79+
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
80+
81+
# To load completions for each session, execute once:
82+
$ yourprogram completion zsh > "${fpath[1]}/_yourprogram"
83+
84+
# You will need to start a new shell for this setup to take effect.
85+
86+
Fish:
87+
88+
$ yourprogram completion fish | source
89+
90+
# To load completions for each session, execute once:
91+
$ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish
92+
`,
93+
DisableFlagsInUseLine: true,
94+
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
95+
Args: cobra.ExactValidArgs(1),
96+
Run: func(cmd *cobra.Command, args []string) {
97+
switch args[0] {
98+
case "bash":
99+
cmd.Root().GenBashCompletion(os.Stdout)
100+
case "zsh":
101+
cmd.Root().GenZshCompletion(os.Stdout)
102+
case "fish":
103+
cmd.Root().GenFishCompletion(os.Stdout, true)
104+
case "powershell":
105+
cmd.Root().GenPowerShellCompletion(os.Stdout)
106+
}
107+
},
63108
}

0 commit comments

Comments
 (0)