diff --git a/internal/cmd/completion.go b/internal/cmd/completion.go index dd01945e..dfa8992f 100644 --- a/internal/cmd/completion.go +++ b/internal/cmd/completion.go @@ -7,14 +7,11 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(completionCmd) -} - -var completionCmd = &cobra.Command{ - Use: "completion [bash|zsh|fish|powershell]", - Short: "Generate completion script for your shell", - Long: `To load completions: +func CompletionCmd() *cobra.Command { + return &cobra.Command{ + Use: "completion [bash|zsh|fish|powershell]", + Short: "Generate completion script for your shell", + Long: `To load completions: Bash: @@ -53,19 +50,20 @@ PowerShell: PS> pscale completion powershell > pscale.ps1 # and source this file from your PowerShell profile. `, - DisableFlagsInUseLine: true, - ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, - Args: cmdutil.RequiredArgs("shell"), - Run: func(cmd *cobra.Command, args []string) { - switch args[0] { - case "bash": - cmd.Root().GenBashCompletion(os.Stdout) - case "zsh": - cmd.Root().GenZshCompletion(os.Stdout) - case "fish": - cmd.Root().GenFishCompletion(os.Stdout, true) - case "powershell": - cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) - } - }, + DisableFlagsInUseLine: true, + ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, + Args: cmdutil.RequiredArgs("shell"), + Run: func(cmd *cobra.Command, args []string) { + switch args[0] { + case "bash": + cmd.Root().GenBashCompletion(os.Stdout) + case "zsh": + cmd.Root().GenZshCompletion(os.Stdout) + case "fish": + cmd.Root().GenFishCompletion(os.Stdout, true) + case "powershell": + cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) + } + }, + } } diff --git a/internal/cmd/connect/connect.go b/internal/cmd/connect/connect.go index b614ec6d..61a3abd7 100644 --- a/internal/cmd/connect/connect.go +++ b/internal/cmd/connect/connect.go @@ -44,6 +44,7 @@ func ConnectCmd(ch *cmdutil.Helper) *cobra.Command { // we only require database, because we deduct branch automatically Args: cmdutil.RequiredArgs("database"), Short: "Create a secure connection to a database and branch for a local client", + Long: "Create a secure connection to a database and branch for a local client.\n\nThis command is only supported for Vitess databases.", Example: `The connect subcommand establishes a secure connection between your host and PlanetScale. By default, if no branch names are given and there is only one branch, it diff --git a/internal/cmd/database/dump.go b/internal/cmd/database/dump.go index e3e07471..93983034 100644 --- a/internal/cmd/database/dump.go +++ b/internal/cmd/database/dump.go @@ -44,7 +44,8 @@ func DumpCmd(ch *cmdutil.Helper) *cobra.Command { f := &dumpFlags{} cmd := &cobra.Command{ Use: "dump [options]", - Short: "Backup and dump your database", + Short: "Backup and dump your database (Vitess databases only)", + Long: "Backup and dump your database.\n\nThis command is only supported for Vitess databases. For Postgres databases, use standard PostgreSQL tools like pg_dump. See: https://planetscale.com/docs/postgres/imports/postgres-migrate-dumprestore", Args: cmdutil.RequiredArgs("database", "branch"), RunE: func(cmd *cobra.Command, args []string) error { return dump(ch, cmd, f, args) }, } diff --git a/internal/cmd/database/restore.go b/internal/cmd/database/restore.go index e618ce9c..84823ad9 100644 --- a/internal/cmd/database/restore.go +++ b/internal/cmd/database/restore.go @@ -39,7 +39,8 @@ func RestoreCmd(ch *cmdutil.Helper) *cobra.Command { f := &restoreFlags{} cmd := &cobra.Command{ Use: "restore-dump [options]", - Short: "Restore your database from a local dump directory", + Short: "Restore your database from a local dump directory (Vitess databases only)", + Long: "Restore your database from a local dump directory.\n\nThis command is only supported for Vitess databases. For Postgres databases, use standard PostgreSQL tools like pg_restore. See: https://planetscale.com/docs/postgres/imports/postgres-migrate-dumprestore", Args: cmdutil.RequiredArgs("database", "branch"), RunE: func(cmd *cobra.Command, args []string) error { return restore(ch, cmd, f, args) }, } diff --git a/internal/cmd/dataimports/dataimports.go b/internal/cmd/dataimports/dataimports.go index 5a374dbe..a0ab21a6 100644 --- a/internal/cmd/dataimports/dataimports.go +++ b/internal/cmd/dataimports/dataimports.go @@ -10,6 +10,7 @@ func DataImportsCmd(ch *cmdutil.Helper) *cobra.Command { cmd := &cobra.Command{ Use: "data-imports ", Short: "Create, list, and delete branch data imports", + Long: "Create, list, and delete branch data imports.\n\nThis command is only supported for Vitess databases.", PersistentPreRunE: cmdutil.CheckAuthentication(ch.Config), } diff --git a/internal/cmd/deployrequest/dr.go b/internal/cmd/deployrequest/dr.go index eef3dc1c..61f4ea5d 100644 --- a/internal/cmd/deployrequest/dr.go +++ b/internal/cmd/deployrequest/dr.go @@ -16,6 +16,7 @@ func DeployRequestCmd(ch *cmdutil.Helper) *cobra.Command { cmd := &cobra.Command{ Use: "deploy-request ", Short: "Create, review, diff, revert, and manage deploy requests", + Long: "Create, review, diff, revert, and manage deploy requests.\n\nThis command is only supported for Vitess databases.", Aliases: []string{"dr"}, PersistentPreRunE: cmdutil.CheckAuthentication(ch.Config), } @@ -79,9 +80,9 @@ func formatTimestamp(t *time.Time) string { if t == nil || t.IsZero() { return "" } - + duration := time.Since(*t) - + switch { case duration < time.Minute: return "less than a minute ago" diff --git a/internal/cmd/keyspace/keyspace.go b/internal/cmd/keyspace/keyspace.go index b4dba2c9..edfb6aa4 100644 --- a/internal/cmd/keyspace/keyspace.go +++ b/internal/cmd/keyspace/keyspace.go @@ -13,6 +13,7 @@ func KeyspaceCmd(ch *cmdutil.Helper) *cobra.Command { cmd := &cobra.Command{ Use: "keyspace ", Short: "List, show, and manage keyspaces", + Long: "List, show, and manage keyspaces.\n\nThis command is only supported for Vitess databases.", PersistentPreRunE: cmdutil.CheckAuthentication(ch.Config), } diff --git a/internal/cmd/password/password.go b/internal/cmd/password/password.go index 333e8b14..9cc15e47 100644 --- a/internal/cmd/password/password.go +++ b/internal/cmd/password/password.go @@ -17,6 +17,7 @@ func PasswordCmd(ch *cmdutil.Helper) *cobra.Command { cmd := &cobra.Command{ Use: "password ", Short: "Create, list, and delete branch passwords", + Long: "Create, list, and delete branch passwords.\n\nThis command is only supported for Vitess databases.", PersistentPreRunE: cmdutil.CheckAuthentication(ch.Config), } diff --git a/internal/cmd/role/role.go b/internal/cmd/role/role.go index 3b9dd159..5c9f12e3 100644 --- a/internal/cmd/role/role.go +++ b/internal/cmd/role/role.go @@ -9,6 +9,7 @@ func RoleCmd(ch *cmdutil.Helper) *cobra.Command { cmd := &cobra.Command{ Use: "role", Short: "Manage database roles for a Postgres database branch", + Long: "Manage database roles for a Postgres database branch.\n\nThis command is only supported for Postgres databases.", PersistentPreRunE: cmdutil.CheckAuthentication(ch.Config), } diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 728f010a..81ac97c3 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -205,6 +205,12 @@ func runCmd(ctx context.Context, ver, commit, buildDate string, format *printer. // We don't want to show the default value rootCmd.PersistentFlags().Lookup("api-token").DefValue = "" + // Add command groups for better organization + rootCmd.AddGroup(&cobra.Group{ID: "database", Title: printer.Bold("Database management:")}) + rootCmd.AddGroup(&cobra.Group{ID: "vitess", Title: printer.Bold("Vitess-specific commands:")}) + rootCmd.AddGroup(&cobra.Group{ID: "postgres", Title: printer.Bold("Postgres-specific commands:")}) + rootCmd.AddGroup(&cobra.Group{ID: "platform", Title: printer.Bold("Platform & account management:")}) + loginCmd := auth.LoginCmd(ch) loginCmd.Hidden = true logoutCmd := auth.LogoutCmd(ch) @@ -212,28 +218,102 @@ func runCmd(ctx context.Context, ver, commit, buildDate string, format *printer. rootCmd.AddCommand(loginCmd) rootCmd.AddCommand(logoutCmd) - rootCmd.AddCommand(api.ApiCmd(ch, userAgent, headers)) - rootCmd.AddCommand(auditlog.AuditLogCmd(ch)) - rootCmd.AddCommand(auth.AuthCmd(ch)) - rootCmd.AddCommand(backup.BackupCmd(ch)) - rootCmd.AddCommand(branch.BranchCmd(ch)) - rootCmd.AddCommand(connect.ConnectCmd(ch)) - rootCmd.AddCommand(database.DatabaseCmd(ch)) - rootCmd.AddCommand(dataimports.DataImportsCmd(ch)) - rootCmd.AddCommand(deployrequest.DeployRequestCmd(ch)) - rootCmd.AddCommand(keyspace.KeyspaceCmd(ch)) - rootCmd.AddCommand(mcp.McpCmd(ch)) - rootCmd.AddCommand(org.OrgCmd(ch)) - rootCmd.AddCommand(password.PasswordCmd(ch)) - rootCmd.AddCommand(ping.PingCmd(ch)) - rootCmd.AddCommand(region.RegionCmd(ch)) - rootCmd.AddCommand(shell.ShellCmd(ch, sigc, signals...)) - rootCmd.AddCommand(signup.SignupCmd(ch)) - rootCmd.AddCommand(size.SizeCmd(ch)) - rootCmd.AddCommand(token.TokenCmd(ch)) - rootCmd.AddCommand(version.VersionCmd(ch, ver, commit, buildDate)) - rootCmd.AddCommand(workflow.WorkflowCmd(ch)) - rootCmd.AddCommand(role.RoleCmd(ch)) + + // Platform & Account Management commands + apiCmd := api.ApiCmd(ch, userAgent, headers) + apiCmd.GroupID = "platform" + rootCmd.AddCommand(apiCmd) + + auditlogCmd := auditlog.AuditLogCmd(ch) + auditlogCmd.GroupID = "platform" + rootCmd.AddCommand(auditlogCmd) + + authCmd := auth.AuthCmd(ch) + authCmd.GroupID = "platform" + rootCmd.AddCommand(authCmd) + + completionCmd := CompletionCmd() + completionCmd.GroupID = "platform" + rootCmd.AddCommand(completionCmd) + + mcpCmd := mcp.McpCmd(ch) + mcpCmd.GroupID = "platform" + rootCmd.AddCommand(mcpCmd) + + orgCmd := org.OrgCmd(ch) + orgCmd.GroupID = "platform" + rootCmd.AddCommand(orgCmd) + + pingCmd := ping.PingCmd(ch) + pingCmd.GroupID = "platform" + rootCmd.AddCommand(pingCmd) + + regionCmd := region.RegionCmd(ch) + regionCmd.GroupID = "platform" + rootCmd.AddCommand(regionCmd) + + signupCmd := signup.SignupCmd(ch) + signupCmd.GroupID = "platform" + rootCmd.AddCommand(signupCmd) + + sizeCmd := size.SizeCmd(ch) + sizeCmd.GroupID = "platform" + rootCmd.AddCommand(sizeCmd) + + tokenCmd := token.TokenCmd(ch) + tokenCmd.GroupID = "platform" + rootCmd.AddCommand(tokenCmd) + + versionCmd := version.VersionCmd(ch, ver, commit, buildDate) + versionCmd.GroupID = "platform" + rootCmd.AddCommand(versionCmd) + + // Database management commands (Both databases) + backupCmd := backup.BackupCmd(ch) + backupCmd.GroupID = "database" + rootCmd.AddCommand(backupCmd) + + branchCmd := branch.BranchCmd(ch) + branchCmd.GroupID = "database" + rootCmd.AddCommand(branchCmd) + + databaseCmd := database.DatabaseCmd(ch) + databaseCmd.GroupID = "database" + rootCmd.AddCommand(databaseCmd) + + // Vitess-specific commands + connectCmd := connect.ConnectCmd(ch) + connectCmd.GroupID = "vitess" + rootCmd.AddCommand(connectCmd) + + dataimportsCmd := dataimports.DataImportsCmd(ch) + dataimportsCmd.GroupID = "vitess" + rootCmd.AddCommand(dataimportsCmd) + + deployRequestCmd := deployrequest.DeployRequestCmd(ch) + deployRequestCmd.GroupID = "vitess" + rootCmd.AddCommand(deployRequestCmd) + + keyspaceCmd := keyspace.KeyspaceCmd(ch) + keyspaceCmd.GroupID = "vitess" + rootCmd.AddCommand(keyspaceCmd) + + passwordCmd := password.PasswordCmd(ch) + passwordCmd.GroupID = "vitess" + rootCmd.AddCommand(passwordCmd) + + shellCmd := shell.ShellCmd(ch, sigc, signals...) + shellCmd.GroupID = "vitess" + rootCmd.AddCommand(shellCmd) + + workflowCmd := workflow.WorkflowCmd(ch) + workflowCmd.GroupID = "vitess" + rootCmd.AddCommand(workflowCmd) + + // Postgres-specific commands + roleCmd := role.RoleCmd(ch) + roleCmd.GroupID = "postgres" + rootCmd.AddCommand(roleCmd) return rootCmd.ExecuteContext(ctx) } diff --git a/internal/cmd/shell/shell.go b/internal/cmd/shell/shell.go index b6227422..86a75915 100644 --- a/internal/cmd/shell/shell.go +++ b/internal/cmd/shell/shell.go @@ -35,6 +35,7 @@ func ShellCmd(ch *cmdutil.Helper, sigc chan os.Signal, signals ...os.Signal) *co // we only require database, because we deduct branch automatically Args: cmdutil.RequiredArgs("database"), Short: "Open a MySQL shell instance to a database and branch", + Long: "Open a MySQL shell instance to a database and branch.\n\nThis command is only supported for Vitess databases.", Example: `The shell subcommand opens a secure MySQL shell instance to your database. It uses the MySQL command-line client ("mysql"), which needs to be installed. diff --git a/internal/cmd/workflow/workflow.go b/internal/cmd/workflow/workflow.go index 656a1992..cec9fd14 100644 --- a/internal/cmd/workflow/workflow.go +++ b/internal/cmd/workflow/workflow.go @@ -9,6 +9,7 @@ func WorkflowCmd(ch *cmdutil.Helper) *cobra.Command { cmd := &cobra.Command{ Use: "workflow ", Short: "Manage the workflows for PlanetScale databases", + Long: "Manage the workflows for PlanetScale databases.\n\nThis command is only supported for Vitess databases.", PersistentPreRunE: cmdutil.CheckAuthentication(ch.Config), }