mirror of https://github.com/coder/coder.git
fix: Group subcommands for cognitive ease (#1351)
This commit is contained in:
parent
20caee1502
commit
ddb9631d7a
|
@ -18,11 +18,12 @@ The default schedule is at 09:00 in your local timezone (TZ env, UTC by default)
|
|||
|
||||
func autostart() *cobra.Command {
|
||||
autostartCmd := &cobra.Command{
|
||||
Use: "autostart enable <workspace>",
|
||||
Short: "schedule a workspace to automatically start at a regular time",
|
||||
Long: autostartDescriptionLong,
|
||||
Example: "coder autostart enable my-workspace --minute 30 --hour 9 --days 1-5 --tz Europe/Dublin",
|
||||
Hidden: true,
|
||||
Annotations: workspaceCommand,
|
||||
Use: "autostart enable <workspace>",
|
||||
Short: "schedule a workspace to automatically start at a regular time",
|
||||
Long: autostartDescriptionLong,
|
||||
Example: "coder autostart enable my-workspace --minute 30 --hour 9 --days 1-5 --tz Europe/Dublin",
|
||||
Hidden: true,
|
||||
}
|
||||
|
||||
autostartCmd.AddCommand(autostartEnable())
|
||||
|
|
|
@ -36,7 +36,9 @@ func configSSH() *cobra.Command {
|
|||
skipProxyCommand bool
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "config-ssh",
|
||||
Annotations: workspaceCommand,
|
||||
Use: "config-ssh",
|
||||
Short: "Populate your SSH config with Host entries for all of your workspaces",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -20,8 +20,9 @@ func create() *cobra.Command {
|
|||
templateName string
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [name]",
|
||||
Short: "Create a workspace from a template",
|
||||
Annotations: workspaceCommand,
|
||||
Use: "create [name]",
|
||||
Short: "Create a workspace from a template",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -13,9 +13,11 @@ import (
|
|||
// nolint
|
||||
func delete() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "delete <workspace>",
|
||||
Aliases: []string{"rm"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Annotations: workspaceCommand,
|
||||
Use: "delete <workspace>",
|
||||
Short: "Delete a workspace",
|
||||
Aliases: []string{"rm"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -13,8 +13,10 @@ import (
|
|||
|
||||
func list() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Annotations: workspaceCommand,
|
||||
Use: "list",
|
||||
Short: "List all workspaces",
|
||||
Aliases: []string{"ls"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -37,8 +37,9 @@ func init() {
|
|||
|
||||
func login() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "login <url>",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Use: "login <url>",
|
||||
Short: "Authenticate with a Coder deployment",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
rawURL := args[0]
|
||||
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func parameterCreate() *cobra.Command {
|
||||
var (
|
||||
name string
|
||||
value string
|
||||
scheme string
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "create <scope> [name]",
|
||||
Aliases: []string{"mk"},
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
organization, err := currentOrganization(cmd, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scopeName := ""
|
||||
if len(args) >= 2 {
|
||||
scopeName = args[1]
|
||||
}
|
||||
scope, scopeID, err := parseScopeAndID(cmd.Context(), client, organization, args[0], scopeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scheme, err := parseParameterScheme(scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = client.CreateParameter(cmd.Context(), scope, scopeID, codersdk.CreateParameterRequest{
|
||||
Name: name,
|
||||
SourceValue: value,
|
||||
SourceScheme: database.ParameterSourceSchemeData,
|
||||
DestinationScheme: scheme,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = fmt.Printf("Created!\n")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(&name, "name", "n", "", "Name for a parameter.")
|
||||
_ = cmd.MarkFlagRequired("name")
|
||||
cmd.Flags().StringVarP(&value, "value", "v", "", "Value for a parameter.")
|
||||
_ = cmd.MarkFlagRequired("value")
|
||||
cmd.Flags().StringVarP(&scheme, "scheme", "s", "var", `Scheme for the parameter ("var" or "env").`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func parseParameterScheme(scheme string) (database.ParameterDestinationScheme, error) {
|
||||
switch scheme {
|
||||
case "env":
|
||||
return database.ParameterDestinationSchemeEnvironmentVariable, nil
|
||||
case "var":
|
||||
return database.ParameterDestinationSchemeProvisionerVariable, nil
|
||||
}
|
||||
return database.ParameterDestinationSchemeNone, xerrors.Errorf("scheme %q not recognized", scheme)
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package cli
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
func parameterDelete() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "delete",
|
||||
Aliases: []string{"rm"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func parameterList() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list <scope> <scope-id>",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
organization, err := currentOrganization(cmd, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := ""
|
||||
if len(args) >= 2 {
|
||||
name = args[1]
|
||||
}
|
||||
scope, scopeID, err := parseScopeAndID(cmd.Context(), client, organization, args[0], name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params, err := client.Parameters(cmd.Context(), scope, scopeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writer := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 0, 4, ' ', 0)
|
||||
_, _ = fmt.Fprintf(writer, "%s\t%s\t%s\n",
|
||||
color.HiBlackString("Parameter"),
|
||||
color.HiBlackString("Created"),
|
||||
color.HiBlackString("Scheme"))
|
||||
for _, param := range params {
|
||||
_, _ = fmt.Fprintf(writer, "%s\t%s\t%s\n",
|
||||
color.New(color.FgHiCyan).Sprint(param.Name),
|
||||
color.WhiteString(param.UpdatedAt.Format("January 2, 2006")),
|
||||
color.New(color.FgHiWhite).Sprint(param.DestinationScheme))
|
||||
}
|
||||
return writer.Flush()
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func parameters() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "parameters",
|
||||
Aliases: []string{"params"},
|
||||
}
|
||||
|
||||
cmd.AddCommand(parameterCreate(), parameterList(), parameterDelete())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func parseScopeAndID(ctx context.Context, client *codersdk.Client, organization codersdk.Organization, rawScope string, name string) (codersdk.ParameterScope, uuid.UUID, error) {
|
||||
scope, err := parseParameterScope(rawScope)
|
||||
if err != nil {
|
||||
return scope, uuid.Nil, err
|
||||
}
|
||||
|
||||
var scopeID uuid.UUID
|
||||
switch scope {
|
||||
case codersdk.ParameterOrganization:
|
||||
if name == "" {
|
||||
scopeID = organization.ID
|
||||
} else {
|
||||
org, err := client.OrganizationByName(ctx, codersdk.Me, name)
|
||||
if err != nil {
|
||||
return scope, uuid.Nil, err
|
||||
}
|
||||
scopeID = org.ID
|
||||
}
|
||||
case codersdk.ParameterTemplate:
|
||||
template, err := client.TemplateByName(ctx, organization.ID, name)
|
||||
if err != nil {
|
||||
return scope, uuid.Nil, err
|
||||
}
|
||||
scopeID = template.ID
|
||||
case codersdk.ParameterUser:
|
||||
uid, _ := uuid.Parse(name)
|
||||
user, err := client.User(ctx, uid)
|
||||
if err != nil {
|
||||
return scope, uuid.Nil, err
|
||||
}
|
||||
scopeID = user.ID
|
||||
case codersdk.ParameterWorkspace:
|
||||
workspace, err := client.WorkspaceByOwnerAndName(ctx, organization.ID, codersdk.Me, name)
|
||||
if err != nil {
|
||||
return scope, uuid.Nil, err
|
||||
}
|
||||
scopeID = workspace.ID
|
||||
}
|
||||
|
||||
return scope, scopeID, nil
|
||||
}
|
||||
|
||||
func parseParameterScope(scope string) (codersdk.ParameterScope, error) {
|
||||
switch scope {
|
||||
case string(codersdk.ParameterOrganization):
|
||||
return codersdk.ParameterOrganization, nil
|
||||
case string(codersdk.ParameterTemplate):
|
||||
return codersdk.ParameterTemplate, nil
|
||||
case string(codersdk.ParameterUser):
|
||||
return codersdk.ParameterUser, nil
|
||||
case string(codersdk.ParameterWorkspace):
|
||||
return codersdk.ParameterWorkspace, nil
|
||||
}
|
||||
return codersdk.ParameterOrganization, xerrors.Errorf("no scope found by name %q", scope)
|
||||
}
|
|
@ -14,6 +14,7 @@ func publickey() *cobra.Command {
|
|||
return &cobra.Command{
|
||||
Use: "publickey",
|
||||
Aliases: []string{"pubkey"},
|
||||
Short: "Output your public key for Git operations",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
62
cli/root.go
62
cli/root.go
|
@ -3,10 +3,8 @@ package cli
|
|||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/kirsle/configdir"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -20,6 +18,12 @@ import (
|
|||
|
||||
var (
|
||||
caret = cliui.Styles.Prompt.String()
|
||||
|
||||
// Applied as annotations to workspace commands
|
||||
// so they display in a separated "help" section.
|
||||
workspaceCommand = map[string]string{
|
||||
"workspaces": " ",
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -38,33 +42,14 @@ func Root() *cobra.Command {
|
|||
Version: buildinfo.Version(),
|
||||
SilenceErrors: true,
|
||||
SilenceUsage: true,
|
||||
Long: ` ▄█▀ ▀█▄
|
||||
▄▄ ▀▀▀ █▌ ██▀▀█▄ ▐█
|
||||
▄▄██▀▀█▄▄▄ ██ ██ █▀▀█ ▐█▀▀██ ▄█▀▀█ █▀▀
|
||||
█▌ ▄▌ ▐█ █▌ ▀█▄▄▄█▌ █ █ ▐█ ██ ██▀▀ █
|
||||
██████▀▄█ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀ ▀
|
||||
` + lipgloss.NewStyle().Underline(true).Render("Self-hosted developer workspaces on your infra") + `
|
||||
|
||||
Long: `Coder — A tool for provisioning self-hosted development environments.
|
||||
`,
|
||||
Example: cliui.Styles.Paragraph.Render(`Start Coder in "dev" mode. This dev-mode requires no further setup, and your local `+cliui.Styles.Code.Render("coder")+` CLI will be authenticated to talk to it. This makes it easy to experiment with Coder.`) + `
|
||||
Example: ` Start Coder in "dev" mode. This dev-mode requires no further setup, and your local ` + cliui.Styles.Code.Render("coder") + ` CLI will be authenticated to talk to it. This makes it easy to experiment with Coder.
|
||||
` + cliui.Styles.Code.Render("$ coder server --dev") + `
|
||||
|
||||
` + cliui.Styles.Code.Render("$ coder server --dev") + `
|
||||
` + cliui.Styles.Paragraph.Render("Get started by creating a template from an example.") + `
|
||||
|
||||
` + cliui.Styles.Code.Render("$ coder templates init"),
|
||||
Get started by creating a template from an example.
|
||||
` + cliui.Styles.Code.Render("$ coder templates init"),
|
||||
}
|
||||
// Customizes the color of headings to make subcommands
|
||||
// more visually appealing.
|
||||
header := cliui.Styles.Placeholder
|
||||
cmd.SetUsageTemplate(strings.NewReplacer(
|
||||
`Usage:`, header.Render("Usage:"),
|
||||
`Examples:`, header.Render("Examples:"),
|
||||
`Available Commands:`, header.Render("Commands:"),
|
||||
`Global Flags:`, header.Render("Global Flags:"),
|
||||
`Flags:`, header.Render("Flags:"),
|
||||
`Additional help topics:`, header.Render("Additional help:"),
|
||||
).Replace(cmd.UsageTemplate()))
|
||||
cmd.SetVersionTemplate(versionTemplate())
|
||||
|
||||
cmd.AddCommand(
|
||||
autostart(),
|
||||
|
@ -75,7 +60,6 @@ func Root() *cobra.Command {
|
|||
gitssh(),
|
||||
list(),
|
||||
login(),
|
||||
parameters(),
|
||||
publickey(),
|
||||
server(),
|
||||
show(),
|
||||
|
@ -90,6 +74,9 @@ func Root() *cobra.Command {
|
|||
workspaceAgent(),
|
||||
)
|
||||
|
||||
cmd.SetUsageTemplate(usageTemplate())
|
||||
cmd.SetVersionTemplate(versionTemplate())
|
||||
|
||||
cmd.PersistentFlags().String(varURL, "", "Specify the URL to your deployment.")
|
||||
cmd.PersistentFlags().String(varToken, "", "Specify an authentication token.")
|
||||
cliflag.String(cmd.PersistentFlags(), varAgentToken, "", "CODER_AGENT_TOKEN", "", "Specify an agent authentication token.")
|
||||
|
@ -188,6 +175,27 @@ func isTTY(cmd *cobra.Command) bool {
|
|||
return isatty.IsTerminal(file.Fd())
|
||||
}
|
||||
|
||||
func usageTemplate() string {
|
||||
// Customizes the color of headings to make subcommands
|
||||
// more visually appealing.
|
||||
header := cliui.Styles.Placeholder
|
||||
|
||||
return `{{if .HasExample}}` + header.Render("Get Started:") + `
|
||||
{{.Example}}
|
||||
|
||||
{{end}}{{if .HasAvailableLocalFlags}}` + header.Render("Flags:") + `
|
||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
|
||||
` + header.Render("Commands:") + `{{range .Commands}}{{if and .IsAvailableCommand (eq (len .Annotations) 0)}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .HasParent }}
|
||||
|
||||
` + header.Render("Workspace Commands:") + `{{range .Commands}}{{if and .IsAvailableCommand (ne (index .Annotations "workspaces") "")}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}
|
||||
|
||||
Use "{{.CommandPath}} [command] --help" for more information about a command.
|
||||
`
|
||||
}
|
||||
|
||||
func versionTemplate() string {
|
||||
template := `Coder {{printf "%s" .Version}}`
|
||||
buildTime, valid := buildinfo.Time()
|
||||
|
|
|
@ -89,7 +89,8 @@ func server() *cobra.Command {
|
|||
)
|
||||
|
||||
root := &cobra.Command{
|
||||
Use: "server",
|
||||
Use: "server",
|
||||
Short: "Start a Coder server",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
logger := slog.Make(sloghuman.Sink(os.Stderr))
|
||||
if verbose {
|
||||
|
|
|
@ -10,8 +10,10 @@ import (
|
|||
|
||||
func show() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "show",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Annotations: workspaceCommand,
|
||||
Use: "show",
|
||||
Short: "Show details of a workspace's resources and agents",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -26,8 +26,10 @@ func ssh() *cobra.Command {
|
|||
stdio bool
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "ssh <workspace>",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Annotations: workspaceCommand,
|
||||
Use: "ssh <workspace>",
|
||||
Short: "SSH into a workspace",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -12,8 +12,10 @@ import (
|
|||
|
||||
func start() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "start <workspace>",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Annotations: workspaceCommand,
|
||||
Use: "start <workspace>",
|
||||
Short: "Build a workspace with the start state",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -13,7 +13,8 @@ import (
|
|||
|
||||
func state() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "state",
|
||||
Use: "state",
|
||||
Short: "Manually manage Terraform state to fix broken workspaces",
|
||||
}
|
||||
cmd.AddCommand(statePull(), statePush())
|
||||
return cmd
|
||||
|
|
|
@ -12,8 +12,10 @@ import (
|
|||
|
||||
func stop() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "stop <workspace>",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Annotations: workspaceCommand,
|
||||
Use: "stop <workspace>",
|
||||
Short: "Build a workspace with the stop state",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
func templates() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "templates",
|
||||
Short: "Create, manage, and deploy templates",
|
||||
Aliases: []string{"template"},
|
||||
Example: `
|
||||
- Create a template for developers to create workspaces
|
||||
|
|
|
@ -4,7 +4,9 @@ import "github.com/spf13/cobra"
|
|||
|
||||
func tunnel() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "tunnel",
|
||||
Annotations: workspaceCommand,
|
||||
Use: "tunnel",
|
||||
Short: "Forward ports to your local machine",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
|
|
|
@ -11,7 +11,9 @@ import (
|
|||
|
||||
func update() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "update",
|
||||
Annotations: workspaceCommand,
|
||||
Use: "update",
|
||||
Short: "Update a workspace to the latest template version",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -4,7 +4,8 @@ import "github.com/spf13/cobra"
|
|||
|
||||
func users() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "users",
|
||||
Short: "Create, remove, and list users",
|
||||
Use: "users",
|
||||
}
|
||||
cmd.AddCommand(userCreate(), userList())
|
||||
return cmd
|
||||
|
|
Loading…
Reference in New Issue