chore(cli): rename Cmd to Command (#12616)

I think Command is cleaner and my original decision to use "Cmd"
a mistake.

Plus this creates better parity with cobra.
This commit is contained in:
Ammar Bandukwala 2024-03-17 09:45:26 -05:00 committed by GitHub
parent 2a77580ba6
commit b4c0fa80d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
95 changed files with 313 additions and 311 deletions

View File

@ -34,7 +34,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) workspaceAgent() *serpent.Cmd { func (r *RootCmd) workspaceAgent() *serpent.Command {
var ( var (
auth string auth string
logDir string logDir string
@ -49,7 +49,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Cmd {
slogJSONPath string slogJSONPath string
slogStackdriverPath string slogStackdriverPath string
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "agent", Use: "agent",
Short: `Starts the Coder workspace agent.`, Short: `Starts the Coder workspace agent.`,
// This command isn't useful to manually execute. // This command isn't useful to manually execute.

View File

@ -11,9 +11,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) autoupdate() *serpent.Cmd { func (r *RootCmd) autoupdate() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "autoupdate <workspace> <always|never>", Use: "autoupdate <workspace> <always|never>",
Short: "Toggle auto-update policy for a workspace", Short: "Toggle auto-update policy for a workspace",

View File

@ -23,7 +23,7 @@ func TestBuilder(t *testing.T) {
t.Run("NoConfiguration", func(t *testing.T) { t.Run("NoConfiguration", func(t *testing.T) {
t.Parallel() t.Parallel()
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: testHandler(t), Handler: testHandler(t),
} }
@ -35,7 +35,7 @@ func TestBuilder(t *testing.T) {
t.Parallel() t.Parallel()
tempFile := filepath.Join(t.TempDir(), "test.log") tempFile := filepath.Join(t.TempDir(), "test.log")
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: testHandler(t, Handler: testHandler(t,
clilog.WithHuman(tempFile), clilog.WithHuman(tempFile),
@ -51,7 +51,7 @@ func TestBuilder(t *testing.T) {
t.Parallel() t.Parallel()
tempFile := filepath.Join(t.TempDir(), "test.log") tempFile := filepath.Join(t.TempDir(), "test.log")
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: testHandler(t, Handler: testHandler(t,
clilog.WithHuman(tempFile), clilog.WithHuman(tempFile),
@ -68,7 +68,7 @@ func TestBuilder(t *testing.T) {
t.Parallel() t.Parallel()
tempFile := filepath.Join(t.TempDir(), "test.log") tempFile := filepath.Join(t.TempDir(), "test.log")
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: testHandler(t, clilog.WithHuman(tempFile)), Handler: testHandler(t, clilog.WithHuman(tempFile)),
} }
@ -81,7 +81,7 @@ func TestBuilder(t *testing.T) {
t.Parallel() t.Parallel()
tempFile := filepath.Join(t.TempDir(), "test.log") tempFile := filepath.Join(t.TempDir(), "test.log")
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: testHandler(t, clilog.WithJSON(tempFile), clilog.WithVerbose()), Handler: testHandler(t, clilog.WithJSON(tempFile), clilog.WithVerbose()),
} }
@ -107,7 +107,7 @@ func TestBuilder(t *testing.T) {
// Use the default deployment values. // Use the default deployment values.
dv := coderdtest.DeploymentValues(t) dv := coderdtest.DeploymentValues(t)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: testHandler(t, clilog.FromDeploymentValues(dv)), Handler: testHandler(t, clilog.FromDeploymentValues(dv)),
} }
@ -135,7 +135,7 @@ func TestBuilder(t *testing.T) {
Enable: true, Enable: true,
}, },
} }
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: testHandler(t, clilog.FromDeploymentValues(dv)), Handler: testHandler(t, clilog.FromDeploymentValues(dv)),
} }
@ -150,7 +150,7 @@ func TestBuilder(t *testing.T) {
t.Parallel() t.Parallel()
tempFile := filepath.Join(t.TempDir(), "doesnotexist", "test.log") tempFile := filepath.Join(t.TempDir(), "doesnotexist", "test.log")
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
logger, closeLog, err := clilog.New( logger, closeLog, err := clilog.New(

View File

@ -56,7 +56,7 @@ func (l *logWriter) Write(p []byte) (n int, err error) {
} }
func NewWithCommand( func NewWithCommand(
t testing.TB, cmd *serpent.Cmd, args ...string, t testing.TB, cmd *serpent.Command, args ...string,
) (*serpent.Invocation, config.Root) { ) (*serpent.Invocation, config.Root) {
configDir := config.Root(t.TempDir()) configDir := config.Root(t.TempDir())
// I really would like to fail test on error logs, but realistically, turning on by default // I really would like to fail test on error logs, but realistically, turning on by default

View File

@ -48,7 +48,7 @@ func DefaultCases() []CommandHelpCase {
// TestCommandHelp will test the help output of the given commands // TestCommandHelp will test the help output of the given commands
// using golden files. // using golden files.
func TestCommandHelp(t *testing.T, getRoot func(t *testing.T) *serpent.Cmd, cases []CommandHelpCase) { func TestCommandHelp(t *testing.T, getRoot func(t *testing.T) *serpent.Command, cases []CommandHelpCase) {
t.Parallel() t.Parallel()
rootClient, replacements := prepareTestData(t) rootClient, replacements := prepareTestData(t)
@ -148,7 +148,7 @@ func NormalizeGoldenFile(t *testing.T, byt []byte) []byte {
return byt return byt
} }
func extractVisibleCommandPaths(cmdPath []string, cmds []*serpent.Cmd) [][]string { func extractVisibleCommandPaths(cmdPath []string, cmds []*serpent.Command) [][]string {
var cmdPaths [][]string var cmdPaths [][]string
for _, c := range cmds { for _, c := range cmds {
if c.Hidden { if c.Hidden {

View File

@ -11,8 +11,8 @@ import (
// non-root commands (like 'groups' or 'users'), a handler is required. // non-root commands (like 'groups' or 'users'), a handler is required.
// These handlers are likely just the 'help' handler, but this must be // These handlers are likely just the 'help' handler, but this must be
// explicitly set. // explicitly set.
func HandlersOK(t *testing.T, cmd *serpent.Cmd) { func HandlersOK(t *testing.T, cmd *serpent.Command) {
cmd.Walk(func(cmd *serpent.Cmd) { cmd.Walk(func(cmd *serpent.Command) {
if cmd.Handler == nil { if cmd.Handler == nil {
// If you see this error, make the Handler a helper invoker. // If you see this error, make the Handler a helper invoker.
// Handler: func(inv *serpent.Invocation) error { // Handler: func(inv *serpent.Invocation) error {

View File

@ -382,7 +382,7 @@ func TestAgent(t *testing.T) {
output := make(chan string, 100) // Buffered to avoid blocking, overflow is discarded. output := make(chan string, 100) // Buffered to avoid blocking, overflow is discarded.
logs := make(chan []codersdk.WorkspaceAgentLog, 1) logs := make(chan []codersdk.WorkspaceAgentLog, 1)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
tc.opts.Fetch = func(_ context.Context, _ uuid.UUID) (codersdk.WorkspaceAgent, error) { tc.opts.Fetch = func(_ context.Context, _ uuid.UUID) (codersdk.WorkspaceAgent, error) {
t.Log("iter", len(tc.iter)) t.Log("iter", len(tc.iter))
@ -450,7 +450,7 @@ func TestAgent(t *testing.T) {
t.Parallel() t.Parallel()
var fetchCalled uint64 var fetchCalled uint64
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
buf := bytes.Buffer{} buf := bytes.Buffer{}
err := cliui.Agent(inv.Context(), &buf, uuid.Nil, cliui.AgentOptions{ err := cliui.Agent(inv.Context(), &buf, uuid.Nil, cliui.AgentOptions{

View File

@ -22,7 +22,7 @@ func TestExternalAuth(t *testing.T) {
defer cancel() defer cancel()
ptty := ptytest.New(t) ptty := ptytest.New(t)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var fetched atomic.Bool var fetched atomic.Bool
return cliui.ExternalAuth(inv.Context(), inv.Stdout, cliui.ExternalAuthOptions{ return cliui.ExternalAuth(inv.Context(), inv.Stdout, cliui.ExternalAuthOptions{

View File

@ -11,12 +11,12 @@ var defaultQuery = "owner:me"
// and allows easy integration to a CLI command. // and allows easy integration to a CLI command.
// Example usage: // Example usage:
// //
// func (r *RootCmd) MyCmd() *serpent.Cmd { // func (r *RootCmd) MyCmd() *serpent.Command {
// var ( // var (
// filter cliui.WorkspaceFilter // filter cliui.WorkspaceFilter
// ... // ...
// ) // )
// cmd := &serpent.Cmd{ // cmd := &serpent.Command{
// ... // ...
// } // }
// filter.AttachOptions(&cmd.Options) // filter.AttachOptions(&cmd.Options)

View File

@ -101,7 +101,7 @@ func Test_OutputFormatter(t *testing.T) {
}, },
) )
cmd := &serpent.Cmd{} cmd := &serpent.Command{}
f.AttachOptions(&cmd.Options) f.AttachOptions(&cmd.Options)
fs := cmd.Options.FlagSet() fs := cmd.Options.FlagSet()

View File

@ -147,7 +147,7 @@ func TestPrompt(t *testing.T) {
func newPrompt(ptty *ptytest.PTY, opts cliui.PromptOptions, invOpt func(inv *serpent.Invocation)) (string, error) { func newPrompt(ptty *ptytest.PTY, opts cliui.PromptOptions, invOpt func(inv *serpent.Invocation)) (string, error) {
value := "" value := ""
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var err error var err error
value, err = cliui.Prompt(inv, opts) value, err = cliui.Prompt(inv, opts)
@ -210,7 +210,7 @@ func TestPasswordTerminalState(t *testing.T) {
// nolint:unused // nolint:unused
func passwordHelper() { func passwordHelper() {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
cliui.Prompt(inv, cliui.PromptOptions{ cliui.Prompt(inv, cliui.PromptOptions{
Text: "Password:", Text: "Password:",

View File

@ -127,7 +127,7 @@ func newProvisionerJob(t *testing.T) provisionerJobTest {
} }
jobLock := sync.Mutex{} jobLock := sync.Mutex{}
logs := make(chan codersdk.ProvisionerJobLog, 1) logs := make(chan codersdk.ProvisionerJobLog, 1)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return cliui.ProvisionerJob(inv.Context(), inv.Stdout, cliui.ProvisionerJobOptions{ return cliui.ProvisionerJob(inv.Context(), inv.Stdout, cliui.ProvisionerJobOptions{
FetchInterval: time.Millisecond, FetchInterval: time.Millisecond,

View File

@ -31,7 +31,7 @@ func TestSelect(t *testing.T) {
func newSelect(ptty *ptytest.PTY, opts cliui.SelectOptions) (string, error) { func newSelect(ptty *ptytest.PTY, opts cliui.SelectOptions) (string, error) {
value := "" value := ""
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var err error var err error
value, err = cliui.Select(inv, opts) value, err = cliui.Select(inv, opts)
@ -72,7 +72,7 @@ func TestRichSelect(t *testing.T) {
func newRichSelect(ptty *ptytest.PTY, opts cliui.RichSelectOptions) (string, error) { func newRichSelect(ptty *ptytest.PTY, opts cliui.RichSelectOptions) (string, error) {
value := "" value := ""
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
richOption, err := cliui.RichSelect(inv, opts) richOption, err := cliui.RichSelect(inv, opts)
if err == nil { if err == nil {
@ -105,7 +105,7 @@ func TestMultiSelect(t *testing.T) {
func newMultiSelect(ptty *ptytest.PTY, items []string) ([]string, error) { func newMultiSelect(ptty *ptytest.PTY, items []string) ([]string, error) {
var values []string var values []string
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
selectedItems, err := cliui.MultiSelect(inv, items) selectedItems, err := cliui.MultiSelect(inv, items)
if err == nil { if err == nil {

View File

@ -215,7 +215,7 @@ func sshPrepareWorkspaceConfigs(ctx context.Context, client *codersdk.Client) (r
} }
} }
func (r *RootCmd) configSSH() *serpent.Cmd { func (r *RootCmd) configSSH() *serpent.Command {
var ( var (
sshConfigFile string sshConfigFile string
sshConfigOpts sshConfigOptions sshConfigOpts sshConfigOptions
@ -226,7 +226,7 @@ func (r *RootCmd) configSSH() *serpent.Cmd {
coderCliPath string coderCliPath string
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "config-ssh", Use: "config-ssh",
Short: "Add an SSH Host entry for your workspaces \"ssh coder.workspace\"", Short: "Add an SSH Host entry for your workspaces \"ssh coder.workspace\"",

View File

@ -19,7 +19,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) create() *serpent.Cmd { func (r *RootCmd) create() *serpent.Command {
var ( var (
templateName string templateName string
startAt string startAt string
@ -31,7 +31,7 @@ func (r *RootCmd) create() *serpent.Cmd {
copyParametersFrom string copyParametersFrom string
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "create [name]", Use: "create [name]",
Short: "Create a workspace", Short: "Create a workspace",

View File

@ -10,10 +10,10 @@ import (
) )
// nolint // nolint
func (r *RootCmd) deleteWorkspace() *serpent.Cmd { func (r *RootCmd) deleteWorkspace() *serpent.Command {
var orphan bool var orphan bool
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "delete <workspace>", Use: "delete <workspace>",
Short: "Delete a workspace", Short: "Delete a workspace",

View File

@ -19,12 +19,12 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) dotfiles() *serpent.Cmd { func (r *RootCmd) dotfiles() *serpent.Command {
var symlinkDir string var symlinkDir string
var gitbranch string var gitbranch string
var dotfilesRepoDir string var dotfilesRepoDir string
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "dotfiles <git_repo_url>", Use: "dotfiles <git_repo_url>",
Middleware: serpent.RequireNArgs(1), Middleware: serpent.RequireNArgs(1),
Short: "Personalize your workspace by applying a canonical dotfiles repository", Short: "Personalize your workspace by applying a canonical dotfiles repository",

View File

@ -13,9 +13,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (RootCmd) errorExample() *serpent.Cmd { func (RootCmd) errorExample() *serpent.Command {
errorCmd := func(use string, err error) *serpent.Cmd { errorCmd := func(use string, err error) *serpent.Command {
return &serpent.Cmd{ return &serpent.Command{
Use: use, Use: use,
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return err return err
@ -52,7 +52,7 @@ func (RootCmd) errorExample() *serpent.Cmd {
// Some flags // Some flags
var magicWord serpent.String var magicWord serpent.String
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "example-error", Use: "example-error",
Short: "Shows what different error messages look like", Short: "Shows what different error messages look like",
Long: "This command is pretty pointless, but without it testing errors is" + Long: "This command is pretty pointless, but without it testing errors is" +
@ -61,7 +61,7 @@ func (RootCmd) errorExample() *serpent.Cmd {
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
// Typical codersdk api error // Typical codersdk api error
errorCmd("api", apiError), errorCmd("api", apiError),

View File

@ -2,15 +2,15 @@ package cli
import "github.com/coder/serpent" import "github.com/coder/serpent"
func (r *RootCmd) expCmd() *serpent.Cmd { func (r *RootCmd) expCmd() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "exp", Use: "exp",
Short: "Internal commands for testing and experimentation. These are prone to breaking changes with no notice.", Short: "Internal commands for testing and experimentation. These are prone to breaking changes with no notice.",
Handler: func(i *serpent.Invocation) error { Handler: func(i *serpent.Invocation) error {
return i.Command.HelpHandler(i) return i.Command.HelpHandler(i)
}, },
Hidden: true, Hidden: true,
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.scaletestCmd(), r.scaletestCmd(),
r.errorExample(), r.errorExample(),
}, },

View File

@ -44,14 +44,14 @@ import (
const scaletestTracerName = "coder_scaletest" const scaletestTracerName = "coder_scaletest"
func (r *RootCmd) scaletestCmd() *serpent.Cmd { func (r *RootCmd) scaletestCmd() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "scaletest", Use: "scaletest",
Short: "Run a scale test against the Coder API", Short: "Run a scale test against the Coder API",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.scaletestCleanup(), r.scaletestCleanup(),
r.scaletestDashboard(), r.scaletestDashboard(),
r.scaletestCreateWorkspaces(), r.scaletestCreateWorkspaces(),
@ -398,13 +398,13 @@ func (r *userCleanupRunner) Run(ctx context.Context, _ string, _ io.Writer) erro
return nil return nil
} }
func (r *RootCmd) scaletestCleanup() *serpent.Cmd { func (r *RootCmd) scaletestCleanup() *serpent.Command {
var template string var template string
cleanupStrategy := &scaletestStrategyFlags{cleanup: true} cleanupStrategy := &scaletestStrategyFlags{cleanup: true}
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "cleanup", Use: "cleanup",
Short: "Cleanup scaletest workspaces, then cleanup scaletest users.", Short: "Cleanup scaletest workspaces, then cleanup scaletest users.",
Long: "The strategy flags will apply to each stage of the cleanup process.", Long: "The strategy flags will apply to each stage of the cleanup process.",
@ -521,7 +521,7 @@ func (r *RootCmd) scaletestCleanup() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Cmd { func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Command {
var ( var (
count int64 count int64
retry int64 retry int64
@ -558,7 +558,7 @@ func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Cmd {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "create-workspaces", Use: "create-workspaces",
Short: "Creates many users, then creates a workspace for each user and waits for them finish building and fully come online. Optionally runs a command inside each workspace, and connects to the workspace over WireGuard.", Short: "Creates many users, then creates a workspace for each user and waits for them finish building and fully come online. Optionally runs a command inside each workspace, and connects to the workspace over WireGuard.",
Long: `It is recommended that all rate limits are disabled on the server before running this scaletest. This test generates many login events which will be rate limited against the (most likely single) IP.`, Long: `It is recommended that all rate limits are disabled on the server before running this scaletest. This test generates many login events which will be rate limited against the (most likely single) IP.`,
@ -864,7 +864,7 @@ func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Cmd { func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
var ( var (
tickInterval time.Duration tickInterval time.Duration
bytesPerTick int64 bytesPerTick int64
@ -881,7 +881,7 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Cmd {
prometheusFlags = &scaletestPrometheusFlags{} prometheusFlags = &scaletestPrometheusFlags{}
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "workspace-traffic", Use: "workspace-traffic",
Short: "Generate traffic to scaletest workspaces through coderd", Short: "Generate traffic to scaletest workspaces through coderd",
Middleware: serpent.Chain( Middleware: serpent.Chain(
@ -1109,7 +1109,7 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) scaletestDashboard() *serpent.Cmd { func (r *RootCmd) scaletestDashboard() *serpent.Command {
var ( var (
interval time.Duration interval time.Duration
jitter time.Duration jitter time.Duration
@ -1125,7 +1125,7 @@ func (r *RootCmd) scaletestDashboard() *serpent.Cmd {
prometheusFlags = &scaletestPrometheusFlags{} prometheusFlags = &scaletestPrometheusFlags{}
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "dashboard", Use: "dashboard",
Short: "Generate traffic to the HTTP API to simulate use of the dashboard.", Short: "Generate traffic to the HTTP API to simulate use of the dashboard.",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -4,8 +4,8 @@ package cli
import "github.com/coder/serpent" import "github.com/coder/serpent"
func (r *RootCmd) scaletestCmd() *serpent.Cmd { func (r *RootCmd) scaletestCmd() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "scaletest", Use: "scaletest",
Short: "Run a scale test against the Coder API", Short: "Run a scale test against the Coder API",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {

View File

@ -12,23 +12,23 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) externalAuth() *serpent.Cmd { func (r *RootCmd) externalAuth() *serpent.Command {
return &serpent.Cmd{ return &serpent.Command{
Use: "external-auth", Use: "external-auth",
Short: "Manage external authentication", Short: "Manage external authentication",
Long: "Authenticate with external services inside of a workspace.", Long: "Authenticate with external services inside of a workspace.",
Handler: func(i *serpent.Invocation) error { Handler: func(i *serpent.Invocation) error {
return i.Command.HelpHandler(i) return i.Command.HelpHandler(i)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.externalAuthAccessToken(), r.externalAuthAccessToken(),
}, },
} }
} }
func (r *RootCmd) externalAuthAccessToken() *serpent.Cmd { func (r *RootCmd) externalAuthAccessToken() *serpent.Command {
var extra string var extra string
return &serpent.Cmd{ return &serpent.Command{
Use: "access-token <provider>", Use: "access-token <provider>",
Short: "Print auth for an external provider", Short: "Print auth for an external provider",
Long: "Print an access-token for an external auth provider. " + Long: "Print an access-token for an external auth provider. " +

View File

@ -9,9 +9,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) favorite() *serpent.Cmd { func (r *RootCmd) favorite() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Aliases: []string{"fav", "favou" + "rite"}, Aliases: []string{"fav", "favou" + "rite"},
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "favorite <workspace>", Use: "favorite <workspace>",
@ -36,9 +36,9 @@ func (r *RootCmd) favorite() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) unfavorite() *serpent.Cmd { func (r *RootCmd) unfavorite() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Aliases: []string{"unfav", "unfavou" + "rite"}, Aliases: []string{"unfav", "unfavou" + "rite"},
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "unfavorite <workspace>", Use: "unfavorite <workspace>",

View File

@ -18,8 +18,8 @@ import (
// gitAskpass is used by the Coder agent to automatically authenticate // gitAskpass is used by the Coder agent to automatically authenticate
// with Git providers based on a hostname. // with Git providers based on a hostname.
func (r *RootCmd) gitAskpass() *serpent.Cmd { func (r *RootCmd) gitAskpass() *serpent.Command {
return &serpent.Cmd{ return &serpent.Command{
Use: "gitaskpass", Use: "gitaskpass",
Hidden: true, Hidden: true,
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {

View File

@ -18,8 +18,8 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) gitssh() *serpent.Cmd { func (r *RootCmd) gitssh() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "gitssh", Use: "gitssh",
Hidden: true, Hidden: true,
Short: `Wraps the "ssh" command and uses the coder gitssh key for authentication`, Short: `Wraps the "ssh" command and uses the coder gitssh key for authentication`,

View File

@ -107,7 +107,7 @@ var usageTemplate = func() *template.Template {
} }
return sb.String() return sb.String()
}, },
"formatSubcommand": func(cmd *serpent.Cmd) string { "formatSubcommand": func(cmd *serpent.Command) string {
// Minimize padding by finding the longest neighboring name. // Minimize padding by finding the longest neighboring name.
maxNameLength := len(cmd.Name()) maxNameLength := len(cmd.Name())
if parent := cmd.Parent; parent != nil { if parent := cmd.Parent; parent != nil {
@ -189,12 +189,12 @@ var usageTemplate = func() *template.Template {
s = wrapTTY(s) s = wrapTTY(s)
return s return s
}, },
"visibleChildren": func(cmd *serpent.Cmd) []*serpent.Cmd { "visibleChildren": func(cmd *serpent.Command) []*serpent.Command {
return filterSlice(cmd.Children, func(c *serpent.Cmd) bool { return filterSlice(cmd.Children, func(c *serpent.Command) bool {
return !c.Hidden return !c.Hidden
}) })
}, },
"optionGroups": func(cmd *serpent.Cmd) []optionGroup { "optionGroups": func(cmd *serpent.Command) []optionGroup {
groups := []optionGroup{{ groups := []optionGroup{{
// Default group. // Default group.
Name: "", Name: "",

View File

@ -70,7 +70,7 @@ func workspaceListRowFromWorkspace(now time.Time, workspace codersdk.Workspace)
} }
} }
func (r *RootCmd) list() *serpent.Cmd { func (r *RootCmd) list() *serpent.Command {
var ( var (
filter cliui.WorkspaceFilter filter cliui.WorkspaceFilter
formatter = cliui.NewOutputFormatter( formatter = cliui.NewOutputFormatter(
@ -92,7 +92,7 @@ func (r *RootCmd) list() *serpent.Cmd {
) )
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "list", Use: "list",
Short: "List workspaces", Short: "List workspaces",

View File

@ -125,7 +125,7 @@ func (r *RootCmd) loginWithPassword(
return nil return nil
} }
func (r *RootCmd) login() *serpent.Cmd { func (r *RootCmd) login() *serpent.Command {
const firstUserTrialEnv = "CODER_FIRST_USER_TRIAL" const firstUserTrialEnv = "CODER_FIRST_USER_TRIAL"
var ( var (
@ -135,7 +135,7 @@ func (r *RootCmd) login() *serpent.Cmd {
trial bool trial bool
useTokenForSession bool useTokenForSession bool
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "login [<url>]", Use: "login [<url>]",
Short: "Authenticate with Coder deployment", Short: "Authenticate with Coder deployment",
Middleware: serpent.RequireRangeArgs(0, 1), Middleware: serpent.RequireRangeArgs(0, 1),

View File

@ -12,9 +12,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) logout() *serpent.Cmd { func (r *RootCmd) logout() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "logout", Use: "logout",
Short: "Unauthenticate your local session", Short: "Unauthenticate your local session",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -13,10 +13,10 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) netcheck() *serpent.Cmd { func (r *RootCmd) netcheck() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "netcheck", Use: "netcheck",
Short: "Print network debug information for DERP and STUN", Short: "Print network debug information for DERP and STUN",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -17,14 +17,14 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) open() *serpent.Cmd { func (r *RootCmd) open() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "open", Use: "open",
Short: "Open a workspace", Short: "Open a workspace",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.openVSCode(), r.openVSCode(),
}, },
} }
@ -33,14 +33,14 @@ func (r *RootCmd) open() *serpent.Cmd {
const vscodeDesktopName = "VS Code Desktop" const vscodeDesktopName = "VS Code Desktop"
func (r *RootCmd) openVSCode() *serpent.Cmd { func (r *RootCmd) openVSCode() *serpent.Command {
var ( var (
generateToken bool generateToken bool
testOpenError bool testOpenError bool
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "vscode <workspace> [<directory in workspace>]", Use: "vscode <workspace> [<directory in workspace>]",
Short: fmt.Sprintf("Open a workspace in %s", vscodeDesktopName), Short: fmt.Sprintf("Open a workspace in %s", vscodeDesktopName),

View File

@ -16,8 +16,8 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) organizations() *serpent.Cmd { func (r *RootCmd) organizations() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "organizations [subcommand]", Use: "organizations [subcommand]",
Short: "Organization related commands", Short: "Organization related commands",
@ -26,7 +26,7 @@ func (r *RootCmd) organizations() *serpent.Cmd {
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.currentOrganization(), r.currentOrganization(),
r.switchOrganization(), r.switchOrganization(),
r.createOrganization(), r.createOrganization(),
@ -37,10 +37,10 @@ func (r *RootCmd) organizations() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) switchOrganization() *serpent.Cmd { func (r *RootCmd) switchOrganization() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "set <organization name | ID>", Use: "set <organization name | ID>",
Short: "set the organization used by the CLI. Pass an empty string to reset to the default organization.", Short: "set the organization used by the CLI. Pass an empty string to reset to the default organization.",
Long: "set the organization used by the CLI. Pass an empty string to reset to the default organization.\n" + formatExamples( Long: "set the organization used by the CLI. Pass an empty string to reset to the default organization.\n" + formatExamples(
@ -206,7 +206,7 @@ func orgNames(orgs []codersdk.Organization) []string {
return names return names
} }
func (r *RootCmd) currentOrganization() *serpent.Cmd { func (r *RootCmd) currentOrganization() *serpent.Command {
var ( var (
stringFormat func(orgs []codersdk.Organization) (string, error) stringFormat func(orgs []codersdk.Organization) (string, error)
client = new(codersdk.Client) client = new(codersdk.Client)
@ -224,7 +224,7 @@ func (r *RootCmd) currentOrganization() *serpent.Cmd {
) )
onlyID = false onlyID = false
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "show [current|me|uuid]", Use: "show [current|me|uuid]",
Short: "Show the organization, if no argument is given, the organization currently in use will be shown.", Short: "Show the organization, if no argument is given, the organization currently in use will be shown.",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -12,10 +12,10 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) createOrganization() *serpent.Cmd { func (r *RootCmd) createOrganization() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "create <organization name>", Use: "create <organization name>",
Short: "Create a new organization.", Short: "Create a new organization.",
// This action is currently irreversible, so it's hidden until we have a way to delete organizations. // This action is currently irreversible, so it's hidden until we have a way to delete organizations.

View File

@ -17,7 +17,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) ping() *serpent.Cmd { func (r *RootCmd) ping() *serpent.Command {
var ( var (
pingNum int64 pingNum int64
pingTimeout time.Duration pingTimeout time.Duration
@ -25,7 +25,7 @@ func (r *RootCmd) ping() *serpent.Cmd {
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "ping <workspace>", Use: "ping <workspace>",
Short: "Ping a workspace", Short: "Ping a workspace",

View File

@ -23,14 +23,14 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) portForward() *serpent.Cmd { func (r *RootCmd) portForward() *serpent.Command {
var ( var (
tcpForwards []string // <port>:<port> tcpForwards []string // <port>:<port>
udpForwards []string // <port>:<port> udpForwards []string // <port>:<port>
disableAutostart bool disableAutostart bool
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "port-forward <workspace>", Use: "port-forward <workspace>",
Short: `Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R".`, Short: `Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R".`,
Aliases: []string{"tunnel"}, Aliases: []string{"tunnel"},

View File

@ -12,10 +12,10 @@ import (
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"
) )
func (r *RootCmd) publickey() *serpent.Cmd { func (r *RootCmd) publickey() *serpent.Command {
var reset bool var reset bool
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "publickey", Use: "publickey",
Aliases: []string{"pubkey"}, Aliases: []string{"pubkey"},
Short: "Output your Coder public key used for Git operations", Short: "Output your Coder public key used for Git operations",

View File

@ -12,9 +12,9 @@ import (
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"
) )
func (r *RootCmd) rename() *serpent.Cmd { func (r *RootCmd) rename() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "rename <workspace> <new name>", Use: "rename <workspace> <new name>",
Short: "Rename a workspace", Short: "Rename a workspace",

View File

@ -17,10 +17,10 @@ import (
"github.com/coder/coder/v2/coderd/userpassword" "github.com/coder/coder/v2/coderd/userpassword"
) )
func (*RootCmd) resetPassword() *serpent.Cmd { func (*RootCmd) resetPassword() *serpent.Command {
var postgresURL string var postgresURL string
root := &serpent.Cmd{ root := &serpent.Command{
Use: "reset-password <username>", Use: "reset-password <username>",
Short: "Directly connect to the database to reset a user's password", Short: "Directly connect to the database to reset a user's password",
Middleware: serpent.RequireNArgs(1), Middleware: serpent.RequireNArgs(1),

View File

@ -4,8 +4,8 @@ package cli
import "github.com/coder/serpent" import "github.com/coder/serpent"
func (*RootCmd) resetPassword() *serpent.Cmd { func (*RootCmd) resetPassword() *serpent.Command {
root := &serpent.Cmd{ root := &serpent.Command{
Use: "reset-password <username>", Use: "reset-password <username>",
Short: "Directly connect to the database to reset a user's password", Short: "Directly connect to the database to reset a user's password",
// We accept RawArgs so all commands and flags are accepted. // We accept RawArgs so all commands and flags are accepted.

View File

@ -13,11 +13,11 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) restart() *serpent.Cmd { func (r *RootCmd) restart() *serpent.Command {
var parameterFlags workspaceParameterFlags var parameterFlags workspaceParameterFlags
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "restart <workspace>", Use: "restart <workspace>",
Short: "Restart a workspace", Short: "Restart a workspace",

View File

@ -84,9 +84,9 @@ var (
errUnauthenticatedURLSaved = xerrors.New(notLoggedInURLSavedMessage) errUnauthenticatedURLSaved = xerrors.New(notLoggedInURLSavedMessage)
) )
func (r *RootCmd) Core() []*serpent.Cmd { func (r *RootCmd) Core() []*serpent.Command {
// Please re-sort this list alphabetically if you change it! // Please re-sort this list alphabetically if you change it!
return []*serpent.Cmd{ return []*serpent.Command{
r.dotfiles(), r.dotfiles(),
r.externalAuth(), r.externalAuth(),
r.login(), r.login(),
@ -132,13 +132,13 @@ func (r *RootCmd) Core() []*serpent.Cmd {
} }
} }
func (r *RootCmd) AGPL() []*serpent.Cmd { func (r *RootCmd) AGPL() []*serpent.Command {
all := append(r.Core(), r.Server( /* Do not import coderd here. */ nil)) all := append(r.Core(), r.Server( /* Do not import coderd here. */ nil))
return all return all
} }
// Main is the entrypoint for the Coder CLI. // Main is the entrypoint for the Coder CLI.
func (r *RootCmd) RunMain(subcommands []*serpent.Cmd) { func (r *RootCmd) RunMain(subcommands []*serpent.Command) {
rand.Seed(time.Now().UnixMicro()) rand.Seed(time.Now().UnixMicro())
cmd, err := r.Command(subcommands) cmd, err := r.Command(subcommands)
@ -166,10 +166,10 @@ func (r *RootCmd) RunMain(subcommands []*serpent.Cmd) {
} }
} }
func (r *RootCmd) Command(subcommands []*serpent.Cmd) (*serpent.Cmd, error) { func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, error) {
fmtLong := `Coder %s A tool for provisioning self-hosted development environments with Terraform. fmtLong := `Coder %s A tool for provisioning self-hosted development environments with Terraform.
` `
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "coder [global-flags] <subcommand>", Use: "coder [global-flags] <subcommand>",
Long: fmt.Sprintf(fmtLong, buildinfo.Version()) + formatExamples( Long: fmt.Sprintf(fmtLong, buildinfo.Version()) + formatExamples(
example{ example{
@ -200,7 +200,7 @@ func (r *RootCmd) Command(subcommands []*serpent.Cmd) (*serpent.Cmd, error) {
cmd.AddSubcommands(subcommands...) cmd.AddSubcommands(subcommands...)
// Set default help handler for all commands. // Set default help handler for all commands.
cmd.Walk(func(c *serpent.Cmd) { cmd.Walk(func(c *serpent.Command) {
if c.HelpHandler == nil { if c.HelpHandler == nil {
c.HelpHandler = helpFn() c.HelpHandler = helpFn()
} }
@ -208,7 +208,7 @@ func (r *RootCmd) Command(subcommands []*serpent.Cmd) (*serpent.Cmd, error) {
var merr error var merr error
// Add [flags] to usage when appropriate. // Add [flags] to usage when appropriate.
cmd.Walk(func(cmd *serpent.Cmd) { cmd.Walk(func(cmd *serpent.Command) {
const flags = "[flags]" const flags = "[flags]"
if strings.Contains(cmd.Use, flags) { if strings.Contains(cmd.Use, flags) {
merr = errors.Join( merr = errors.Join(
@ -244,7 +244,7 @@ func (r *RootCmd) Command(subcommands []*serpent.Cmd) (*serpent.Cmd, error) {
}) })
// Add alises when appropriate. // Add alises when appropriate.
cmd.Walk(func(cmd *serpent.Cmd) { cmd.Walk(func(cmd *serpent.Command) {
// TODO: we should really be consistent about naming. // TODO: we should really be consistent about naming.
if cmd.Name() == "delete" || cmd.Name() == "remove" { if cmd.Name() == "delete" || cmd.Name() == "remove" {
if slices.Contains(cmd.Aliases, "rm") { if slices.Contains(cmd.Aliases, "rm") {
@ -259,7 +259,7 @@ func (r *RootCmd) Command(subcommands []*serpent.Cmd) (*serpent.Cmd, error) {
}) })
// Sanity-check command options. // Sanity-check command options.
cmd.Walk(func(cmd *serpent.Cmd) { cmd.Walk(func(cmd *serpent.Command) {
for _, opt := range cmd.Options { for _, opt := range cmd.Options {
// Verify that every option is configurable. // Verify that every option is configurable.
if opt.Flag == "" && opt.Env == "" { if opt.Flag == "" && opt.Env == "" {
@ -282,7 +282,7 @@ func (r *RootCmd) Command(subcommands []*serpent.Cmd) (*serpent.Cmd, error) {
var debugOptions bool var debugOptions bool
// Add a wrapper to every command to enable debugging options. // Add a wrapper to every command to enable debugging options.
cmd.Walk(func(cmd *serpent.Cmd) { cmd.Walk(func(cmd *serpent.Command) {
h := cmd.Handler h := cmd.Handler
if h == nil { if h == nil {
// We should never have a nil handler, but if we do, do not // We should never have a nil handler, but if we do, do not

View File

@ -28,7 +28,7 @@ import (
//nolint:tparallel,paralleltest //nolint:tparallel,paralleltest
func TestCommandHelp(t *testing.T) { func TestCommandHelp(t *testing.T) {
// Test with AGPL commands // Test with AGPL commands
getCmds := func(t *testing.T) *serpent.Cmd { getCmds := func(t *testing.T) *serpent.Command {
// Must return a fresh instance of cmds each time. // Must return a fresh instance of cmds each time.
t.Helper() t.Helper()

View File

@ -53,15 +53,15 @@ When enabling scheduled stop, enter a duration in one of the following formats:
` `
) )
func (r *RootCmd) schedules() *serpent.Cmd { func (r *RootCmd) schedules() *serpent.Command {
scheduleCmd := &serpent.Cmd{ scheduleCmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "schedule { show | start | stop | override } <workspace>", Use: "schedule { show | start | stop | override } <workspace>",
Short: "Schedule automated start and stop times for workspaces", Short: "Schedule automated start and stop times for workspaces",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.scheduleShow(), r.scheduleShow(),
r.scheduleStart(), r.scheduleStart(),
r.scheduleStop(), r.scheduleStop(),
@ -73,7 +73,7 @@ func (r *RootCmd) schedules() *serpent.Cmd {
} }
// scheduleShow() is just a wrapper for list() with some different defaults. // scheduleShow() is just a wrapper for list() with some different defaults.
func (r *RootCmd) scheduleShow() *serpent.Cmd { func (r *RootCmd) scheduleShow() *serpent.Command {
var ( var (
filter cliui.WorkspaceFilter filter cliui.WorkspaceFilter
formatter = cliui.NewOutputFormatter( formatter = cliui.NewOutputFormatter(
@ -91,7 +91,7 @@ func (r *RootCmd) scheduleShow() *serpent.Cmd {
) )
) )
client := new(codersdk.Client) client := new(codersdk.Client)
showCmd := &serpent.Cmd{ showCmd := &serpent.Command{
Use: "show <workspace | --search <query> | --all>", Use: "show <workspace | --search <query> | --all>",
Short: "Show workspace schedules", Short: "Show workspace schedules",
Long: scheduleShowDescriptionLong, Long: scheduleShowDescriptionLong,
@ -136,9 +136,9 @@ func (r *RootCmd) scheduleShow() *serpent.Cmd {
return showCmd return showCmd
} }
func (r *RootCmd) scheduleStart() *serpent.Cmd { func (r *RootCmd) scheduleStart() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "start <workspace-name> { <start-time> [day-of-week] [location] | manual }", Use: "start <workspace-name> { <start-time> [day-of-week] [location] | manual }",
Long: scheduleStartDescriptionLong + "\n" + formatExamples( Long: scheduleStartDescriptionLong + "\n" + formatExamples(
example{ example{
@ -185,9 +185,9 @@ func (r *RootCmd) scheduleStart() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) scheduleStop() *serpent.Cmd { func (r *RootCmd) scheduleStop() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
return &serpent.Cmd{ return &serpent.Command{
Use: "stop <workspace-name> { <duration> | manual }", Use: "stop <workspace-name> { <duration> | manual }",
Long: scheduleStopDescriptionLong + "\n" + formatExamples( Long: scheduleStopDescriptionLong + "\n" + formatExamples(
example{ example{
@ -229,9 +229,9 @@ func (r *RootCmd) scheduleStop() *serpent.Cmd {
} }
} }
func (r *RootCmd) scheduleOverride() *serpent.Cmd { func (r *RootCmd) scheduleOverride() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
overrideCmd := &serpent.Cmd{ overrideCmd := &serpent.Command{
Use: "override-stop <workspace-name> <duration from now>", Use: "override-stop <workspace-name> <duration from now>",
Short: "Override the stop time of a currently running workspace instance.", Short: "Override the stop time of a currently running workspace instance.",
Long: scheduleOverrideDescriptionLong + "\n" + formatExamples( Long: scheduleOverrideDescriptionLong + "\n" + formatExamples(

View File

@ -258,7 +258,7 @@ func enablePrometheus(
), nil ), nil
} }
func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *serpent.Cmd { func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *serpent.Command {
if newAPI == nil { if newAPI == nil {
newAPI = func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { newAPI = func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) {
api := coderd.New(o) api := coderd.New(o)
@ -270,7 +270,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
vals = new(codersdk.DeploymentValues) vals = new(codersdk.DeploymentValues)
opts = vals.Options() opts = vals.Options()
) )
serverCmd := &serpent.Cmd{ serverCmd := &serpent.Command{
Use: "server", Use: "server",
Short: "Start a Coder server", Short: "Start a Coder server",
Options: opts, Options: opts,
@ -1148,7 +1148,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
var pgRawURL bool var pgRawURL bool
postgresBuiltinURLCmd := &serpent.Cmd{ postgresBuiltinURLCmd := &serpent.Command{
Use: "postgres-builtin-url", Use: "postgres-builtin-url",
Short: "Output the connection URL for the built-in PostgreSQL deployment.", Short: "Output the connection URL for the built-in PostgreSQL deployment.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
@ -1165,7 +1165,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
}, },
} }
postgresBuiltinServeCmd := &serpent.Cmd{ postgresBuiltinServeCmd := &serpent.Command{
Use: "postgres-builtin-serve", Use: "postgres-builtin-serve",
Short: "Run the built-in PostgreSQL deployment.", Short: "Run the built-in PostgreSQL deployment.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {

View File

@ -22,7 +22,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) newCreateAdminUserCommand() *serpent.Cmd { func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
var ( var (
newUserDBURL string newUserDBURL string
newUserSSHKeygenAlgorithm string newUserSSHKeygenAlgorithm string
@ -30,7 +30,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Cmd {
newUserEmail string newUserEmail string
newUserPassword string newUserPassword string
) )
createAdminUserCommand := &serpent.Cmd{ createAdminUserCommand := &serpent.Command{
Use: "create-admin-user", Use: "create-admin-user",
Short: "Create a new admin user with the given username, email and password and adds it to every organization.", Short: "Create a new admin user with the given username, email and password and adds it to every organization.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {

View File

@ -4,8 +4,8 @@ package cli
import "github.com/coder/serpent" import "github.com/coder/serpent"
func (r *RootCmd) Server(_ func()) *serpent.Cmd { func (r *RootCmd) Server(_ func()) *serpent.Command {
root := &serpent.Cmd{ root := &serpent.Command{
Use: "server", Use: "server",
Short: "Start a Coder server", Short: "Start a Coder server",
// We accept RawArgs so all commands and flags are accepted. // We accept RawArgs so all commands and flags are accepted.

View File

@ -8,9 +8,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) show() *serpent.Cmd { func (r *RootCmd) show() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
return &serpent.Cmd{ return &serpent.Command{
Use: "show <workspace>", Use: "show <workspace>",
Short: "Display details of a workspace's resources and agents", Short: "Display details of a workspace's resources and agents",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -18,7 +18,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) speedtest() *serpent.Cmd { func (r *RootCmd) speedtest() *serpent.Command {
var ( var (
direct bool direct bool
duration time.Duration duration time.Duration
@ -26,7 +26,7 @@ func (r *RootCmd) speedtest() *serpent.Cmd {
pcapFile string pcapFile string
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "speedtest <workspace>", Use: "speedtest <workspace>",
Short: "Run upload and download tests from your machine to a workspace", Short: "Run upload and download tests from your machine to a workspace",

View File

@ -44,7 +44,7 @@ var (
autostopNotifyCountdown = []time.Duration{30 * time.Minute} autostopNotifyCountdown = []time.Duration{30 * time.Minute}
) )
func (r *RootCmd) ssh() *serpent.Cmd { func (r *RootCmd) ssh() *serpent.Command {
var ( var (
stdio bool stdio bool
forwardAgent bool forwardAgent bool
@ -58,7 +58,7 @@ func (r *RootCmd) ssh() *serpent.Cmd {
disableAutostart bool disableAutostart bool
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "ssh <workspace>", Use: "ssh <workspace>",
Short: "Start a shell into a workspace", Short: "Start a shell into a workspace",

View File

@ -12,11 +12,11 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) start() *serpent.Cmd { func (r *RootCmd) start() *serpent.Command {
var parameterFlags workspaceParameterFlags var parameterFlags workspaceParameterFlags
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "start <workspace>", Use: "start <workspace>",
Short: "Start a workspace", Short: "Start a workspace",

View File

@ -26,7 +26,7 @@ func initStatterMW(tgt **clistat.Statter, fs afero.Fs) serpent.MiddlewareFunc {
} }
} }
func (r *RootCmd) stat() *serpent.Cmd { func (r *RootCmd) stat() *serpent.Command {
var ( var (
st *clistat.Statter st *clistat.Statter
fs = afero.NewReadOnlyFs(afero.NewOsFs()) fs = afero.NewReadOnlyFs(afero.NewOsFs())
@ -41,11 +41,11 @@ func (r *RootCmd) stat() *serpent.Cmd {
cliui.JSONFormat(), cliui.JSONFormat(),
) )
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "stat", Use: "stat",
Short: "Show resource usage for the current workspace.", Short: "Show resource usage for the current workspace.",
Middleware: initStatterMW(&st, fs), Middleware: initStatterMW(&st, fs),
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.statCPU(fs), r.statCPU(fs),
r.statMem(fs), r.statMem(fs),
r.statDisk(fs), r.statDisk(fs),
@ -130,13 +130,13 @@ func (r *RootCmd) stat() *serpent.Cmd {
return cmd return cmd
} }
func (*RootCmd) statCPU(fs afero.Fs) *serpent.Cmd { func (*RootCmd) statCPU(fs afero.Fs) *serpent.Command {
var ( var (
hostArg bool hostArg bool
st *clistat.Statter st *clistat.Statter
formatter = cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat()) formatter = cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat())
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "cpu", Use: "cpu",
Short: "Show CPU usage, in cores.", Short: "Show CPU usage, in cores.",
Middleware: initStatterMW(&st, fs), Middleware: initStatterMW(&st, fs),
@ -171,14 +171,14 @@ func (*RootCmd) statCPU(fs afero.Fs) *serpent.Cmd {
return cmd return cmd
} }
func (*RootCmd) statMem(fs afero.Fs) *serpent.Cmd { func (*RootCmd) statMem(fs afero.Fs) *serpent.Command {
var ( var (
hostArg bool hostArg bool
prefixArg string prefixArg string
st *clistat.Statter st *clistat.Statter
formatter = cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat()) formatter = cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat())
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "mem", Use: "mem",
Short: "Show memory usage, in gigabytes.", Short: "Show memory usage, in gigabytes.",
Middleware: initStatterMW(&st, fs), Middleware: initStatterMW(&st, fs),
@ -225,14 +225,14 @@ func (*RootCmd) statMem(fs afero.Fs) *serpent.Cmd {
return cmd return cmd
} }
func (*RootCmd) statDisk(fs afero.Fs) *serpent.Cmd { func (*RootCmd) statDisk(fs afero.Fs) *serpent.Command {
var ( var (
pathArg string pathArg string
prefixArg string prefixArg string
st *clistat.Statter st *clistat.Statter
formatter = cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat()) formatter = cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat())
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "disk", Use: "disk",
Short: "Show disk usage, in gigabytes.", Short: "Show disk usage, in gigabytes.",
Middleware: initStatterMW(&st, fs), Middleware: initStatterMW(&st, fs),

View File

@ -11,14 +11,14 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) state() *serpent.Cmd { func (r *RootCmd) state() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "state", Use: "state",
Short: "Manually manage Terraform state to fix broken workspaces", Short: "Manually manage Terraform state to fix broken workspaces",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.statePull(), r.statePull(),
r.statePush(), r.statePush(),
}, },
@ -26,10 +26,10 @@ func (r *RootCmd) state() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) statePull() *serpent.Cmd { func (r *RootCmd) statePull() *serpent.Command {
var buildNumber int64 var buildNumber int64
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "pull <workspace> [file]", Use: "pull <workspace> [file]",
Short: "Pull a Terraform state file from a workspace.", Short: "Pull a Terraform state file from a workspace.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
@ -84,10 +84,10 @@ func buildNumberOption(n *int64) serpent.Option {
} }
} }
func (r *RootCmd) statePush() *serpent.Cmd { func (r *RootCmd) statePush() *serpent.Command {
var buildNumber int64 var buildNumber int64
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "push <workspace> <file>", Use: "push <workspace> <file>",
Short: "Push a Terraform state file to a workspace.", Short: "Push a Terraform state file to a workspace.",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -9,9 +9,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) stop() *serpent.Cmd { func (r *RootCmd) stop() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "stop <workspace>", Use: "stop <workspace>",
Short: "Stop a workspace", Short: "Stop a workspace",

View File

@ -21,25 +21,25 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) support() *serpent.Cmd { func (r *RootCmd) support() *serpent.Command {
supportCmd := &serpent.Cmd{ supportCmd := &serpent.Command{
Use: "support", Use: "support",
Short: "Commands for troubleshooting issues with a Coder deployment.", Short: "Commands for troubleshooting issues with a Coder deployment.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Hidden: true, // TODO: un-hide once the must-haves from #12160 are completed. Hidden: true, // TODO: un-hide once the must-haves from #12160 are completed.
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.supportBundle(), r.supportBundle(),
}, },
} }
return supportCmd return supportCmd
} }
func (r *RootCmd) supportBundle() *serpent.Cmd { func (r *RootCmd) supportBundle() *serpent.Command {
var outputPath string var outputPath string
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "bundle <workspace> [<agent>]", Use: "bundle <workspace> [<agent>]",
Short: "Generate a support bundle to troubleshoot issues connecting to a workspace.", Short: "Generate a support bundle to troubleshoot issues connecting to a workspace.",
Long: `This command generates a file containing detailed troubleshooting information about the Coder deployment and workspace connections. You must specify a single workspace (and optionally an agent name).`, Long: `This command generates a file containing detailed troubleshooting information about the Coder deployment and workspace connections. You must specify a single workspace (and optionally an agent name).`,

View File

@ -16,7 +16,7 @@ import (
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"
) )
func (r *RootCmd) templateCreate() *serpent.Cmd { func (r *RootCmd) templateCreate() *serpent.Command {
var ( var (
provisioner string provisioner string
provisionerTags []string provisionerTags []string
@ -34,7 +34,7 @@ func (r *RootCmd) templateCreate() *serpent.Cmd {
uploadFlags templateUploadFlags uploadFlags templateUploadFlags
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "create [name]", Use: "create [name]",
Short: "DEPRECATED: Create a template from the current directory or as specified by flag", Short: "DEPRECATED: Create a template from the current directory or as specified by flag",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -14,9 +14,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) templateDelete() *serpent.Cmd { func (r *RootCmd) templateDelete() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "delete [name...]", Use: "delete [name...]",
Short: "Delete templates", Short: "Delete templates",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -15,7 +15,7 @@ import (
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"
) )
func (r *RootCmd) templateEdit() *serpent.Cmd { func (r *RootCmd) templateEdit() *serpent.Command {
const deprecatedFlagName = "deprecated" const deprecatedFlagName = "deprecated"
var ( var (
name string name string
@ -40,7 +40,7 @@ func (r *RootCmd) templateEdit() *serpent.Cmd {
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "edit <template>", Use: "edit <template>",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),

View File

@ -20,7 +20,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (*RootCmd) templateInit() *serpent.Cmd { func (*RootCmd) templateInit() *serpent.Command {
var templateID string var templateID string
exampleList, err := examples.List() exampleList, err := examples.List()
if err != nil { if err != nil {
@ -32,7 +32,7 @@ func (*RootCmd) templateInit() *serpent.Cmd {
templateIDs = append(templateIDs, ex.ID) templateIDs = append(templateIDs, ex.ID)
} }
sort.Strings(templateIDs) sort.Strings(templateIDs)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "init [directory]", Use: "init [directory]",
Short: "Get started with a templated template.", Short: "Get started with a templated template.",
Middleware: serpent.RequireRangeArgs(0, 1), Middleware: serpent.RequireRangeArgs(0, 1),

View File

@ -10,14 +10,14 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) templateList() *serpent.Cmd { func (r *RootCmd) templateList() *serpent.Command {
formatter := cliui.NewOutputFormatter( formatter := cliui.NewOutputFormatter(
cliui.TableFormat([]templateTableRow{}, []string{"name", "last updated", "used by"}), cliui.TableFormat([]templateTableRow{}, []string{"name", "last updated", "used by"}),
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "list", Use: "list",
Short: "List all the templates available for the organization", Short: "List all the templates available for the organization",
Aliases: []string{"ls"}, Aliases: []string{"ls"},

View File

@ -15,7 +15,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) templatePull() *serpent.Cmd { func (r *RootCmd) templatePull() *serpent.Command {
var ( var (
tarMode bool tarMode bool
zipMode bool zipMode bool
@ -23,7 +23,7 @@ func (r *RootCmd) templatePull() *serpent.Cmd {
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "pull <name> [destination]", Use: "pull <name> [destination]",
Short: "Download the active, latest, or specified version of a template to a path.", Short: "Download the active, latest, or specified version of a template to a path.",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -23,7 +23,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) templatePush() *serpent.Cmd { func (r *RootCmd) templatePush() *serpent.Command {
var ( var (
versionName string versionName string
provisioner string provisioner string
@ -36,7 +36,7 @@ func (r *RootCmd) templatePush() *serpent.Cmd {
activate bool activate bool
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "push [template]", Use: "push [template]",
Short: "Create or update a template from the current directory or as specified by flag", Short: "Create or update a template from the current directory or as specified by flag",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -12,8 +12,8 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) templates() *serpent.Cmd { func (r *RootCmd) templates() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "templates", Use: "templates",
Short: "Manage templates", Short: "Manage templates",
Long: "Templates are written in standard Terraform and describe the infrastructure for workspaces\n" + formatExamples( Long: "Templates are written in standard Terraform and describe the infrastructure for workspaces\n" + formatExamples(
@ -30,7 +30,7 @@ func (r *RootCmd) templates() *serpent.Cmd {
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.templateCreate(), r.templateCreate(),
r.templateEdit(), r.templateEdit(),
r.templateInit(), r.templateInit(),

View File

@ -14,16 +14,16 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) unarchiveTemplateVersion() *serpent.Cmd { func (r *RootCmd) unarchiveTemplateVersion() *serpent.Command {
return r.setArchiveTemplateVersion(false) return r.setArchiveTemplateVersion(false)
} }
func (r *RootCmd) archiveTemplateVersion() *serpent.Cmd { func (r *RootCmd) archiveTemplateVersion() *serpent.Command {
return r.setArchiveTemplateVersion(true) return r.setArchiveTemplateVersion(true)
} }
//nolint:revive //nolint:revive
func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Cmd { func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Command {
presentVerb := "archive" presentVerb := "archive"
pastVerb := "archived" pastVerb := "archived"
if !archive { if !archive {
@ -32,7 +32,7 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Cmd {
} }
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: presentVerb + " <template-name> [template-version-names...] ", Use: presentVerb + " <template-name> [template-version-names...] ",
Short: strings.ToUpper(string(presentVerb[0])) + presentVerb[1:] + " a template version(s).", Short: strings.ToUpper(string(presentVerb[0])) + presentVerb[1:] + " a template version(s).",
Middleware: serpent.Chain( Middleware: serpent.Chain(
@ -96,10 +96,10 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) archiveTemplateVersions() *serpent.Cmd { func (r *RootCmd) archiveTemplateVersions() *serpent.Command {
var all serpent.Bool var all serpent.Bool
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "archive [template-name...] ", Use: "archive [template-name...] ",
Short: "Archive unused or failed template versions from a given template(s)", Short: "Archive unused or failed template versions from a given template(s)",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -14,8 +14,8 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) templateVersions() *serpent.Cmd { func (r *RootCmd) templateVersions() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "versions", Use: "versions",
Short: "Manage different versions of the specified template", Short: "Manage different versions of the specified template",
Aliases: []string{"version"}, Aliases: []string{"version"},
@ -28,7 +28,7 @@ func (r *RootCmd) templateVersions() *serpent.Cmd {
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.templateVersionsList(), r.templateVersionsList(),
r.archiveTemplateVersion(), r.archiveTemplateVersion(),
r.unarchiveTemplateVersion(), r.unarchiveTemplateVersion(),
@ -38,7 +38,7 @@ func (r *RootCmd) templateVersions() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) templateVersionsList() *serpent.Cmd { func (r *RootCmd) templateVersionsList() *serpent.Command {
defaultColumns := []string{ defaultColumns := []string{
"Name", "Name",
"Created At", "Created At",
@ -54,7 +54,7 @@ func (r *RootCmd) templateVersionsList() *serpent.Cmd {
var includeArchived serpent.Bool var includeArchived serpent.Bool
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "list <template>", Use: "list <template>",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),

View File

@ -13,8 +13,8 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) tokens() *serpent.Cmd { func (r *RootCmd) tokens() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "tokens", Use: "tokens",
Short: "Manage personal access tokens", Short: "Manage personal access tokens",
Long: "Tokens are used to authenticate automated clients to Coder.\n" + formatExamples( Long: "Tokens are used to authenticate automated clients to Coder.\n" + formatExamples(
@ -35,7 +35,7 @@ func (r *RootCmd) tokens() *serpent.Cmd {
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.createToken(), r.createToken(),
r.listTokens(), r.listTokens(),
r.removeToken(), r.removeToken(),
@ -44,13 +44,13 @@ func (r *RootCmd) tokens() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) createToken() *serpent.Cmd { func (r *RootCmd) createToken() *serpent.Command {
var ( var (
tokenLifetime time.Duration tokenLifetime time.Duration
name string name string
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "create", Use: "create",
Short: "Create a token", Short: "Create a token",
Middleware: serpent.Chain( Middleware: serpent.Chain(
@ -118,7 +118,7 @@ func tokenListRowFromToken(token codersdk.APIKeyWithOwner) tokenListRow {
} }
} }
func (r *RootCmd) listTokens() *serpent.Cmd { func (r *RootCmd) listTokens() *serpent.Command {
// we only display the 'owner' column if the --all argument is passed in // we only display the 'owner' column if the --all argument is passed in
defaultCols := []string{"id", "name", "last used", "expires at", "created at"} defaultCols := []string{"id", "name", "last used", "expires at", "created at"}
if slices.Contains(os.Args, "-a") || slices.Contains(os.Args, "--all") { if slices.Contains(os.Args, "-a") || slices.Contains(os.Args, "--all") {
@ -135,7 +135,7 @@ func (r *RootCmd) listTokens() *serpent.Cmd {
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "list", Use: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Short: "List tokens", Short: "List tokens",
@ -187,9 +187,9 @@ func (r *RootCmd) listTokens() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) removeToken() *serpent.Cmd { func (r *RootCmd) removeToken() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "remove <name>", Use: "remove <name>",
Aliases: []string{"delete"}, Aliases: []string{"delete"},
Short: "Delete a token", Short: "Delete a token",

View File

@ -9,11 +9,11 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) update() *serpent.Cmd { func (r *RootCmd) update() *serpent.Command {
var parameterFlags workspaceParameterFlags var parameterFlags workspaceParameterFlags
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "update <workspace>", Use: "update <workspace>",
Short: "Will update and start a given workspace if it is out of date", Short: "Will update and start a given workspace if it is out of date",

View File

@ -15,7 +15,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) userCreate() *serpent.Cmd { func (r *RootCmd) userCreate() *serpent.Command {
var ( var (
email string email string
username string username string
@ -24,7 +24,7 @@ func (r *RootCmd) userCreate() *serpent.Cmd {
loginType string loginType string
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "create", Use: "create",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),

View File

@ -11,9 +11,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) userDelete() *serpent.Cmd { func (r *RootCmd) userDelete() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "delete <username|user_id>", Use: "delete <username|user_id>",
Short: "Delete a user by username or user_id.", Short: "Delete a user by username or user_id.",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -13,14 +13,14 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) userList() *serpent.Cmd { func (r *RootCmd) userList() *serpent.Command {
formatter := cliui.NewOutputFormatter( formatter := cliui.NewOutputFormatter(
cliui.TableFormat([]codersdk.User{}, []string{"username", "email", "created_at", "status"}), cliui.TableFormat([]codersdk.User{}, []string{"username", "email", "created_at", "status"}),
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "list", Use: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
@ -47,14 +47,14 @@ func (r *RootCmd) userList() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) userSingle() *serpent.Cmd { func (r *RootCmd) userSingle() *serpent.Command {
formatter := cliui.NewOutputFormatter( formatter := cliui.NewOutputFormatter(
&userShowFormat{}, &userShowFormat{},
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "show <username|user_id|'me'>", Use: "show <username|user_id|'me'>",
Short: "Show a single user. Use 'me' to indicate the currently authenticated user.", Short: "Show a single user. Use 'me' to indicate the currently authenticated user.",
Long: formatExamples( Long: formatExamples(

View File

@ -5,15 +5,15 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) users() *serpent.Cmd { func (r *RootCmd) users() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Short: "Manage users", Short: "Manage users",
Use: "users [subcommand]", Use: "users [subcommand]",
Aliases: []string{"user"}, Aliases: []string{"user"},
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.userCreate(), r.userCreate(),
r.userList(), r.userList(),
r.userSingle(), r.userSingle(),

View File

@ -14,7 +14,7 @@ import (
) )
// createUserStatusCommand sets a user status. // createUserStatusCommand sets a user status.
func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *serpent.Cmd { func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *serpent.Command {
var verb string var verb string
var pastVerb string var pastVerb string
var aliases []string var aliases []string
@ -36,7 +36,7 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *serpen
client := new(codersdk.Client) client := new(codersdk.Client)
var columns []string var columns []string
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: fmt.Sprintf("%s <username|user_id>", verb), Use: fmt.Sprintf("%s <username|user_id>", verb),
Short: short, Short: short,
Aliases: aliases, Aliases: aliases,

View File

@ -61,7 +61,7 @@ func defaultVersionInfo() *versionInfo {
} }
// version prints the coder version // version prints the coder version
func (*RootCmd) version(versionInfo func() *versionInfo) *serpent.Cmd { func (*RootCmd) version(versionInfo func() *versionInfo) *serpent.Command {
var ( var (
formatter = cliui.NewOutputFormatter( formatter = cliui.NewOutputFormatter(
cliui.TextFormat(), cliui.TextFormat(),
@ -70,7 +70,7 @@ func (*RootCmd) version(versionInfo func() *versionInfo) *serpent.Cmd {
vi = versionInfo() vi = versionInfo()
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "version", Use: "version",
Short: "Show coder version", Short: "Show coder version",
Options: serpent.OptionSet{}, Options: serpent.OptionSet{},

View File

@ -32,7 +32,7 @@ import (
// This command needs to remain stable for compatibility with // This command needs to remain stable for compatibility with
// various VS Code versions, so it's kept separate from our // various VS Code versions, so it's kept separate from our
// standard SSH command. // standard SSH command.
func (r *RootCmd) vscodeSSH() *serpent.Cmd { func (r *RootCmd) vscodeSSH() *serpent.Command {
var ( var (
sessionTokenFile string sessionTokenFile string
urlFile string urlFile string
@ -41,7 +41,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Cmd {
networkInfoInterval time.Duration networkInfoInterval time.Duration
waitEnum string waitEnum string
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
// A SSH config entry is added by the VS Code extension that // A SSH config entry is added by the VS Code extension that
// passes %h to ProxyCommand. The prefix of `coder-vscode--` // passes %h to ProxyCommand. The prefix of `coder-vscode--`
// is a magical string represented in our VS Code extension. // is a magical string represented in our VS Code extension.

View File

@ -22,8 +22,8 @@ import (
) )
func main() { func main() {
var root *serpent.Cmd var root *serpent.Command
root = &serpent.Cmd{ root = &serpent.Command{
Use: "cliui", Use: "cliui",
Short: "Used for visually testing UI components for the CLI.", Short: "Used for visually testing UI components for the CLI.",
HelpHandler: func(inv *serpent.Invocation) error { HelpHandler: func(inv *serpent.Invocation) error {
@ -37,7 +37,7 @@ func main() {
}, },
} }
root.Children = append(root.Children, &serpent.Cmd{ root.Children = append(root.Children, &serpent.Command{
Use: "prompt", Use: "prompt",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
_, err := cliui.Prompt(inv, cliui.PromptOptions{ _, err := cliui.Prompt(inv, cliui.PromptOptions{
@ -75,7 +75,7 @@ func main() {
}, },
}) })
root.Children = append(root.Children, &serpent.Cmd{ root.Children = append(root.Children, &serpent.Command{
Use: "select", Use: "select",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
value, err := cliui.Select(inv, cliui.SelectOptions{ value, err := cliui.Select(inv, cliui.SelectOptions{
@ -87,7 +87,7 @@ func main() {
}, },
}) })
root.Children = append(root.Children, &serpent.Cmd{ root.Children = append(root.Children, &serpent.Command{
Use: "job", Use: "job",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
job := codersdk.ProvisionerJob{ job := codersdk.ProvisionerJob{
@ -173,7 +173,7 @@ func main() {
}, },
}) })
root.Children = append(root.Children, &serpent.Cmd{ root.Children = append(root.Children, &serpent.Command{
Use: "agent", Use: "agent",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var agent codersdk.WorkspaceAgent var agent codersdk.WorkspaceAgent
@ -265,7 +265,7 @@ func main() {
}, },
}) })
root.Children = append(root.Children, &serpent.Cmd{ root.Children = append(root.Children, &serpent.Command{
Use: "resources", Use: "resources",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
disconnected := dbtime.Now().Add(-4 * time.Second) disconnected := dbtime.Now().Add(-4 * time.Second)
@ -315,7 +315,7 @@ func main() {
}, },
}) })
root.Children = append(root.Children, &serpent.Cmd{ root.Children = append(root.Children, &serpent.Command{
Use: "git-auth", Use: "git-auth",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var count atomic.Int32 var count atomic.Int32

View File

@ -15,22 +15,22 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) features() *serpent.Cmd { func (r *RootCmd) features() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Short: "List Enterprise features", Short: "List Enterprise features",
Use: "features", Use: "features",
Aliases: []string{"feature"}, Aliases: []string{"feature"},
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.featuresList(), r.featuresList(),
}, },
} }
return cmd return cmd
} }
func (r *RootCmd) featuresList() *serpent.Cmd { func (r *RootCmd) featuresList() *serpent.Command {
var ( var (
featureColumns = []string{"Name", "Entitlement", "Enabled", "Limit", "Actual"} featureColumns = []string{"Name", "Entitlement", "Enabled", "Limit", "Actual"}
columns []string columns []string
@ -38,7 +38,7 @@ func (r *RootCmd) featuresList() *serpent.Cmd {
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "list", Use: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -12,14 +12,14 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) groupCreate() *serpent.Cmd { func (r *RootCmd) groupCreate() *serpent.Command {
var ( var (
avatarURL string avatarURL string
displayName string displayName string
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "create <name>", Use: "create <name>",
Short: "Create a user group", Short: "Create a user group",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -12,9 +12,9 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) groupDelete() *serpent.Cmd { func (r *RootCmd) groupDelete() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "delete <name>", Use: "delete <name>",
Short: "Delete a user group", Short: "Delete a user group",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -15,7 +15,7 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) groupEdit() *serpent.Cmd { func (r *RootCmd) groupEdit() *serpent.Command {
var ( var (
avatarURL string avatarURL string
name string name string
@ -24,7 +24,7 @@ func (r *RootCmd) groupEdit() *serpent.Cmd {
rmUsers []string rmUsers []string
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "edit <name>", Use: "edit <name>",
Short: "Edit a user group", Short: "Edit a user group",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -13,14 +13,14 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) groupList() *serpent.Cmd { func (r *RootCmd) groupList() *serpent.Command {
formatter := cliui.NewOutputFormatter( formatter := cliui.NewOutputFormatter(
cliui.TableFormat([]groupTableRow{}, nil), cliui.TableFormat([]groupTableRow{}, nil),
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "list", Use: "list",
Short: "List user groups", Short: "List user groups",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -2,15 +2,15 @@ package cli
import "github.com/coder/serpent" import "github.com/coder/serpent"
func (r *RootCmd) groups() *serpent.Cmd { func (r *RootCmd) groups() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "groups", Use: "groups",
Short: "Manage groups", Short: "Manage groups",
Aliases: []string{"group"}, Aliases: []string{"group"},
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.groupCreate(), r.groupCreate(),
r.groupList(), r.groupList(),
r.groupEdit(), r.groupEdit(),

View File

@ -20,15 +20,15 @@ import (
var jwtRegexp = regexp.MustCompile(`^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$`) var jwtRegexp = regexp.MustCompile(`^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$`)
func (r *RootCmd) licenses() *serpent.Cmd { func (r *RootCmd) licenses() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Short: "Add, delete, and list licenses", Short: "Add, delete, and list licenses",
Use: "licenses", Use: "licenses",
Aliases: []string{"license"}, Aliases: []string{"license"},
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.licenseAdd(), r.licenseAdd(),
r.licensesList(), r.licensesList(),
r.licenseDelete(), r.licenseDelete(),
@ -37,14 +37,14 @@ func (r *RootCmd) licenses() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) licenseAdd() *serpent.Cmd { func (r *RootCmd) licenseAdd() *serpent.Command {
var ( var (
filename string filename string
license string license string
debug bool debug bool
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "add [-f file | -l license]", Use: "add [-f file | -l license]",
Short: "Add license to Coder deployment", Short: "Add license to Coder deployment",
Middleware: serpent.Chain( Middleware: serpent.Chain(
@ -136,7 +136,7 @@ func validJWT(s string) error {
return xerrors.New("Invalid license") return xerrors.New("Invalid license")
} }
func (r *RootCmd) licensesList() *serpent.Cmd { func (r *RootCmd) licensesList() *serpent.Command {
type tableLicense struct { type tableLicense struct {
ID int32 `table:"id,default_sort"` ID int32 `table:"id,default_sort"`
UUID uuid.UUID `table:"uuid" format:"uuid"` UUID uuid.UUID `table:"uuid" format:"uuid"`
@ -208,7 +208,7 @@ func (r *RootCmd) licensesList() *serpent.Cmd {
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "list", Use: "list",
Short: "List licenses (including expired)", Short: "List licenses (including expired)",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
@ -239,9 +239,9 @@ func (r *RootCmd) licensesList() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) licenseDelete() *serpent.Cmd { func (r *RootCmd) licenseDelete() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "delete <id>", Use: "delete <id>",
Short: "Delete license by ID", Short: "Delete license by ID",
Aliases: []string{"del"}, Aliases: []string{"del"},

View File

@ -32,14 +32,14 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) provisionerDaemons() *serpent.Cmd { func (r *RootCmd) provisionerDaemons() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "provisionerd", Use: "provisionerd",
Short: "Manage provisioner daemons", Short: "Manage provisioner daemons",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.provisionerDaemonStart(), r.provisionerDaemonStart(),
}, },
} }
@ -57,7 +57,7 @@ func validateProvisionerDaemonName(name string) error {
return nil return nil
} }
func (r *RootCmd) provisionerDaemonStart() *serpent.Cmd { func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
var ( var (
cacheDir string cacheDir string
logHuman string logHuman string
@ -75,7 +75,7 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Cmd {
prometheusAddress string prometheusAddress string
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "start", Use: "start",
Short: "Run a provisioner daemon", Short: "Run a provisioner daemon",
Middleware: serpent.Chain( Middleware: serpent.Chain(

View File

@ -7,8 +7,8 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) provisionerDaemons() *serpent.Cmd { func (r *RootCmd) provisionerDaemons() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "provisionerd", Use: "provisionerd",
Short: "Manage provisioner daemons", Short: "Manage provisioner daemons",
// We accept RawArgs so all commands and flags are accepted. // We accept RawArgs so all commands and flags are accepted.

View File

@ -44,7 +44,7 @@ func (c *closers) Add(f func()) {
*c = append(*c, f) *c = append(*c, f)
} }
func (r *RootCmd) proxyServer() *serpent.Cmd { func (r *RootCmd) proxyServer() *serpent.Command {
var ( var (
cfg = new(codersdk.DeploymentValues) cfg = new(codersdk.DeploymentValues)
// Filter options for only relevant ones. // Filter options for only relevant ones.
@ -102,7 +102,7 @@ func (r *RootCmd) proxyServer() *serpent.Cmd {
}, },
) )
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "server", Use: "server",
Short: "Start a workspace proxy server", Short: "Start a workspace proxy server",
Options: opts, Options: opts,

View File

@ -7,8 +7,8 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) proxyServer() *serpent.Cmd { func (r *RootCmd) proxyServer() *serpent.Command {
root := &serpent.Cmd{ root := &serpent.Command{
Use: "server", Use: "server",
Short: "Start a workspace proxy server", Short: "Start a workspace proxy server",
Aliases: []string{}, Aliases: []string{},

View File

@ -9,8 +9,8 @@ type RootCmd struct {
cli.RootCmd cli.RootCmd
} }
func (r *RootCmd) enterpriseOnly() []*serpent.Cmd { func (r *RootCmd) enterpriseOnly() []*serpent.Command {
return []*serpent.Cmd{ return []*serpent.Command{
r.Server(nil), r.Server(nil),
r.workspaceProxy(), r.workspaceProxy(),
r.features(), r.features(),
@ -20,7 +20,7 @@ func (r *RootCmd) enterpriseOnly() []*serpent.Cmd {
} }
} }
func (r *RootCmd) EnterpriseSubcommands() []*serpent.Cmd { func (r *RootCmd) EnterpriseSubcommands() []*serpent.Command {
all := append(r.Core(), r.enterpriseOnly()...) all := append(r.Core(), r.enterpriseOnly()...)
return all return all
} }

View File

@ -13,7 +13,7 @@ import (
//nolint:tparallel,paralleltest //nolint:tparallel,paralleltest
func TestEnterpriseCommandHelp(t *testing.T) { func TestEnterpriseCommandHelp(t *testing.T) {
// Only test the enterprise commands // Only test the enterprise commands
getCmds := func(t *testing.T) *serpent.Cmd { getCmds := func(t *testing.T) *serpent.Command {
// Must return a fresh instance of cmds each time. // Must return a fresh instance of cmds each time.
t.Helper() t.Helper()
var root cli.RootCmd var root cli.RootCmd

View File

@ -28,7 +28,7 @@ import (
agplcoderd "github.com/coder/coder/v2/coderd" agplcoderd "github.com/coder/coder/v2/coderd"
) )
func (r *RootCmd) Server(_ func()) *serpent.Cmd { func (r *RootCmd) Server(_ func()) *serpent.Command {
cmd := r.RootCmd.Server(func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { cmd := r.RootCmd.Server(func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) {
if options.DeploymentValues.DERP.Server.RelayURL.String() != "" { if options.DeploymentValues.DERP.Server.RelayURL.String() != "" {
_, err := url.Parse(options.DeploymentValues.DERP.Server.RelayURL.String()) _, err := url.Parse(options.DeploymentValues.DERP.Server.RelayURL.String())

View File

@ -18,8 +18,8 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
) )
func (r *RootCmd) dbcryptCmd() *serpent.Cmd { func (r *RootCmd) dbcryptCmd() *serpent.Command {
dbcryptCmd := &serpent.Cmd{ dbcryptCmd := &serpent.Command{
Use: "dbcrypt", Use: "dbcrypt",
Short: "Manage database encryption.", Short: "Manage database encryption.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
@ -34,9 +34,9 @@ func (r *RootCmd) dbcryptCmd() *serpent.Cmd {
return dbcryptCmd return dbcryptCmd
} }
func (*RootCmd) dbcryptRotateCmd() *serpent.Cmd { func (*RootCmd) dbcryptRotateCmd() *serpent.Command {
var flags rotateFlags var flags rotateFlags
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "rotate", Use: "rotate",
Short: "Rotate database encryption keys.", Short: "Rotate database encryption keys.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
@ -107,9 +107,9 @@ func (*RootCmd) dbcryptRotateCmd() *serpent.Cmd {
return cmd return cmd
} }
func (*RootCmd) dbcryptDecryptCmd() *serpent.Cmd { func (*RootCmd) dbcryptDecryptCmd() *serpent.Command {
var flags decryptFlags var flags decryptFlags
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "decrypt", Use: "decrypt",
Short: "Decrypt a previously encrypted database.", Short: "Decrypt a previously encrypted database.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
@ -164,9 +164,9 @@ func (*RootCmd) dbcryptDecryptCmd() *serpent.Cmd {
return cmd return cmd
} }
func (*RootCmd) dbcryptDeleteCmd() *serpent.Cmd { func (*RootCmd) dbcryptDeleteCmd() *serpent.Command {
var flags deleteFlags var flags deleteFlags
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "delete", Use: "delete",
Short: "Delete all encrypted data from the database. THIS IS A DESTRUCTIVE OPERATION.", Short: "Delete all encrypted data from the database. THIS IS A DESTRUCTIVE OPERATION.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {

View File

@ -15,8 +15,8 @@ import (
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) workspaceProxy() *serpent.Cmd { func (r *RootCmd) workspaceProxy() *serpent.Command {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "workspace-proxy", Use: "workspace-proxy",
Short: "Workspace proxies provide low-latency experiences for geo-distributed teams.", Short: "Workspace proxies provide low-latency experiences for geo-distributed teams.",
Long: "Workspace proxies provide low-latency experiences for geo-distributed teams. " + Long: "Workspace proxies provide low-latency experiences for geo-distributed teams. " +
@ -27,7 +27,7 @@ func (r *RootCmd) workspaceProxy() *serpent.Cmd {
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
}, },
Children: []*serpent.Cmd{ Children: []*serpent.Command{
r.proxyServer(), r.proxyServer(),
r.createProxy(), r.createProxy(),
r.deleteProxy(), r.deleteProxy(),
@ -40,10 +40,10 @@ func (r *RootCmd) workspaceProxy() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) regenerateProxyToken() *serpent.Cmd { func (r *RootCmd) regenerateProxyToken() *serpent.Command {
formatter := newUpdateProxyResponseFormatter() formatter := newUpdateProxyResponseFormatter()
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "regenerate-token <name|id>", Use: "regenerate-token <name|id>",
Short: "Regenerate a workspace proxy authentication token. " + Short: "Regenerate a workspace proxy authentication token. " +
"This will invalidate the existing authentication token.", "This will invalidate the existing authentication token.",
@ -86,7 +86,7 @@ func (r *RootCmd) regenerateProxyToken() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) patchProxy() *serpent.Cmd { func (r *RootCmd) patchProxy() *serpent.Command {
var ( var (
proxyName string proxyName string
displayName string displayName string
@ -113,7 +113,7 @@ func (r *RootCmd) patchProxy() *serpent.Cmd {
) )
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "edit <name|id>", Use: "edit <name|id>",
Short: "Edit a workspace proxy", Short: "Edit a workspace proxy",
Middleware: serpent.Chain( Middleware: serpent.Chain(
@ -186,9 +186,9 @@ func (r *RootCmd) patchProxy() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) deleteProxy() *serpent.Cmd { func (r *RootCmd) deleteProxy() *serpent.Command {
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "delete <name|id>", Use: "delete <name|id>",
Short: "Delete a workspace proxy", Short: "Delete a workspace proxy",
Options: serpent.OptionSet{ Options: serpent.OptionSet{
@ -229,7 +229,7 @@ func (r *RootCmd) deleteProxy() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) createProxy() *serpent.Cmd { func (r *RootCmd) createProxy() *serpent.Command {
var ( var (
proxyName string proxyName string
displayName string displayName string
@ -245,7 +245,7 @@ func (r *RootCmd) createProxy() *serpent.Cmd {
} }
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "create", Use: "create",
Short: "Create a workspace proxy", Short: "Create a workspace proxy",
Middleware: serpent.Chain( Middleware: serpent.Chain(
@ -335,7 +335,7 @@ func (r *RootCmd) createProxy() *serpent.Cmd {
return cmd return cmd
} }
func (r *RootCmd) listProxies() *serpent.Cmd { func (r *RootCmd) listProxies() *serpent.Command {
formatter := cliui.NewOutputFormatter( formatter := cliui.NewOutputFormatter(
cliui.TableFormat([]codersdk.WorkspaceProxy{}, []string{"name", "url", "proxy status"}), cliui.TableFormat([]codersdk.WorkspaceProxy{}, []string{"name", "url", "proxy status"}),
cliui.JSONFormat(), cliui.JSONFormat(),
@ -363,7 +363,7 @@ func (r *RootCmd) listProxies() *serpent.Cmd {
) )
client := new(codersdk.Client) client := new(codersdk.Client)
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "ls", Use: "ls",
Aliases: []string{"list"}, Aliases: []string{"list"},
Short: "List all workspace proxies", Short: "List all workspace proxies",

2
go.mod
View File

@ -208,7 +208,7 @@ require go.uber.org/mock v0.4.0
require ( require (
github.com/benbjohnson/clock v1.3.5 github.com/benbjohnson/clock v1.3.5
github.com/coder/serpent v0.4.0 github.com/coder/serpent v0.4.1-0.20240315163851-a0148c87ea3f
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47
) )

2
go.sum
View File

@ -216,6 +216,8 @@ github.com/coder/retry v1.5.1 h1:iWu8YnD8YqHs3XwqrqsjoBTAVqT9ml6z9ViJ2wlMiqc=
github.com/coder/retry v1.5.1/go.mod h1:blHMk9vs6LkoRT9ZHyuZo360cufXEhrxqvEzeMtRGoY= github.com/coder/retry v1.5.1/go.mod h1:blHMk9vs6LkoRT9ZHyuZo360cufXEhrxqvEzeMtRGoY=
github.com/coder/serpent v0.4.0 h1:L/itwnCxfhLutxQ2mScP3tH1ro8z8+Kc/iKKyZZxEMk= github.com/coder/serpent v0.4.0 h1:L/itwnCxfhLutxQ2mScP3tH1ro8z8+Kc/iKKyZZxEMk=
github.com/coder/serpent v0.4.0/go.mod h1:Wud83ikZF/ulbdMcEMAwqvkEIQx7+l47+ef69M/arAA= github.com/coder/serpent v0.4.0/go.mod h1:Wud83ikZF/ulbdMcEMAwqvkEIQx7+l47+ef69M/arAA=
github.com/coder/serpent v0.4.1-0.20240315163851-a0148c87ea3f h1:nqJ/Mvm+nLI22n5BIYhvSmTZ6CD+MRo/aGVZwVQgr1k=
github.com/coder/serpent v0.4.1-0.20240315163851-a0148c87ea3f/go.mod h1:REkJ5ZFHQUWFTPLExhXYZ1CaHFjxvGNRlLXLdsI08YA=
github.com/coder/ssh v0.0.0-20231128192721-70855dedb788 h1:YoUSJ19E8AtuUFVYBpXuOD6a/zVP3rcxezNsoDseTUw= github.com/coder/ssh v0.0.0-20231128192721-70855dedb788 h1:YoUSJ19E8AtuUFVYBpXuOD6a/zVP3rcxezNsoDseTUw=
github.com/coder/ssh v0.0.0-20231128192721-70855dedb788/go.mod h1:aGQbuCLyhRLMzZF067xc84Lh7JDs1FKwCmF1Crl9dxQ= github.com/coder/ssh v0.0.0-20231128192721-70855dedb788/go.mod h1:aGQbuCLyhRLMzZF067xc84Lh7JDs1FKwCmF1Crl9dxQ=
github.com/coder/tailscale v1.1.1-0.20240214140224-3788ab894ba1 h1:A7dZHNidAVH6Kxn5D3hTEH+iRO8slnM0aRer6/cxlyE= github.com/coder/tailscale v1.1.1-0.20240214140224-3788ab894ba1 h1:A7dZHNidAVH6Kxn5D3hTEH+iRO8slnM0aRer6/cxlyE=

View File

@ -59,7 +59,7 @@ func TestPtytest(t *testing.T) {
tt := tt tt := tt
// nolint:paralleltest // Avoid parallel test to more easily identify the issue. // nolint:paralleltest // Avoid parallel test to more easily identify the issue.
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
cmd := &serpent.Cmd{ cmd := &serpent.Command{
Use: "test", Use: "test",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
fmt.Fprint(inv.Stdout, tt.output) fmt.Fprint(inv.Stdout, tt.output)

View File

@ -25,8 +25,8 @@ var commandTemplate *template.Template
func init() { func init() {
commandTemplate = template.Must( commandTemplate = template.Must(
template.New("command.tpl").Funcs(template.FuncMap{ template.New("command.tpl").Funcs(template.FuncMap{
"visibleSubcommands": func(cmd *serpent.Cmd) []*serpent.Cmd { "visibleSubcommands": func(cmd *serpent.Command) []*serpent.Command {
var visible []*serpent.Cmd var visible []*serpent.Command
for _, sub := range cmd.Children { for _, sub := range cmd.Children {
if sub.Hidden { if sub.Hidden {
continue continue
@ -35,7 +35,7 @@ func init() {
} }
return visible return visible
}, },
"visibleOptions": func(cmd *serpent.Cmd) []serpent.Option { "visibleOptions": func(cmd *serpent.Command) []serpent.Option {
var visible []serpent.Option var visible []serpent.Option
for _, opt := range cmd.Options { for _, opt := range cmd.Options {
if opt.Hidden { if opt.Hidden {
@ -45,7 +45,7 @@ func init() {
} }
return visible return visible
}, },
"atRoot": func(cmd *serpent.Cmd) bool { "atRoot": func(cmd *serpent.Command) bool {
return cmd.FullName() == "coder" return cmd.FullName() == "coder"
}, },
"newLinesToBr": func(s string) string { "newLinesToBr": func(s string) string {
@ -54,7 +54,7 @@ func init() {
"wrapCode": func(s string) string { "wrapCode": func(s string) string {
return fmt.Sprintf("<code>%s</code>", s) return fmt.Sprintf("<code>%s</code>", s)
}, },
"commandURI": func(cmd *serpent.Cmd) string { "commandURI": func(cmd *serpent.Command) string {
return fmtDocFilename(cmd) return fmtDocFilename(cmd)
}, },
"fullName": fullName, "fullName": fullName,
@ -67,14 +67,14 @@ func init() {
) )
} }
func fullName(cmd *serpent.Cmd) string { func fullName(cmd *serpent.Command) string {
if cmd.FullName() == "coder" { if cmd.FullName() == "coder" {
return "coder" return "coder"
} }
return strings.TrimPrefix(cmd.FullName(), "coder ") return strings.TrimPrefix(cmd.FullName(), "coder ")
} }
func fmtDocFilename(cmd *serpent.Cmd) string { func fmtDocFilename(cmd *serpent.Command) string {
if cmd.FullName() == "coder" { if cmd.FullName() == "coder" {
// Special case for index. // Special case for index.
return "../cli.md" return "../cli.md"
@ -83,7 +83,7 @@ func fmtDocFilename(cmd *serpent.Cmd) string {
return fmt.Sprintf("%s.md", name) return fmt.Sprintf("%s.md", name)
} }
func writeCommand(w io.Writer, cmd *serpent.Cmd) error { func writeCommand(w io.Writer, cmd *serpent.Command) error {
var b strings.Builder var b strings.Builder
err := commandTemplate.Execute(&b, cmd) err := commandTemplate.Execute(&b, cmd)
if err != nil { if err != nil {
@ -112,7 +112,7 @@ func writeCommand(w io.Writer, cmd *serpent.Cmd) error {
return err return err
} }
func genTree(dir string, cmd *serpent.Cmd, wroteLog map[string]*serpent.Cmd) error { func genTree(dir string, cmd *serpent.Command, wroteLog map[string]*serpent.Command) error {
if cmd.Hidden { if cmd.Hidden {
return nil return nil
} }

View File

@ -83,7 +83,7 @@ func main() {
root := (&cli.RootCmd{}) root := (&cli.RootCmd{})
// wroteMap indexes file paths to commands. // wroteMap indexes file paths to commands.
wroteMap := make(map[string]*serpent.Cmd) wroteMap := make(map[string]*serpent.Command)
var ( var (
docsDir = filepath.Join(workdir, "docs") docsDir = filepath.Join(workdir, "docs")