From 9b61d347608cd8d7989a9eee251bba97b346ebbd Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Wed, 12 Aug 2020 13:09:09 -0500 Subject: [PATCH 1/4] Move cmd to internal package --- cmd/coder/main.go | 73 +-------------------- {cmd/coder => internal/cmd}/auth.go | 2 +- {cmd/coder => internal/cmd}/ceapi.go | 2 +- internal/cmd/cmd.go | 80 ++++++++++++++++++++++++ {cmd/coder => internal/cmd}/configssh.go | 2 +- {cmd/coder => internal/cmd}/envs.go | 2 +- {cmd/coder => internal/cmd}/login.go | 2 +- {cmd/coder => internal/cmd}/logout.go | 2 +- {cmd/coder => internal/cmd}/secrets.go | 2 +- {cmd/coder => internal/cmd}/shell.go | 2 +- {cmd/coder => internal/cmd}/sync.go | 2 +- {cmd/coder => internal/cmd}/urls.go | 2 +- {cmd/coder => internal/cmd}/users.go | 2 +- 13 files changed, 94 insertions(+), 81 deletions(-) rename {cmd/coder => internal/cmd}/auth.go (98%) rename {cmd/coder => internal/cmd}/ceapi.go (99%) create mode 100644 internal/cmd/cmd.go rename {cmd/coder => internal/cmd}/configssh.go (99%) rename {cmd/coder => internal/cmd}/envs.go (99%) rename {cmd/coder => internal/cmd}/login.go (99%) rename {cmd/coder => internal/cmd}/logout.go (97%) rename {cmd/coder => internal/cmd}/secrets.go (99%) rename {cmd/coder => internal/cmd}/shell.go (99%) rename {cmd/coder => internal/cmd}/sync.go (99%) rename {cmd/coder => internal/cmd}/urls.go (99%) rename {cmd/coder => internal/cmd}/users.go (99%) diff --git a/cmd/coder/main.go b/cmd/coder/main.go index 5cf3454b..5ed4344b 100644 --- a/cmd/coder/main.go +++ b/cmd/coder/main.go @@ -9,8 +9,8 @@ import ( "os" "runtime" + "cdr.dev/coder-cli/internal/cmd" "cdr.dev/coder-cli/internal/x/xterminal" - "github.com/spf13/cobra" "go.coder.com/flog" ) @@ -35,78 +35,11 @@ func main() { } defer xterminal.Restore(os.Stdout.Fd(), stdoutState) - 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 := cmd.Make() + app.Version = fmt.Sprintf("%s %s %s/%s", version, runtime.Version(), runtime.GOOS, runtime.GOARCH) - app.AddCommand( - makeLoginCmd(), - makeLogoutCmd(), - makeShellCmd(), - makeUsersCmd(), - makeConfigSSHCmd(), - makeSecretsCmd(), - makeEnvsCommand(), - makeSyncCmd(), - makeURLCmd(), - completionCmd, - ) err = app.ExecuteContext(ctx) if err != nil { os.Exit(1) } } - -// 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) - } - }, -} diff --git a/cmd/coder/auth.go b/internal/cmd/auth.go similarity index 98% rename from cmd/coder/auth.go rename to internal/cmd/auth.go index abd0e8d0..64bcfb23 100644 --- a/cmd/coder/auth.go +++ b/internal/cmd/auth.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "net/url" diff --git a/cmd/coder/ceapi.go b/internal/cmd/ceapi.go similarity index 99% rename from cmd/coder/ceapi.go rename to internal/cmd/ceapi.go index 823f36ff..b69788fc 100644 --- a/cmd/coder/ceapi.go +++ b/internal/cmd/ceapi.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "context" diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go new file mode 100644 index 00000000..ad28fa39 --- /dev/null +++ b/internal/cmd/cmd.go @@ -0,0 +1,80 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +func Make() *cobra.Command { + app := &cobra.Command{ + Use: "coder", + Short: "coder provides a CLI for working with an existing Coder Enterprise installation", + } + + app.AddCommand( + makeLoginCmd(), + makeLogoutCmd(), + makeShellCmd(), + makeUsersCmd(), + makeConfigSSHCmd(), + makeSecretsCmd(), + makeEnvsCommand(), + makeSyncCmd(), + makeURLCmd(), + completionCmd, + ) + return app +} + +// 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 <(coder completion bash) + +# To load completions for each session, execute once: +Linux: + $ coder completion bash > /etc/bash_completion.d/coder +MacOS: + $ coder completion bash > /usr/local/etc/bash_completion.d/coder + +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: +$ coder completion zsh > "${fpath[1]}/_coder" + +# You will need to start a new shell for this setup to take effect. + +Fish: + +$ coder completion fish | source + +# To load completions for each session, execute once: +$ coder completion fish > ~/.config/fish/completions/coder.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) + } + }, +} diff --git a/cmd/coder/configssh.go b/internal/cmd/configssh.go similarity index 99% rename from cmd/coder/configssh.go rename to internal/cmd/configssh.go index 50b991d4..fc67ee1a 100644 --- a/cmd/coder/configssh.go +++ b/internal/cmd/configssh.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "context" diff --git a/cmd/coder/envs.go b/internal/cmd/envs.go similarity index 99% rename from cmd/coder/envs.go rename to internal/cmd/envs.go index 32d73245..4e3219b7 100644 --- a/cmd/coder/envs.go +++ b/internal/cmd/envs.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "encoding/json" diff --git a/cmd/coder/login.go b/internal/cmd/login.go similarity index 99% rename from cmd/coder/login.go rename to internal/cmd/login.go index ed8cb499..d81dac0e 100644 --- a/cmd/coder/login.go +++ b/internal/cmd/login.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "net" diff --git a/cmd/coder/logout.go b/internal/cmd/logout.go similarity index 97% rename from cmd/coder/logout.go rename to internal/cmd/logout.go index 079a2e56..621ed89e 100644 --- a/cmd/coder/logout.go +++ b/internal/cmd/logout.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "os" diff --git a/cmd/coder/secrets.go b/internal/cmd/secrets.go similarity index 99% rename from cmd/coder/secrets.go rename to internal/cmd/secrets.go index 9433588c..7303d8fa 100644 --- a/cmd/coder/secrets.go +++ b/internal/cmd/secrets.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "fmt" diff --git a/cmd/coder/shell.go b/internal/cmd/shell.go similarity index 99% rename from cmd/coder/shell.go rename to internal/cmd/shell.go index eca157e0..7cfee8b0 100644 --- a/cmd/coder/shell.go +++ b/internal/cmd/shell.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "context" diff --git a/cmd/coder/sync.go b/internal/cmd/sync.go similarity index 99% rename from cmd/coder/sync.go rename to internal/cmd/sync.go index 6cab1bc2..c41d06fd 100644 --- a/cmd/coder/sync.go +++ b/internal/cmd/sync.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "bytes" diff --git a/cmd/coder/urls.go b/internal/cmd/urls.go similarity index 99% rename from cmd/coder/urls.go rename to internal/cmd/urls.go index 7f29cdb8..ba6e5494 100644 --- a/cmd/coder/urls.go +++ b/internal/cmd/urls.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "context" diff --git a/cmd/coder/users.go b/internal/cmd/users.go similarity index 99% rename from cmd/coder/users.go rename to internal/cmd/users.go index d43d2f4b..c88dae47 100644 --- a/cmd/coder/users.go +++ b/internal/cmd/users.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "encoding/json" From b9ebf97a834e6b21b8375eafed3d1733d62e6816 Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Wed, 12 Aug 2020 10:10:32 -0500 Subject: [PATCH 2/4] Link to auto-generated docs --- README.md | 8 ++++++-- docs/coder.md | 26 +++++++++++++++++++++++ docs/coder_config-ssh.md | 24 ++++++++++++++++++++++ docs/coder_envs.md | 20 ++++++++++++++++++ docs/coder_envs_ls.md | 29 ++++++++++++++++++++++++++ docs/coder_login.md | 22 ++++++++++++++++++++ docs/coder_logout.md | 22 ++++++++++++++++++++ docs/coder_secrets.md | 23 +++++++++++++++++++++ docs/coder_secrets_create.md | 40 ++++++++++++++++++++++++++++++++++++ docs/coder_secrets_ls.md | 28 +++++++++++++++++++++++++ docs/coder_secrets_rm.md | 34 ++++++++++++++++++++++++++++++ docs/coder_secrets_view.md | 34 ++++++++++++++++++++++++++++++ docs/coder_sh.md | 28 +++++++++++++++++++++++++ docs/coder_sync.md | 23 +++++++++++++++++++++ docs/coder_urls.md | 21 +++++++++++++++++++ docs/coder_urls_create.md | 24 ++++++++++++++++++++++ docs/coder_urls_ls.md | 23 +++++++++++++++++++++ docs/coder_urls_rm.md | 22 ++++++++++++++++++++ docs/coder_users.md | 19 +++++++++++++++++ docs/coder_users_ls.md | 30 +++++++++++++++++++++++++++ internal/cmd/cmd.go | 1 + 21 files changed, 499 insertions(+), 2 deletions(-) create mode 100644 docs/coder.md create mode 100644 docs/coder_config-ssh.md create mode 100644 docs/coder_envs.md create mode 100644 docs/coder_envs_ls.md create mode 100644 docs/coder_login.md create mode 100644 docs/coder_logout.md create mode 100644 docs/coder_secrets.md create mode 100644 docs/coder_secrets_create.md create mode 100644 docs/coder_secrets_ls.md create mode 100644 docs/coder_secrets_rm.md create mode 100644 docs/coder_secrets_view.md create mode 100644 docs/coder_sh.md create mode 100644 docs/coder_sync.md create mode 100644 docs/coder_urls.md create mode 100644 docs/coder_urls_create.md create mode 100644 docs/coder_urls_ls.md create mode 100644 docs/coder_urls_rm.md create mode 100644 docs/coder_users.md create mode 100644 docs/coder_users_ls.md diff --git a/README.md b/README.md index 427ad846..95b321a4 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,14 @@ `coder` is a command line utility for Coder Enterprise. -To view usage documentation, head over to [https://enterprise.coder.com](https://enterprise.coder.com/docs/getting-started). - To report bugs and request features, please [open an issue](https://github.com/cdr/coder-cli/issues/new). +## Usage + +View the `coder-cli` documentation [here](./docs/coder.md). + +You can find additional Coder Enterprise usage documentation on [https://enterprise.coder.com](https://enterprise.coder.com/docs/getting-started). + ## Install Release Download the latest [release](https://github.com/cdr/coder-cli/releases): diff --git a/docs/coder.md b/docs/coder.md new file mode 100644 index 00000000..518eb29a --- /dev/null +++ b/docs/coder.md @@ -0,0 +1,26 @@ +## coder + +coder provides a CLI for working with an existing Coder Enterprise installation + +### Synopsis + +coder provides a CLI for working with an existing Coder Enterprise installation + +### Options + +``` + -h, --help help for coder +``` + +### SEE ALSO + +* [coder config-ssh](coder_config-ssh.md) - Configure SSH to access Coder environments +* [coder envs](coder_envs.md) - Interact with Coder environments +* [coder login](coder_login.md) - Authenticate this client for future operations +* [coder logout](coder_logout.md) - Remove local authentication credentials if any exist +* [coder secrets](coder_secrets.md) - Interact with Coder Secrets +* [coder sh](coder_sh.md) - Open a shell and execute commands in a Coder environment +* [coder sync](coder_sync.md) - Establish a one way directory sync to a Coder environment +* [coder urls](coder_urls.md) - Interact with environment DevURLs +* [coder users](coder_users.md) - Interact with Coder user accounts + diff --git a/docs/coder_config-ssh.md b/docs/coder_config-ssh.md new file mode 100644 index 00000000..b4cb3c59 --- /dev/null +++ b/docs/coder_config-ssh.md @@ -0,0 +1,24 @@ +## coder config-ssh + +Configure SSH to access Coder environments + +### Synopsis + +Inject the proper OpenSSH configuration into your local SSH config file. + +``` +coder config-ssh [flags] +``` + +### Options + +``` + --filepath string overide the default path of your ssh config file (default "~/.ssh/config") + -h, --help help for config-ssh + --remove remove the auto-generated Coder Enterprise ssh config +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation + diff --git a/docs/coder_envs.md b/docs/coder_envs.md new file mode 100644 index 00000000..30657d19 --- /dev/null +++ b/docs/coder_envs.md @@ -0,0 +1,20 @@ +## coder envs + +Interact with Coder environments + +### Synopsis + +Perform operations on the Coder environments owned by the active user. + +### Options + +``` + -h, --help help for envs + --user string Specify the user whose resources to target (default "me") +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation +* [coder envs ls](coder_envs_ls.md) - list all environments owned by the active user + diff --git a/docs/coder_envs_ls.md b/docs/coder_envs_ls.md new file mode 100644 index 00000000..df0569ec --- /dev/null +++ b/docs/coder_envs_ls.md @@ -0,0 +1,29 @@ +## coder envs ls + +list all environments owned by the active user + +### Synopsis + +List all Coder environments owned by the active user. + +``` +coder envs ls [flags] +``` + +### Options + +``` + -h, --help help for ls + -o, --output string human | json (default "human") +``` + +### Options inherited from parent commands + +``` + --user string Specify the user whose resources to target (default "me") +``` + +### SEE ALSO + +* [coder envs](coder_envs.md) - Interact with Coder environments + diff --git a/docs/coder_login.md b/docs/coder_login.md new file mode 100644 index 00000000..8551f43b --- /dev/null +++ b/docs/coder_login.md @@ -0,0 +1,22 @@ +## coder login + +Authenticate this client for future operations + +### Synopsis + +Authenticate this client for future operations + +``` +coder login [Coder Enterprise URL eg. http://my.coder.domain/] [flags] +``` + +### Options + +``` + -h, --help help for login +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation + diff --git a/docs/coder_logout.md b/docs/coder_logout.md new file mode 100644 index 00000000..5c308147 --- /dev/null +++ b/docs/coder_logout.md @@ -0,0 +1,22 @@ +## coder logout + +Remove local authentication credentials if any exist + +### Synopsis + +Remove local authentication credentials if any exist + +``` +coder logout [flags] +``` + +### Options + +``` + -h, --help help for logout +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation + diff --git a/docs/coder_secrets.md b/docs/coder_secrets.md new file mode 100644 index 00000000..917e4cf9 --- /dev/null +++ b/docs/coder_secrets.md @@ -0,0 +1,23 @@ +## coder secrets + +Interact with Coder Secrets + +### Synopsis + +Interact with secrets objects owned by the active user. + +### Options + +``` + -h, --help help for secrets + --user string Specify the user whose resources to target (default "me") +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation +* [coder secrets create](coder_secrets_create.md) - Create a new secret +* [coder secrets ls](coder_secrets_ls.md) - List all secrets owned by the active user +* [coder secrets rm](coder_secrets_rm.md) - Remove one or more secrets by name +* [coder secrets view](coder_secrets_view.md) - View a secret by name + diff --git a/docs/coder_secrets_create.md b/docs/coder_secrets_create.md new file mode 100644 index 00000000..5dfe7e1a --- /dev/null +++ b/docs/coder_secrets_create.md @@ -0,0 +1,40 @@ +## coder secrets create + +Create a new secret + +### Synopsis + +Create a new secret object to store application secrets and access them securely from within your environments. + +``` +coder secrets create [secret_name] [flags] +``` + +### Examples + +``` +coder secrets create mysql-password --from-literal 123password +coder secrets create mysql-password --from-prompt +coder secrets create aws-credentials --from-file ./credentials.json +``` + +### Options + +``` + --description string a description of the secret + --from-file string a file from which to read the value of the secret + --from-literal string the value of the secret + --from-prompt enter the secret value through a terminal prompt + -h, --help help for create +``` + +### Options inherited from parent commands + +``` + --user string Specify the user whose resources to target (default "me") +``` + +### SEE ALSO + +* [coder secrets](coder_secrets.md) - Interact with Coder Secrets + diff --git a/docs/coder_secrets_ls.md b/docs/coder_secrets_ls.md new file mode 100644 index 00000000..49f7e8b7 --- /dev/null +++ b/docs/coder_secrets_ls.md @@ -0,0 +1,28 @@ +## coder secrets ls + +List all secrets owned by the active user + +### Synopsis + +List all secrets owned by the active user + +``` +coder secrets ls [flags] +``` + +### Options + +``` + -h, --help help for ls +``` + +### Options inherited from parent commands + +``` + --user string Specify the user whose resources to target (default "me") +``` + +### SEE ALSO + +* [coder secrets](coder_secrets.md) - Interact with Coder Secrets + diff --git a/docs/coder_secrets_rm.md b/docs/coder_secrets_rm.md new file mode 100644 index 00000000..d628b148 --- /dev/null +++ b/docs/coder_secrets_rm.md @@ -0,0 +1,34 @@ +## coder secrets rm + +Remove one or more secrets by name + +### Synopsis + +Remove one or more secrets by name + +``` +coder secrets rm [...secret_name] [flags] +``` + +### Examples + +``` +coder secrets rm mysql-password mysql-user +``` + +### Options + +``` + -h, --help help for rm +``` + +### Options inherited from parent commands + +``` + --user string Specify the user whose resources to target (default "me") +``` + +### SEE ALSO + +* [coder secrets](coder_secrets.md) - Interact with Coder Secrets + diff --git a/docs/coder_secrets_view.md b/docs/coder_secrets_view.md new file mode 100644 index 00000000..f8c18ee9 --- /dev/null +++ b/docs/coder_secrets_view.md @@ -0,0 +1,34 @@ +## coder secrets view + +View a secret by name + +### Synopsis + +View a secret by name + +``` +coder secrets view [secret_name] [flags] +``` + +### Examples + +``` +coder secrets view mysql-password +``` + +### Options + +``` + -h, --help help for view +``` + +### Options inherited from parent commands + +``` + --user string Specify the user whose resources to target (default "me") +``` + +### SEE ALSO + +* [coder secrets](coder_secrets.md) - Interact with Coder Secrets + diff --git a/docs/coder_sh.md b/docs/coder_sh.md new file mode 100644 index 00000000..e32ecd65 --- /dev/null +++ b/docs/coder_sh.md @@ -0,0 +1,28 @@ +## coder sh + +Open a shell and execute commands in a Coder environment + +### Synopsis + +Execute a remote command on the environment\nIf no command is specified, the default shell is opened. + +``` +coder sh [environment_name] [] [flags] +``` + +### Examples + +``` +coder sh backend-env +``` + +### Options + +``` + -h, --help help for sh +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation + diff --git a/docs/coder_sync.md b/docs/coder_sync.md new file mode 100644 index 00000000..cf03a01e --- /dev/null +++ b/docs/coder_sync.md @@ -0,0 +1,23 @@ +## coder sync + +Establish a one way directory sync to a Coder environment + +### Synopsis + +Establish a one way directory sync to a Coder environment + +``` +coder sync [local directory] [:] [flags] +``` + +### Options + +``` + -h, --help help for sync + --init do initial transfer and exit +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation + diff --git a/docs/coder_urls.md b/docs/coder_urls.md new file mode 100644 index 00000000..dd181999 --- /dev/null +++ b/docs/coder_urls.md @@ -0,0 +1,21 @@ +## coder urls + +Interact with environment DevURLs + +### Synopsis + +Interact with environment DevURLs + +### Options + +``` + -h, --help help for urls +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation +* [coder urls create](coder_urls_create.md) - Create a new devurl for an environment +* [coder urls ls](coder_urls_ls.md) - List all DevURLs for an environment +* [coder urls rm](coder_urls_rm.md) - Remove a dev url + diff --git a/docs/coder_urls_create.md b/docs/coder_urls_create.md new file mode 100644 index 00000000..8cf4c258 --- /dev/null +++ b/docs/coder_urls_create.md @@ -0,0 +1,24 @@ +## coder urls create + +Create a new devurl for an environment + +### Synopsis + +Create a new devurl for an environment + +``` +coder urls create [env_name] [port] [--access ] [--name ] [flags] +``` + +### Options + +``` + --access string Set DevURL access to [private | org | authed | public] (default "private") + -h, --help help for create + --name string DevURL name +``` + +### SEE ALSO + +* [coder urls](coder_urls.md) - Interact with environment DevURLs + diff --git a/docs/coder_urls_ls.md b/docs/coder_urls_ls.md new file mode 100644 index 00000000..f359529e --- /dev/null +++ b/docs/coder_urls_ls.md @@ -0,0 +1,23 @@ +## coder urls ls + +List all DevURLs for an environment + +### Synopsis + +List all DevURLs for an environment + +``` +coder urls ls [environment_name] [flags] +``` + +### Options + +``` + -h, --help help for ls + -o, --output string human|json (default "human") +``` + +### SEE ALSO + +* [coder urls](coder_urls.md) - Interact with environment DevURLs + diff --git a/docs/coder_urls_rm.md b/docs/coder_urls_rm.md new file mode 100644 index 00000000..4d88170b --- /dev/null +++ b/docs/coder_urls_rm.md @@ -0,0 +1,22 @@ +## coder urls rm + +Remove a dev url + +### Synopsis + +Remove a dev url + +``` +coder urls rm [environment_name] [port] [flags] +``` + +### Options + +``` + -h, --help help for rm +``` + +### SEE ALSO + +* [coder urls](coder_urls.md) - Interact with environment DevURLs + diff --git a/docs/coder_users.md b/docs/coder_users.md new file mode 100644 index 00000000..974edc27 --- /dev/null +++ b/docs/coder_users.md @@ -0,0 +1,19 @@ +## coder users + +Interact with Coder user accounts + +### Synopsis + +Interact with Coder user accounts + +### Options + +``` + -h, --help help for users +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation +* [coder users ls](coder_users_ls.md) - list all user accounts + diff --git a/docs/coder_users_ls.md b/docs/coder_users_ls.md new file mode 100644 index 00000000..a6b85a4c --- /dev/null +++ b/docs/coder_users_ls.md @@ -0,0 +1,30 @@ +## coder users ls + +list all user accounts + +### Synopsis + +list all user accounts + +``` +coder users ls [flags] +``` + +### Examples + +``` +coder users ls -o json +coder users ls -o json | jq .[] | jq -r .email +``` + +### Options + +``` + -h, --help help for ls + -o, --output string human | json (default "human") +``` + +### SEE ALSO + +* [coder users](coder_users.md) - Interact with Coder user accounts + diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index ad28fa39..f1fadf30 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -6,6 +6,7 @@ import ( "github.com/spf13/cobra" ) +// Make constructs the "coder" root command func Make() *cobra.Command { app := &cobra.Command{ Use: "coder", From 0e031d9e16b85e101b9ac3c22c34776c3c91701c Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Wed, 12 Aug 2020 21:51:29 -0500 Subject: [PATCH 3/4] Add gendocs ci step --- .github/workflows/test.yaml | 14 ++++++++ ci/steps/fmt.sh | 7 ++-- ci/steps/gendocs.sh | 25 ++++++++++++++ ci/steps/lint.sh | 4 ++- docs/coder.md | 2 +- docs/coder_completion.md | 63 ++++++++++++++++++++++++++++++++++++ docs/coder_config-ssh.md | 1 - docs/coder_envs.md | 1 - docs/coder_envs_ls.md | 1 - docs/coder_login.md | 1 - docs/coder_logout.md | 1 - docs/coder_secrets.md | 1 - docs/coder_secrets_create.md | 1 - docs/coder_secrets_ls.md | 1 - docs/coder_secrets_rm.md | 1 - docs/coder_secrets_view.md | 1 - docs/coder_sh.md | 1 - docs/coder_sync.md | 1 - docs/coder_urls.md | 1 - docs/coder_urls_create.md | 1 - docs/coder_urls_ls.md | 1 - docs/coder_urls_rm.md | 1 - docs/coder_users.md | 1 - docs/coder_users_ls.md | 1 - internal/cmd/cmd.go | 35 ++++++++++++++++---- internal/cmd/configssh.go | 27 ++++++++++------ 26 files changed, 157 insertions(+), 38 deletions(-) create mode 100755 ci/steps/gendocs.sh create mode 100644 docs/coder_completion.md diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 36397b7d..d7712b14 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -44,3 +44,17 @@ jobs: uses: ./ci/image with: args: go test ./internal/... ./cmd/... + gendocs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/cache@v1 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - name: generate-docs + uses: ./ci/image + with: + args: ./ci/steps/gendocs.sh diff --git a/ci/steps/fmt.sh b/ci/steps/fmt.sh index bb4b0d2c..c202dab9 100755 --- a/ci/steps/fmt.sh +++ b/ci/steps/fmt.sh @@ -1,16 +1,17 @@ #!/bin/bash + +set -euo pipefail + echo "Formatting..." go mod tidy gofmt -w -s . goimports -w "-local=$$(go list -m)" . -if [ "$CI" != "" ]; then - if [[ $(git ls-files --other --modified --exclude-standard) != "" ]]; then +if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then echo "Files need generation or are formatted incorrectly:" git -c color.ui=always status | grep --color=no '\e\[31m' echo "Please run the following locally:" echo " ./ci/steps/fmt.sh" exit 1 fi -fi \ No newline at end of file diff --git a/ci/steps/gendocs.sh b/ci/steps/gendocs.sh new file mode 100755 index 00000000..64a3776a --- /dev/null +++ b/ci/steps/gendocs.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -euo pipefail + +echo "Generating docs..." + +cd "$(dirname "$0")" +cd ../../ + +go run ./cmd/coder gen-docs ./docs + +# remove cobra footer from each file +for filename in ./docs/*.md; do + trimmed=$(head -n -1 "$filename") + echo "$trimmed" > $filename +done + + +if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then + echo "Documentation needs generation:" + git -c color.ui=always status | grep --color=no '\e\[31m' + echo "Please run the following locally:" + echo " ./ci/steps/gendocs.sh" + exit 1 +fi diff --git a/ci/steps/lint.sh b/ci/steps/lint.sh index 51da081d..c3f72614 100755 --- a/ci/steps/lint.sh +++ b/ci/steps/lint.sh @@ -1,6 +1,8 @@ #!/bin/bash +set -euo pipefail + echo "Linting..." go vet ./... -golint -set_exit_status ./... \ No newline at end of file +golint -set_exit_status ./... diff --git a/docs/coder.md b/docs/coder.md index 518eb29a..378a9c66 100644 --- a/docs/coder.md +++ b/docs/coder.md @@ -14,6 +14,7 @@ coder provides a CLI for working with an existing Coder Enterprise installation ### SEE ALSO +* [coder completion](coder_completion.md) - Generate completion script * [coder config-ssh](coder_config-ssh.md) - Configure SSH to access Coder environments * [coder envs](coder_envs.md) - Interact with Coder environments * [coder login](coder_login.md) - Authenticate this client for future operations @@ -23,4 +24,3 @@ coder provides a CLI for working with an existing Coder Enterprise installation * [coder sync](coder_sync.md) - Establish a one way directory sync to a Coder environment * [coder urls](coder_urls.md) - Interact with environment DevURLs * [coder users](coder_users.md) - Interact with Coder user accounts - diff --git a/docs/coder_completion.md b/docs/coder_completion.md new file mode 100644 index 00000000..39b862af --- /dev/null +++ b/docs/coder_completion.md @@ -0,0 +1,63 @@ +## coder completion + +Generate completion script + +### Synopsis + +To load completions: + +Bash: + +$ source <(coder completion bash) + +To load completions for each session, execute once: +Linux: + $ coder completion bash > /etc/bash_completion.d/coder +MacOS: + $ coder completion bash > /usr/local/etc/bash_completion.d/coder + +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: +$ coder completion zsh > "${fpath[1]}/_coder" + +You will need to start a new shell for this setup to take effect. + +Fish: + +$ coder completion fish | source + +To load completions for each session, execute once: +$ coder completion fish > ~/.config/fish/completions/coder.fish + + +``` +coder completion [bash|zsh|fish|powershell] +``` + +### Examples + +``` +coder completion fish > ~/.config/fish/completions/coder.fish +coder completion zsh > "${fpath[1]}/_coder" + +Linux: + $ coder completion bash > /etc/bash_completion.d/coder +MacOS: + $ coder completion bash > /usr/local/etc/bash_completion.d/coder +``` + +### Options + +``` + -h, --help help for completion +``` + +### SEE ALSO + +* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation diff --git a/docs/coder_config-ssh.md b/docs/coder_config-ssh.md index b4cb3c59..41b697ef 100644 --- a/docs/coder_config-ssh.md +++ b/docs/coder_config-ssh.md @@ -21,4 +21,3 @@ coder config-ssh [flags] ### SEE ALSO * [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation - diff --git a/docs/coder_envs.md b/docs/coder_envs.md index 30657d19..a08c4b15 100644 --- a/docs/coder_envs.md +++ b/docs/coder_envs.md @@ -17,4 +17,3 @@ Perform operations on the Coder environments owned by the active user. * [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation * [coder envs ls](coder_envs_ls.md) - list all environments owned by the active user - diff --git a/docs/coder_envs_ls.md b/docs/coder_envs_ls.md index df0569ec..d3535af8 100644 --- a/docs/coder_envs_ls.md +++ b/docs/coder_envs_ls.md @@ -26,4 +26,3 @@ coder envs ls [flags] ### SEE ALSO * [coder envs](coder_envs.md) - Interact with Coder environments - diff --git a/docs/coder_login.md b/docs/coder_login.md index 8551f43b..9cd18ae8 100644 --- a/docs/coder_login.md +++ b/docs/coder_login.md @@ -19,4 +19,3 @@ coder login [Coder Enterprise URL eg. http://my.coder.domain/] [flags] ### SEE ALSO * [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation - diff --git a/docs/coder_logout.md b/docs/coder_logout.md index 5c308147..22bce303 100644 --- a/docs/coder_logout.md +++ b/docs/coder_logout.md @@ -19,4 +19,3 @@ coder logout [flags] ### SEE ALSO * [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation - diff --git a/docs/coder_secrets.md b/docs/coder_secrets.md index 917e4cf9..ebdd1af2 100644 --- a/docs/coder_secrets.md +++ b/docs/coder_secrets.md @@ -20,4 +20,3 @@ Interact with secrets objects owned by the active user. * [coder secrets ls](coder_secrets_ls.md) - List all secrets owned by the active user * [coder secrets rm](coder_secrets_rm.md) - Remove one or more secrets by name * [coder secrets view](coder_secrets_view.md) - View a secret by name - diff --git a/docs/coder_secrets_create.md b/docs/coder_secrets_create.md index 5dfe7e1a..c10a771e 100644 --- a/docs/coder_secrets_create.md +++ b/docs/coder_secrets_create.md @@ -37,4 +37,3 @@ coder secrets create aws-credentials --from-file ./credentials.json ### SEE ALSO * [coder secrets](coder_secrets.md) - Interact with Coder Secrets - diff --git a/docs/coder_secrets_ls.md b/docs/coder_secrets_ls.md index 49f7e8b7..40408e8e 100644 --- a/docs/coder_secrets_ls.md +++ b/docs/coder_secrets_ls.md @@ -25,4 +25,3 @@ coder secrets ls [flags] ### SEE ALSO * [coder secrets](coder_secrets.md) - Interact with Coder Secrets - diff --git a/docs/coder_secrets_rm.md b/docs/coder_secrets_rm.md index d628b148..d58dc6f0 100644 --- a/docs/coder_secrets_rm.md +++ b/docs/coder_secrets_rm.md @@ -31,4 +31,3 @@ coder secrets rm mysql-password mysql-user ### SEE ALSO * [coder secrets](coder_secrets.md) - Interact with Coder Secrets - diff --git a/docs/coder_secrets_view.md b/docs/coder_secrets_view.md index f8c18ee9..e5a9770a 100644 --- a/docs/coder_secrets_view.md +++ b/docs/coder_secrets_view.md @@ -31,4 +31,3 @@ coder secrets view mysql-password ### SEE ALSO * [coder secrets](coder_secrets.md) - Interact with Coder Secrets - diff --git a/docs/coder_sh.md b/docs/coder_sh.md index e32ecd65..6c88f203 100644 --- a/docs/coder_sh.md +++ b/docs/coder_sh.md @@ -25,4 +25,3 @@ coder sh backend-env ### SEE ALSO * [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation - diff --git a/docs/coder_sync.md b/docs/coder_sync.md index cf03a01e..03ca7a37 100644 --- a/docs/coder_sync.md +++ b/docs/coder_sync.md @@ -20,4 +20,3 @@ coder sync [local directory] [:] [flags] ### SEE ALSO * [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation - diff --git a/docs/coder_urls.md b/docs/coder_urls.md index dd181999..df4c3c70 100644 --- a/docs/coder_urls.md +++ b/docs/coder_urls.md @@ -18,4 +18,3 @@ Interact with environment DevURLs * [coder urls create](coder_urls_create.md) - Create a new devurl for an environment * [coder urls ls](coder_urls_ls.md) - List all DevURLs for an environment * [coder urls rm](coder_urls_rm.md) - Remove a dev url - diff --git a/docs/coder_urls_create.md b/docs/coder_urls_create.md index 8cf4c258..7afc8d8b 100644 --- a/docs/coder_urls_create.md +++ b/docs/coder_urls_create.md @@ -21,4 +21,3 @@ coder urls create [env_name] [port] [--access ] [--name ] [flags] ### SEE ALSO * [coder urls](coder_urls.md) - Interact with environment DevURLs - diff --git a/docs/coder_urls_ls.md b/docs/coder_urls_ls.md index f359529e..1d01c2e5 100644 --- a/docs/coder_urls_ls.md +++ b/docs/coder_urls_ls.md @@ -20,4 +20,3 @@ coder urls ls [environment_name] [flags] ### SEE ALSO * [coder urls](coder_urls.md) - Interact with environment DevURLs - diff --git a/docs/coder_urls_rm.md b/docs/coder_urls_rm.md index 4d88170b..2b69e2bb 100644 --- a/docs/coder_urls_rm.md +++ b/docs/coder_urls_rm.md @@ -19,4 +19,3 @@ coder urls rm [environment_name] [port] [flags] ### SEE ALSO * [coder urls](coder_urls.md) - Interact with environment DevURLs - diff --git a/docs/coder_users.md b/docs/coder_users.md index 974edc27..6482d76e 100644 --- a/docs/coder_users.md +++ b/docs/coder_users.md @@ -16,4 +16,3 @@ Interact with Coder user accounts * [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation * [coder users ls](coder_users_ls.md) - list all user accounts - diff --git a/docs/coder_users_ls.md b/docs/coder_users_ls.md index a6b85a4c..6cf7ccd1 100644 --- a/docs/coder_users_ls.md +++ b/docs/coder_users_ls.md @@ -27,4 +27,3 @@ coder users ls -o json | jq .[] | jq -r .email ### SEE ALSO * [coder users](coder_users.md) - Interact with Coder user accounts - diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index f1fadf30..30e4af7d 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -4,6 +4,7 @@ import ( "os" "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" ) // Make constructs the "coder" root command @@ -24,21 +25,43 @@ func Make() *cobra.Command { makeSyncCmd(), makeURLCmd(), completionCmd, + genDocs(app), ) return app } +func genDocs(rootCmd *cobra.Command) *cobra.Command { + cmd := &cobra.Command{ + Use: "gen-docs [dir_path]", + Short: "Generate a markdown documentation tree for the root command.", + Example: "coder gen-docs ./docs", + Args: cobra.ExactArgs(1), + Hidden: true, + RunE: func(_ *cobra.Command, args []string) error { + return doc.GenMarkdownTree(rootCmd, args[0]) + }, + } + return cmd +} + // 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", + Example: `coder completion fish > ~/.config/fish/completions/coder.fish +coder completion zsh > "${fpath[1]}/_coder" + +Linux: + $ coder completion bash > /etc/bash_completion.d/coder +MacOS: + $ coder completion bash > /usr/local/etc/bash_completion.d/coder`, Long: `To load completions: Bash: $ source <(coder completion bash) -# To load completions for each session, execute once: +To load completions for each session, execute once: Linux: $ coder completion bash > /etc/bash_completion.d/coder MacOS: @@ -46,21 +69,21 @@ MacOS: Zsh: -# If shell completion is not already enabled in your environment you will need -# to enable it. You can execute the following once: +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: +To load completions for each session, execute once: $ coder completion zsh > "${fpath[1]}/_coder" -# You will need to start a new shell for this setup to take effect. +You will need to start a new shell for this setup to take effect. Fish: $ coder completion fish | source -# To load completions for each session, execute once: +To load completions for each session, execute once: $ coder completion fish > ~/.config/fish/completions/coder.fish `, DisableFlagsInUseLine: true, diff --git a/internal/cmd/configssh.go b/internal/cmd/configssh.go index fc67ee1a..0e755ff1 100644 --- a/internal/cmd/configssh.go +++ b/internal/cmd/configssh.go @@ -7,6 +7,7 @@ import ( "net" "net/url" "os" + "os/user" "path/filepath" "strings" "time" @@ -29,13 +30,13 @@ func makeConfigSSHCmd() *cobra.Command { 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().StringVar(&configpath, "filepath", filepath.Join("~", ".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(cmd *cobra.Command, _ []string) error { +func configSSH(configpath *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. @@ -51,12 +52,20 @@ func configSSH(filepath *string, remove *bool) func(cmd *cobra.Command, _ []stri ctx, cancel := context.WithCancel(context.Background()) defer cancel() - currentConfig, err := readStr(*filepath) + if strings.HasPrefix(*configpath, "~") { + usr, err := user.Current() + if err != nil { + return xerrors.Errorf("get user home directory: %w", err) + } + *configpath = strings.Replace(*configpath, "~", usr.HomeDir, 1) + } + + currentConfig, err := readStr(*configpath) if os.IsNotExist(err) { // SSH configs are not always already there. currentConfig = "" } else if err != nil { - return xerrors.Errorf("read ssh config file %q: %w", filepath, err) + return xerrors.Errorf("read ssh config file %q: %w", configpath, err) } startIndex := strings.Index(currentConfig, startToken) @@ -68,9 +77,9 @@ func configSSH(filepath *string, remove *bool) func(cmd *cobra.Command, _ []stri } currentConfig = currentConfig[:startIndex-1] + currentConfig[endIndex+len(endToken)+1:] - err = writeStr(*filepath, currentConfig) + err = writeStr(*configpath, currentConfig) if err != nil { - return xerrors.Errorf("write to ssh config file %q: %v", *filepath, err) + return xerrors.Errorf("write to ssh config file %q: %v", *configpath, err) } return nil @@ -105,16 +114,16 @@ func configSSH(filepath *string, remove *bool) func(cmd *cobra.Command, _ []stri currentConfig = currentConfig[:startIndex-1] + currentConfig[endIndex+len(endToken)+1:] } - err = writeStr(*filepath, currentConfig+newConfig) + err = writeStr(*configpath, currentConfig+newConfig) if err != nil { - return xerrors.Errorf("write new configurations to ssh config file %q: %w", filepath, err) + return xerrors.Errorf("write new configurations to ssh config file %q: %w", *configpath, err) } err = writeSSHKey(ctx, entClient) if err != nil { return xerrors.Errorf("fetch and write ssh key: %w", err) } - fmt.Printf("An auto-generated ssh config was written to %q\n", *filepath) + fmt.Printf("An auto-generated ssh config was written to %q\n", *configpath) fmt.Printf("Your private ssh key was written to %q\n", privateKeyFilepath) fmt.Println("You should now be able to ssh into your environment") fmt.Printf("For example, try running\n\n\t$ ssh coder.%s\n\n", envs[0].Name) From f21d22465c42b8d5bc462ccfae508039f5ae0813 Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Thu, 13 Aug 2020 10:18:13 -0500 Subject: [PATCH 4/4] Abstract integration test container creation --- ci/integration/devurls_test.go | 33 ++----- ci/integration/integration_test.go | 127 +++++++++++++------------ ci/integration/secrets_test.go | 146 +++++++++++++---------------- ci/integration/ssh_test.go | 18 ++++ ci/integration/users_test.go | 70 ++++++-------- ci/steps/build.sh | 3 +- internal/cmd/configssh.go | 4 + 7 files changed, 198 insertions(+), 203 deletions(-) create mode 100644 ci/integration/ssh_test.go diff --git a/ci/integration/devurls_test.go b/ci/integration/devurls_test.go index 09f1c0e4..06553107 100644 --- a/ci/integration/devurls_test.go +++ b/ci/integration/devurls_test.go @@ -3,36 +3,23 @@ package integration import ( "context" "testing" - "time" "cdr.dev/coder-cli/ci/tcli" - "cdr.dev/slog/sloggers/slogtest/assert" ) func TestDevURLCLI(t *testing.T) { t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) - defer cancel() - - c, err := tcli.NewContainerRunner(ctx, &tcli.ContainerConfig{ - Image: "codercom/enterprise-dev", - Name: "coder-cli-devurl-tests", - BindMounts: map[string]string{ - binpath: "/bin/coder", - }, + run(t, "coder-cli-devurl-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { + c.Run(ctx, "which coder").Assert(t, + tcli.Success(), + tcli.StdoutMatches("/usr/sbin/coder"), + tcli.StderrEmpty(), + ) + + c.Run(ctx, "coder urls ls").Assert(t, + tcli.Error(), + ) }) - assert.Success(t, "new run container", err) - defer c.Close() - - c.Run(ctx, "which coder").Assert(t, - tcli.Success(), - tcli.StdoutMatches("/usr/sbin/coder"), - tcli.StderrEmpty(), - ) - - c.Run(ctx, "coder urls ls").Assert(t, - tcli.Error(), - ) // The following cannot be enabled nor verified until either the // integration testing dogfood target has environments created, or diff --git a/ci/integration/integration_test.go b/ci/integration/integration_test.go index 77a4ec91..eccf68a2 100644 --- a/ci/integration/integration_test.go +++ b/ci/integration/integration_test.go @@ -10,67 +10,78 @@ import ( "cdr.dev/slog/sloggers/slogtest/assert" ) +func run(t *testing.T, container string, execute func(t *testing.T, ctx context.Context, runner *tcli.ContainerRunner)) { + t.Run(container, func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) + defer cancel() + + c, err := tcli.NewContainerRunner(ctx, &tcli.ContainerConfig{ + Image: "codercom/enterprise-dev", + Name: container, + BindMounts: map[string]string{ + binpath: "/bin/coder", + }, + }) + assert.Success(t, "new run container", err) + defer c.Close() + + execute(t, ctx, c) + }) +} + func TestCoderCLI(t *testing.T) { t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) - defer cancel() - - c, err := tcli.NewContainerRunner(ctx, &tcli.ContainerConfig{ - Image: "codercom/enterprise-dev", - Name: "coder-cli-tests", - BindMounts: map[string]string{ - binpath: "/bin/coder", - }, + run(t, "test-coder-cli", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { + c.Run(ctx, "which coder").Assert(t, + tcli.Success(), + tcli.StdoutMatches("/usr/sbin/coder"), + tcli.StderrEmpty(), + ) + + c.Run(ctx, "coder --version").Assert(t, + tcli.StderrEmpty(), + tcli.Success(), + tcli.StdoutMatches("linux"), + ) + + c.Run(ctx, "coder --help").Assert(t, + tcli.Success(), + tcli.StdoutMatches("Available Commands"), + ) + + headlessLogin(ctx, t, c) + + c.Run(ctx, "coder envs").Assert(t, + tcli.Success(), + ) + + c.Run(ctx, "coder envs ls").Assert(t, + tcli.Success(), + ) + + c.Run(ctx, "coder urls").Assert(t, + tcli.Success(), + ) + + c.Run(ctx, "coder sync").Assert(t, + tcli.Error(), + ) + + c.Run(ctx, "coder sh").Assert(t, + tcli.Error(), + ) + + c.Run(ctx, "coder logout").Assert(t, + tcli.Success(), + ) + + c.Run(ctx, "coder envs ls").Assert(t, + tcli.Error(), + ) }) - assert.Success(t, "new run container", err) - defer c.Close() - - c.Run(ctx, "which coder").Assert(t, - tcli.Success(), - tcli.StdoutMatches("/usr/sbin/coder"), - tcli.StderrEmpty(), - ) - - c.Run(ctx, "coder --version").Assert(t, - tcli.StderrEmpty(), - tcli.Success(), - tcli.StdoutMatches("linux"), - ) - - c.Run(ctx, "coder --help").Assert(t, - tcli.Success(), - tcli.StdoutMatches("Available Commands"), - ) - - headlessLogin(ctx, t, c) - - c.Run(ctx, "coder envs").Assert(t, - tcli.Success(), - ) - - c.Run(ctx, "coder envs ls").Assert(t, - tcli.Success(), - ) - - c.Run(ctx, "coder urls").Assert(t, - tcli.Success(), - ) - - c.Run(ctx, "coder sync").Assert(t, - tcli.Error(), - ) - - c.Run(ctx, "coder sh").Assert(t, - tcli.Error(), - ) - - c.Run(ctx, "coder logout").Assert(t, - tcli.Success(), - ) - - c.Run(ctx, "coder envs ls").Assert(t, - tcli.Error(), - ) + } var seededRand = rand.New(rand.NewSource(time.Now().UnixNano())) diff --git a/ci/integration/secrets_test.go b/ci/integration/secrets_test.go index dc063b9b..fadcc84e 100644 --- a/ci/integration/secrets_test.go +++ b/ci/integration/secrets_test.go @@ -5,91 +5,77 @@ import ( "fmt" "regexp" "testing" - "time" "cdr.dev/coder-cli/ci/tcli" - "cdr.dev/slog/sloggers/slogtest/assert" ) func TestSecrets(t *testing.T) { t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) - defer cancel() - - c, err := tcli.NewContainerRunner(ctx, &tcli.ContainerConfig{ - Image: "codercom/enterprise-dev", - Name: "secrets-cli-tests", - BindMounts: map[string]string{ - binpath: "/bin/coder", - }, + run(t, "secrets-cli-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { + headlessLogin(ctx, t, c) + + c.Run(ctx, "coder secrets ls").Assert(t, + tcli.Success(), + ) + + name, value := randString(8), randString(8) + + c.Run(ctx, "coder secrets create").Assert(t, + tcli.Error(), + ) + + // this tests the "Value:" prompt fallback + c.Run(ctx, fmt.Sprintf("echo %s | coder secrets create %s --from-prompt", value, name)).Assert(t, + tcli.Success(), + tcli.StderrEmpty(), + ) + + c.Run(ctx, "coder secrets ls").Assert(t, + tcli.Success(), + tcli.StderrEmpty(), + tcli.StdoutMatches("Value"), + tcli.StdoutMatches(regexp.QuoteMeta(name)), + ) + + c.Run(ctx, "coder secrets view "+name).Assert(t, + tcli.Success(), + tcli.StderrEmpty(), + tcli.StdoutMatches(regexp.QuoteMeta(value)), + ) + + c.Run(ctx, "coder secrets rm").Assert(t, + tcli.Error(), + ) + c.Run(ctx, "coder secrets rm "+name).Assert(t, + tcli.Success(), + ) + c.Run(ctx, "coder secrets view "+name).Assert(t, + tcli.Error(), + tcli.StdoutEmpty(), + ) + + name, value = randString(8), randString(8) + + c.Run(ctx, fmt.Sprintf("coder secrets create %s --from-literal %s", name, value)).Assert(t, + tcli.Success(), + tcli.StderrEmpty(), + ) + + c.Run(ctx, "coder secrets view "+name).Assert(t, + tcli.Success(), + tcli.StdoutMatches(regexp.QuoteMeta(value)), + ) + + name, value = randString(8), randString(8) + c.Run(ctx, fmt.Sprintf("echo %s > ~/secret.json", value)).Assert(t, + tcli.Success(), + ) + c.Run(ctx, fmt.Sprintf("coder secrets create %s --from-file ~/secret.json", name)).Assert(t, + tcli.Success(), + ) + c.Run(ctx, "coder secrets view "+name).Assert(t, + tcli.Success(), + tcli.StdoutMatches(regexp.QuoteMeta(value)), + ) }) - assert.Success(t, "new run container", err) - defer c.Close() - - headlessLogin(ctx, t, c) - - c.Run(ctx, "coder secrets ls").Assert(t, - tcli.Success(), - ) - - name, value := randString(8), randString(8) - - c.Run(ctx, "coder secrets create").Assert(t, - tcli.Error(), - ) - - // this tests the "Value:" prompt fallback - c.Run(ctx, fmt.Sprintf("echo %s | coder secrets create %s --from-prompt", value, name)).Assert(t, - tcli.Success(), - tcli.StderrEmpty(), - ) - - c.Run(ctx, "coder secrets ls").Assert(t, - tcli.Success(), - tcli.StderrEmpty(), - tcli.StdoutMatches("Value"), - tcli.StdoutMatches(regexp.QuoteMeta(name)), - ) - - c.Run(ctx, "coder secrets view "+name).Assert(t, - tcli.Success(), - tcli.StderrEmpty(), - tcli.StdoutMatches(regexp.QuoteMeta(value)), - ) - - c.Run(ctx, "coder secrets rm").Assert(t, - tcli.Error(), - ) - c.Run(ctx, "coder secrets rm "+name).Assert(t, - tcli.Success(), - ) - c.Run(ctx, "coder secrets view "+name).Assert(t, - tcli.Error(), - tcli.StdoutEmpty(), - ) - - name, value = randString(8), randString(8) - - c.Run(ctx, fmt.Sprintf("coder secrets create %s --from-literal %s", name, value)).Assert(t, - tcli.Success(), - tcli.StderrEmpty(), - ) - - c.Run(ctx, "coder secrets view "+name).Assert(t, - tcli.Success(), - tcli.StdoutMatches(regexp.QuoteMeta(value)), - ) - - name, value = randString(8), randString(8) - c.Run(ctx, fmt.Sprintf("echo %s > ~/secret.json", value)).Assert(t, - tcli.Success(), - ) - c.Run(ctx, fmt.Sprintf("coder secrets create %s --from-file ~/secret.json", name)).Assert(t, - tcli.Success(), - ) - // - c.Run(ctx, "coder secrets view "+name).Assert(t, - tcli.Success(), - tcli.StdoutMatches(regexp.QuoteMeta(value)), - ) } diff --git a/ci/integration/ssh_test.go b/ci/integration/ssh_test.go new file mode 100644 index 00000000..273e3850 --- /dev/null +++ b/ci/integration/ssh_test.go @@ -0,0 +1,18 @@ +package integration + +import ( + "context" + "testing" + + "cdr.dev/coder-cli/ci/tcli" +) + +func TestSSH(t *testing.T) { + t.Parallel() + run(t, "ssh-coder-cli-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { + headlessLogin(ctx, t, c) + c.Run(ctx, "coder config-ssh").Assert(t, + tcli.Success(), + ) + }) +} diff --git a/ci/integration/users_test.go b/ci/integration/users_test.go index 659ccc7a..e3c7e6f3 100644 --- a/ci/integration/users_test.go +++ b/ci/integration/users_test.go @@ -3,7 +3,6 @@ package integration import ( "context" "testing" - "time" "cdr.dev/coder-cli/ci/tcli" "cdr.dev/coder-cli/internal/entclient" @@ -12,45 +11,34 @@ import ( func TestUsers(t *testing.T) { t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) - defer cancel() - - c, err := tcli.NewContainerRunner(ctx, &tcli.ContainerConfig{ - Image: "codercom/enterprise-dev", - Name: "users-cli-tests", - BindMounts: map[string]string{ - binpath: "/bin/coder", - }, + run(t, "users-cli-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { + c.Run(ctx, "which coder").Assert(t, + tcli.Success(), + tcli.StdoutMatches("/usr/sbin/coder"), + tcli.StderrEmpty(), + ) + + headlessLogin(ctx, t, c) + + var user entclient.User + c.Run(ctx, `coder users ls --output json | jq -c '.[] | select( .username == "charlie")'`).Assert(t, + tcli.Success(), + tcli.StdoutJSONUnmarshal(&user), + ) + assert.Equal(t, "user email is as expected", "charlie@coder.com", user.Email) + assert.Equal(t, "username is as expected", "Charlie", user.Name) + + c.Run(ctx, "coder users ls --output human | grep charlie").Assert(t, + tcli.Success(), + tcli.StdoutMatches("charlie"), + ) + + c.Run(ctx, "coder logout").Assert(t, + tcli.Success(), + ) + + c.Run(ctx, "coder users ls").Assert(t, + tcli.Error(), + ) }) - assert.Success(t, "new run container", err) - defer c.Close() - - c.Run(ctx, "which coder").Assert(t, - tcli.Success(), - tcli.StdoutMatches("/usr/sbin/coder"), - tcli.StderrEmpty(), - ) - - headlessLogin(ctx, t, c) - - var user entclient.User - c.Run(ctx, `coder users ls --output json | jq -c '.[] | select( .username == "charlie")'`).Assert(t, - tcli.Success(), - tcli.StdoutJSONUnmarshal(&user), - ) - assert.Equal(t, "user email is as expected", "charlie@coder.com", user.Email) - assert.Equal(t, "username is as expected", "Charlie", user.Name) - - c.Run(ctx, "coder users ls --output human | grep charlie").Assert(t, - tcli.Success(), - tcli.StdoutMatches("charlie"), - ) - - c.Run(ctx, "coder logout").Assert(t, - tcli.Success(), - ) - - c.Run(ctx, "coder users ls").Assert(t, - tcli.Error(), - ) } diff --git a/ci/steps/build.sh b/ci/steps/build.sh index 8411ccde..8c3a2326 100755 --- a/ci/steps/build.sh +++ b/ci/steps/build.sh @@ -21,7 +21,8 @@ build(){ tar -czf "$tarname" coder popd - cp "$tmpdir/$tarname" ../bin + mkdir -p ../bin + cp "$tmpdir/$tarname" ../bin/$tarname rm -rf "$tmpdir" } diff --git a/internal/cmd/configssh.go b/internal/cmd/configssh.go index 0e755ff1..1d1c3c7b 100644 --- a/internal/cmd/configssh.go +++ b/internal/cmd/configssh.go @@ -114,6 +114,10 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st currentConfig = currentConfig[:startIndex-1] + currentConfig[endIndex+len(endToken)+1:] } + err = os.MkdirAll(filepath.Dir(*configpath), os.ModePerm) + if err != nil { + return xerrors.Errorf("make configuration directory: %w", err) + } err = writeStr(*configpath, currentConfig+newConfig) if err != nil { return xerrors.Errorf("write new configurations to ssh config file %q: %w", *configpath, err)