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

Migrate to cobra #86

Merged
merged 10 commits into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Migrate to cobra
  • Loading branch information
cmoog committed Aug 10, 2020
commit 74a130af680122b25dae8eb99b90e00cad9339f0
7 changes: 3 additions & 4 deletions ci/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,21 @@ func TestCoderCLI(t *testing.T) {

c.Run(ctx, "coder --help").Assert(t,
tcli.Success(),
tcli.StdoutMatches("COMMANDS:"),
tcli.StdoutMatches("USAGE:"),
tcli.StdoutMatches("Available Commands"),
)

headlessLogin(ctx, t, c)

c.Run(ctx, "coder envs").Assert(t,
tcli.Error(),
tcli.Success(),
)

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

c.Run(ctx, "coder urls").Assert(t,
tcli.Error(),
tcli.Success(),
)

c.Run(ctx, "coder sync").Assert(t,
Expand Down
6 changes: 3 additions & 3 deletions ci/integration/secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestSecrets(t *testing.T) {
)

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

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

c.Run(ctx, fmt.Sprintf("coder secrets create --from-literal %s %s", value, name)).Assert(t,
c.Run(ctx, fmt.Sprintf("coder secrets create %s --from-literal %s", name, value)).Assert(t,
tcli.Success(),
tcli.StderrEmpty(),
)
Expand All @@ -84,7 +84,7 @@ func TestSecrets(t *testing.T) {
c.Run(ctx, fmt.Sprintf("echo %s > ~/secret.json", value)).Assert(t,
tcli.Success(),
)
c.Run(ctx, fmt.Sprintf("coder secrets create --from-file ~/secret.json %s", name)).Assert(t,
c.Run(ctx, fmt.Sprintf("coder secrets create %s --from-file ~/secret.json", name)).Assert(t,
tcli.Success(),
)
//
Expand Down
36 changes: 13 additions & 23 deletions cmd/coder/configssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,29 @@ import (

"cdr.dev/coder-cli/internal/config"
"cdr.dev/coder-cli/internal/entclient"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"golang.org/x/xerrors"
)

func makeConfigSSHCmd() *cli.Command {
func makeConfigSSHCmd() *cobra.Command {
var (
configpath string
remove = false
)

return &cli.Command{
Name: "config-ssh",
Usage: "Configure SSH to access Coder environments",
Description: "Inject the proper OpenSSH configuration into your local SSH config file.",
Action: configSSH(&configpath, &remove),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "filepath",
Usage: "overide the default path of your ssh config file",
Value: filepath.Join(os.Getenv("HOME"), ".ssh", "config"),
TakesFile: true,
Destination: &configpath,
},
&cli.BoolFlag{
Name: "remove",
Usage: "remove the auto-generated Coder Enterprise ssh config",
Destination: &remove,
},
},
cmd := &cobra.Command{
Use: "config-ssh",
Short: "Configure SSH to access Coder environments",
Long: "Inject the proper OpenSSH configuration into your local SSH config file.",
RunE: configSSH(&configpath, &remove),
}
cmd.Flags().StringVar(&configpath, "filepath", filepath.Join(os.Getenv("HOME"), ".ssh", "config"), "overide the default path of your ssh config file")
cmd.Flags().BoolVar(&remove, "remove", false, "remove the auto-generated Coder Enterprise ssh config")

return cmd
}

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

return func(c *cli.Context) error {
return func(cmd *cobra.Command, _ []string) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

Expand Down
86 changes: 39 additions & 47 deletions cmd/coder/envs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,50 @@ import (
"os"

"cdr.dev/coder-cli/internal/x/xtabwriter"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"golang.org/x/xerrors"
)

func makeEnvsCommand() *cli.Command {
func makeEnvsCommand() *cobra.Command {
var outputFmt string
return &cli.Command{
Name: "envs",
Usage: "Interact with Coder environments",
Description: "Perform operations on the Coder environments owned by the active user.",
Action: exitHelp,
Subcommands: []*cli.Command{
{
Name: "ls",
Usage: "list all environments owned by the active user",
Description: "List all Coder environments owned by the active user.",
ArgsUsage: "[...flags]",
Action: func(c *cli.Context) error {
entClient := requireAuth()
envs, err := getEnvs(entClient)
if err != nil {
return err
}
cmd := &cobra.Command{
Use: "envs",
Short: "Interact with Coder environments",
Long: "Perform operations on the Coder environments owned by the active user.",
}

lsCmd := &cobra.Command{
Use: "ls",
Short: "list all environments owned by the active user",
Long: "List all Coder environments owned by the active user.",
RunE: func(cmd *cobra.Command, args []string) error {
entClient := requireAuth()
envs, err := getEnvs(entClient)
if err != nil {
return err
}

switch outputFmt {
case "human":
err := xtabwriter.WriteTable(len(envs), func(i int) interface{} {
return envs[i]
})
if err != nil {
return xerrors.Errorf("write table: %w", err)
}
case "json":
err := json.NewEncoder(os.Stdout).Encode(envs)
if err != nil {
return xerrors.Errorf("write environments as JSON: %w", err)
}
default:
return xerrors.Errorf("unknown --output value %q", outputFmt)
}
return nil
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "json | human",
Value: "human",
Destination: &outputFmt,
},
},
},
switch outputFmt {
case "human":
err := xtabwriter.WriteTable(len(envs), func(i int) interface{} {
return envs[i]
})
if err != nil {
return xerrors.Errorf("write table: %w", err)
}
case "json":
err := json.NewEncoder(os.Stdout).Encode(envs)
if err != nil {
return xerrors.Errorf("write environments as JSON: %w", err)
}
default:
return xerrors.Errorf("unknown --output value %q", outputFmt)
}
return nil
},
}
lsCmd.Flags().StringVarP(&outputFmt, "output", "o", "human", "human | json")
cmd.AddCommand(lsCmd)

return cmd
}
19 changes: 10 additions & 9 deletions cmd/coder/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@ import (
"cdr.dev/coder-cli/internal/config"
"cdr.dev/coder-cli/internal/loginsrv"
"github.com/pkg/browser"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"golang.org/x/xerrors"

"go.coder.com/flog"
)

func makeLoginCmd() *cli.Command {
return &cli.Command{
Name: "login",
Usage: "Authenticate this client for future operations",
ArgsUsage: "[Coder Enterprise URL eg. http://my.coder.domain/]",
Action: login,
func makeLoginCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "login [Coder Enterprise URL eg. http://my.coder.domain/]",
Short: "Authenticate this client for future operations",
Args: cobra.ExactArgs(1),
RunE: login,
}
return cmd
}

func login(c *cli.Context) error {
rawURL := c.Args().First()
func login(cmd *cobra.Command, args []string) error {
rawURL := args[0]
if rawURL == "" || !strings.HasPrefix(rawURL, "http") {
return xerrors.Errorf("invalid URL")
}
Expand Down
14 changes: 7 additions & 7 deletions cmd/coder/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import (
"os"

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

"go.coder.com/flog"
)

func makeLogoutCmd() *cli.Command {
return &cli.Command{
Name: "logout",
Usage: "Remove local authentication credentials if any exist",
Action: logout,
func makeLogoutCmd() *cobra.Command {
return &cobra.Command{
Use: "logout",
Short: "Remove local authentication credentials if any exist",
RunE: logout,
}
}

func logout(_ *cli.Context) error {
func logout(_ *cobra.Command, _ []string) error {
err := config.Session.Delete()
if err != nil {
if os.IsNotExist(err) {
Expand Down
75 changes: 60 additions & 15 deletions cmd/coder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"runtime"

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

"go.coder.com/flog"
)
Expand All @@ -31,16 +31,13 @@ func main() {
}
defer xterminal.Restore(os.Stdout.Fd(), stdoutState)

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

app.Commands = []*cli.Command{
app.AddCommand(
makeLoginCmd(),
makeLogoutCmd(),
makeShellCmd(),
Expand All @@ -50,14 +47,62 @@ func main() {
makeEnvsCommand(),
makeSyncCmd(),
makeURLCmd(),
}
err = app.Run(os.Args)
completionCmd,
)
err = app.Execute()
if err != nil {
flog.Fatal("%v", err)
os.Exit(1)
}
}

func exitHelp(c *cli.Context) error {
cli.ShowCommandHelpAndExit(c, c.Command.FullName(), 1)
return nil
// reference: https://github.com/spf13/cobra/blob/master/shell_completions.md
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: `To load completions:

Bash:

$ source <(yourprogram completion bash)

# To load completions for each session, execute once:
Linux:
$ yourprogram completion bash > /etc/bash_completion.d/yourprogram
MacOS:
$ yourprogram completion bash > /usr/local/etc/bash_completion.d/yourprogram

Zsh:

# If shell completion is not already enabled in your environment you will need
# to enable it. You can execute the following once:

$ echo "autoload -U compinit; compinit" >> ~/.zshrc

# To load completions for each session, execute once:
$ yourprogram completion zsh > "${fpath[1]}/_yourprogram"

# You will need to start a new shell for this setup to take effect.

Fish:

$ yourprogram completion fish | source

# To load completions for each session, execute once:
$ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletion(os.Stdout)
}
},
}
Loading