Files
coder/cli/exp_prompts.go
Cian Johnston c5a265fbc3 feat(cli): add experimental rpty command (#16700)
Relates to https://github.com/coder/coder/issues/16419

Builds upon https://github.com/coder/coder/pull/16638 and adds a command
`exp rpty` that allows you to open a ReconnectingPTY session to an
agent.

This ultimately allows us to add an integration-style CLI test to verify
the functionality added in #16638 .
2025-02-26 12:32:57 +00:00

211 lines
6.1 KiB
Go

package cli
import (
"fmt"
"strings"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (RootCmd) promptExample() *serpent.Command {
promptCmd := func(use string, prompt func(inv *serpent.Invocation) error, options ...serpent.Option) *serpent.Command {
return &serpent.Command{
Use: use,
Options: options,
Handler: func(inv *serpent.Invocation) error {
return prompt(inv)
},
}
}
var (
useSearch bool
useSearchOption = serpent.Option{
Name: "search",
Description: "Show the search.",
Required: false,
Flag: "search",
Value: serpent.BoolOf(&useSearch),
}
multiSelectValues []string
multiSelectError error
useThingsOption = serpent.Option{
Name: "things",
Description: "Tell me what things you want.",
Flag: "things",
Default: "",
Value: serpent.StringArrayOf(&multiSelectValues),
}
enableCustomInput bool
enableCustomInputOption = serpent.Option{
Name: "enable-custom-input",
Description: "Enable custom input option in multi-select.",
Required: false,
Flag: "enable-custom-input",
Value: serpent.BoolOf(&enableCustomInput),
}
)
cmd := &serpent.Command{
Use: "prompt-example",
Short: "Example of various prompt types used within coder cli.",
Long: "Example of various prompt types used within coder cli. " +
"This command exists to aid in adjusting visuals of command prompts.",
Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv)
},
Children: []*serpent.Command{
promptCmd("confirm", func(inv *serpent.Invocation) error {
value, err := cliui.Prompt(inv, cliui.PromptOptions{
Text: "Basic confirmation prompt.",
Default: "yes",
IsConfirm: true,
})
_, _ = fmt.Fprintf(inv.Stdout, "%s\n", value)
return err
}),
promptCmd("validation", func(inv *serpent.Invocation) error {
value, err := cliui.Prompt(inv, cliui.PromptOptions{
Text: "Input a string that starts with a capital letter.",
Default: "",
Secret: false,
IsConfirm: false,
Validate: func(s string) error {
if len(s) == 0 {
return xerrors.Errorf("an input string is required")
}
if strings.ToUpper(string(s[0])) != string(s[0]) {
return xerrors.Errorf("input string must start with a capital letter")
}
return nil
},
})
_, _ = fmt.Fprintf(inv.Stdout, "%s\n", value)
return err
}),
promptCmd("secret", func(inv *serpent.Invocation) error {
value, err := cliui.Prompt(inv, cliui.PromptOptions{
Text: "Input a secret",
Default: "",
Secret: true,
IsConfirm: false,
Validate: func(s string) error {
if len(s) == 0 {
return xerrors.Errorf("an input string is required")
}
return nil
},
})
_, _ = fmt.Fprintf(inv.Stdout, "Your secret of length %d is safe with me\n", len(value))
return err
}),
promptCmd("select", func(inv *serpent.Invocation) error {
value, err := cliui.Select(inv, cliui.SelectOptions{
Options: []string{
"Blue", "Green", "Yellow", "Red", "Something else",
},
Default: "",
Message: "Select your favorite color:",
Size: 5,
HideSearch: !useSearch,
})
if value == "Something else" {
_, _ = fmt.Fprint(inv.Stdout, "I would have picked blue.\n")
} else {
_, _ = fmt.Fprintf(inv.Stdout, "%s is a nice color.\n", value)
}
return err
}, useSearchOption),
promptCmd("multiple", func(inv *serpent.Invocation) error {
_, _ = fmt.Fprintf(inv.Stdout, "This command exists to test the behavior of multiple prompts. The survey library does not erase the original message prompt after.")
thing, err := cliui.Select(inv, cliui.SelectOptions{
Message: "Select a thing",
Options: []string{
"Car", "Bike", "Plane", "Boat", "Train",
},
Default: "Car",
})
if err != nil {
return err
}
color, err := cliui.Select(inv, cliui.SelectOptions{
Message: "Select a color",
Options: []string{
"Blue", "Green", "Yellow", "Red",
},
Default: "Blue",
})
if err != nil {
return err
}
properties, err := cliui.MultiSelect(inv, cliui.MultiSelectOptions{
Message: "Select properties",
Options: []string{
"Fast", "Cool", "Expensive", "New",
},
Defaults: []string{"Fast"},
})
if err != nil {
return err
}
_, _ = fmt.Fprintf(inv.Stdout, "Your %s %s is awesome! Did you paint it %s?\n",
strings.Join(properties, " "),
thing,
color,
)
return err
}),
promptCmd("multi-select", func(inv *serpent.Invocation) error {
if len(multiSelectValues) == 0 {
multiSelectValues, multiSelectError = cliui.MultiSelect(inv, cliui.MultiSelectOptions{
Message: "Select some things:",
Options: []string{
"Code", "Chairs", "Whale", "Diamond", "Carrot",
},
Defaults: []string{"Code"},
EnableCustomInput: enableCustomInput,
})
}
_, _ = fmt.Fprintf(inv.Stdout, "%q are nice choices.\n", strings.Join(multiSelectValues, ", "))
return multiSelectError
}, useThingsOption, enableCustomInputOption),
promptCmd("rich-parameter", func(inv *serpent.Invocation) error {
value, err := cliui.RichSelect(inv, cliui.RichSelectOptions{
Options: []codersdk.TemplateVersionParameterOption{
{
Name: "Blue",
Description: "Like the ocean.",
Value: "blue",
Icon: "/logo/blue.png",
},
{
Name: "Red",
Description: "Like a clown's nose.",
Value: "red",
Icon: "/logo/red.png",
},
{
Name: "Yellow",
Description: "Like a bumblebee. ",
Value: "yellow",
Icon: "/logo/yellow.png",
},
},
Default: "blue",
Size: 5,
HideSearch: useSearch,
})
_, _ = fmt.Fprintf(inv.Stdout, "%s is a good choice.\n", value.Name)
return err
}, useSearchOption),
},
}
return cmd
}