From ab31e741d2ba7a00ed407b748b24cf95b3fc533d Mon Sep 17 00:00:00 2001 From: Michael Dwan Date: Wed, 30 Aug 2023 16:53:49 -0600 Subject: [PATCH] Better help (#2733) * Wrap command and flag descriptions for cleaner help output * Move help command to root, group commands * Add deprecation warnings, hide long deprecated commands * Wrap command and flag descriptions for cleaner help output * Move help command to root, group commands * Add deprecation warnings, hide long deprecated commands * Add subcommands to root command output * Deprecate history * Fix wrapping for unauthenticated message * tweak wording * Fix broken docker secrets docs url --- go.mod | 1 + internal/cli/cli.go | 78 ++++++++++ internal/command/command.go | 3 +- internal/command/create/create.go | 2 +- internal/command/curl/curl.go | 2 + internal/command/destroy/destroy.go | 2 + internal/command/dnsrecords/root.go | 5 + internal/command/domains/root.go | 2 + internal/command/help/help.go | 166 --------------------- internal/command/history/history.go | 2 + internal/command/move/move.go | 2 + internal/command/open/open.go | 5 +- internal/command/resume/resume.go | 2 + internal/command/root/root.go | 220 ++++++++++++++++++---------- internal/command/suspend/suspend.go | 2 + internal/flag/flag.go | 2 +- 16 files changed, 249 insertions(+), 247 deletions(-) delete mode 100644 internal/command/help/help.go diff --git a/go.mod b/go.mod index 719577cac6..ce1814435a 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 github.com/jinzhu/copier v0.3.5 github.com/jpillora/backoff v1.0.0 + github.com/kr/text v0.2.0 github.com/loadsmart/calver-go v0.0.0-20230323142215-56cf73a68e8a github.com/logrusorgru/aurora v2.0.3+incompatible github.com/mattn/go-colorable v0.1.13 diff --git a/internal/cli/cli.go b/internal/cli/cli.go index d062b19ec3..17e497c600 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -5,15 +5,22 @@ import ( "context" "errors" "fmt" + "html/template" + "os" "runtime/debug" + "strings" "time" "github.com/AlecAivazis/survey/v2/terminal" + "github.com/MakeNowJust/heredoc/v2" + "github.com/kr/text" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/superfly/flyctl/internal/flag/flagnames" "github.com/superfly/flyctl/internal/flyerr" "github.com/superfly/flyctl/internal/metrics" "github.com/superfly/flyctl/internal/task" + "golang.org/x/term" "github.com/superfly/flyctl/iostreams" "github.com/superfly/graphql" @@ -56,6 +63,14 @@ func Run(ctx context.Context, io *iostreams.IOStreams, args ...string) int { cmd.SetArgs(args) cmd.SilenceErrors = true + // configure help templates and helpers + cobra.AddTemplateFuncs(template.FuncMap{ + "wrapFlagUsages": wrapFlagUsages, + "wrapText": wrapText, + }) + cmd.SetUsageTemplate(usageTemplate) + cmd.SetHelpTemplate(helpTemplate) + cs := io.ColorScheme() cmd, err = cmd.ExecuteContextC(ctx) @@ -132,3 +147,66 @@ func printError(io *iostreams.IOStreams, cs *iostreams.ColorScheme, cmd *cobra.C func NewRootCommand() *cobra.Command { return root.New() } + +func wrapFlagUsages(cmd *pflag.FlagSet) string { + width := helpWidth() + + return cmd.FlagUsagesWrapped(width - 1) +} + +func wrapText(s string) string { + width := helpWidth() + + return strings.TrimSpace(text.Wrap(heredoc.Doc(s), width-1)) +} + +func helpWidth() int { + fd := int(os.Stdout.Fd()) + width := 80 + + // Get the terminal width and dynamically set + termWidth, _, err := term.GetSize(fd) + if err == nil { + width = termWidth + } + + return min(120, width) +} + +// identical to the default cobra help template, but utilizes wrapText +// https://github.com/spf13/cobra/blob/fd865a44e3c48afeb6a6dbddadb8a5519173e029/command.go#L580-L582 +const helpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces | wrapText}} + +{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` + +// identical to the default cobra usage template, but utilizes wrapFlagUsages +// https://github.com/spf13/cobra/blob/fd865a44e3c48afeb6a6dbddadb8a5519173e029/command.go#L539-L568 +const usageTemplate = `Usage:{{if .Runnable}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} + {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + +Aliases: + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +Examples: +{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} + +Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} + +{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} + +Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +Flags: +{{wrapFlagUsages .LocalFlags | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} + +Global Flags: +{{wrapFlagUsages .InheritedFlags | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} + +Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} + +Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}` diff --git a/internal/command/command.go b/internal/command/command.go index 02dcfda854..f1a7a08107 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -12,7 +12,6 @@ import ( "strconv" "time" - "github.com/MakeNowJust/heredoc/v2" "github.com/logrusorgru/aurora" "github.com/spf13/cobra" "github.com/superfly/flyctl/api" @@ -39,7 +38,7 @@ func New(usage, short, long string, fn Runner, p ...preparers.Preparer) *cobra.C return &cobra.Command{ Use: usage, Short: short, - Long: heredoc.Doc(long), + Long: long, RunE: newRunE(fn, p...), } } diff --git a/internal/command/create/create.go b/internal/command/create/create.go index dd3967f941..119fc14e72 100644 --- a/internal/command/create/create.go +++ b/internal/command/create/create.go @@ -10,7 +10,7 @@ func New() (cmd *cobra.Command) { cmd = &cobra.Command{ Use: "create", Hidden: true, - Deprecated: "replaced by 'apps create'", + Deprecated: "use `fly apps create` instead", } flag.Add(cmd, diff --git a/internal/command/curl/curl.go b/internal/command/curl/curl.go index 70972e4eba..1e3dbd52ed 100644 --- a/internal/command/curl/curl.go +++ b/internal/command/curl/curl.go @@ -37,6 +37,8 @@ func New() (cmd *cobra.Command) { cmd = command.New("curl ", short, long, run, command.RequireSession, ) + cmd.Deprecated = "`fly curl` will be removed in a future release" + cmd.Hidden = true cmd.Args = cobra.ExactArgs(1) diff --git a/internal/command/destroy/destroy.go b/internal/command/destroy/destroy.go index 40957ee4dc..4adc12d9ce 100644 --- a/internal/command/destroy/destroy.go +++ b/internal/command/destroy/destroy.go @@ -20,6 +20,8 @@ from the Fly platform. destroy := command.New(usage, short, long, apps.RunDestroy, command.RequireSession) + destroy.Hidden = true + destroy.Deprecated = "use `fly apps destroy` instead" destroy.Args = cobra.ExactArgs(1) diff --git a/internal/command/dnsrecords/root.go b/internal/command/dnsrecords/root.go index 90afeb156a..ab87a87758 100644 --- a/internal/command/dnsrecords/root.go +++ b/internal/command/dnsrecords/root.go @@ -24,6 +24,8 @@ func New() *cobra.Command { long = "Manage DNS records within a domain" ) cmd := command.New("dns-records", short, long, nil) + cmd.Deprecated = "`fly dns-records` will be removed in a future release" + cmd.Hidden = true cmd.AddCommand( newDNSRecordsList(), newDNSRecordsExport(), @@ -40,6 +42,7 @@ func newDNSRecordsList() *cobra.Command { cmd := command.New("list ", short, long, runDNSRecordsList, command.RequireSession, ) + cmd.Deprecated = "`fly dns-records list` will be removed in a future release" flag.Add(cmd, flag.JSONOutput(), ) @@ -55,6 +58,7 @@ func newDNSRecordsExport() *cobra.Command { cmd := command.New("export [filename]", short, long, runDNSRecordsExport, command.RequireSession, ) + cmd.Deprecated = "`fly dns-records export` will be removed in a future release" cmd.Args = cobra.RangeArgs(1, 2) return cmd } @@ -67,6 +71,7 @@ func newDNSRecordsImport() *cobra.Command { cmd := command.New("import [filename]", short, long, runDNSRecordsImport, command.RequireSession, ) + cmd.Deprecated = "`fly dns-records import` will be removed in a future release" cmd.Args = cobra.RangeArgs(1, 2) return cmd } diff --git a/internal/command/domains/root.go b/internal/command/domains/root.go index 4080640d8d..7238aa7531 100644 --- a/internal/command/domains/root.go +++ b/internal/command/domains/root.go @@ -28,6 +28,8 @@ Notice: this feature is deprecated and no longer supported. You can still view existing domains, but registration is no longer possible.` ) cmd := command.New("domains", short, long, nil) + cmd.Deprecated = "`fly domains` will be removed in a future release" + cmd.Hidden = true cmd.AddCommand( newDomainsList(), newDomainsShow(), diff --git a/internal/command/help/help.go b/internal/command/help/help.go deleted file mode 100644 index 50580124aa..0000000000 --- a/internal/command/help/help.go +++ /dev/null @@ -1,166 +0,0 @@ -package help - -import ( - "context" - "fmt" - - "github.com/spf13/cobra" - "github.com/superfly/flyctl/client" - "github.com/superfly/flyctl/internal/command" - "github.com/superfly/flyctl/internal/flag" - - "github.com/olekukonko/tablewriter" -) - -var deprecatedCommands = map[string]bool{ - "completion": true, - "curl": true, - "dns-records": true, - "domains": true, -} - -func New(root *cobra.Command) *cobra.Command { - cmd := command.New("help", "Help on flyctl commands", "", Help(root)) - - list := command.New("commands", "All flyctl commands", "", HelpCommands(root)) - flag.Add(list, flag.Bool{ - Name: "all", - Shorthand: "a", - Default: false, - Description: "show all commands, even the ones we secretly hate.", - }) - - cmd.AddCommand(list) - - return cmd -} - -// the output of `flyctl`, run by itself with no args -func NewRootHelp() *cobra.Command { - return command.New("", "", "", func(ctx context.Context) error { - auth := ` - -It doesn't look like you're logged in. Try "flyctl auth signup" to create an account, -or "flyctl auth login" to log in to an existing account.` - - if client.FromContext(ctx).Authenticated() { - auth = "" - } - - fmt.Printf(`This is flyctl, the Fly.io command line interface.%s - -Here's a few commands to get you started: - fly launch Launch a new application - fly apps Create and manage apps - fly postgres Create and manage Postgres databases - fly mysql Create and manage PlanetScale MySQL databases - fly redis Create and manage Upstash Redis databases - fly machines Create and manage individual Fly.io machines - -If you need help along the way: - fly help Display a complete list of commands - fly help Display help for a specific command, e.g. 'fly help launch' - -Visit https://fly.io/docs for additional documentation & guides -`, auth) - return nil - }) -} - -// the output of `flyctl help`, possibly with more arguments -func Help(root *cobra.Command) func(ctx context.Context) error { - return func(ctx context.Context) error { - if cmd, _, err := root.Find(flag.Args(ctx)); err == nil && cmd != root { - return cmd.Help() - } - - commands := map[string]*cobra.Command{} - - for _, cmd := range root.Commands() { - cmd := cmd - commands[cmd.Name()] = cmd - if len(cmd.Aliases) > 0 { - for _, alias := range cmd.Aliases { - commands[alias] = cmd - } - } - } - - listCommands := func(names []string) { - for _, name := range names { - fmt.Printf(" %s %s\n", tablewriter.PadRight(name, " ", 15), commands[name].Short) - } - } - - fmt.Printf(` -Deploying apps and machines: -`) - listCommands([]string{"apps", "machine", "launch", "deploy", "destroy", "open"}) - - fmt.Printf(` -Scaling and configuring: -`) - listCommands([]string{"scale", "regions", "secrets"}) - - fmt.Printf(` -Provisioning storage: -`) - listCommands([]string{"volumes", "mysql", "postgres", "redis", "consul"}) - - fmt.Printf(` -Networking configuration: -`) - listCommands([]string{"ips", "wireguard", "proxy", "certs"}) - - fmt.Printf(` -Monitoring and managing things: -`) - listCommands([]string{"logs", "status", "dashboard", "dig", "ping", "ssh", "sftp"}) - - fmt.Printf(` -Platform overview: -`) - listCommands([]string{"platform"}) - - fmt.Printf(` -Access control: -`) - listCommands([]string{"orgs", "auth", "move"}) - - fmt.Printf(` -More help: -`) - listCommands([]string{"docs", "doctor"}) - fmt.Printf(" help commands A complete list of commands (there are a bunch more)\n") - - return nil - } -} - -// the output of `flyctl help commands`; the master list of commands -func HelpCommands(root *cobra.Command) func(ctx context.Context) error { - return func(ctx context.Context) error { - all := flag.GetBool(ctx, "all") - - fmt.Printf("flyctl commands:\n") - for _, cmd := range root.Commands() { - if cmd.Hidden { - continue - } - - name := cmd.Name() - if deprecatedCommands[name] && !all { - continue - } - - fmt.Printf(" %s %s\n", tablewriter.PadRight(name, " ", 15), cmd.Short) - } - - fmt.Printf(` -Flags: - -a, --all List all flyctl commands, even the ones we secretly hate. -`) - - return nil - } -} diff --git a/internal/command/history/history.go b/internal/command/history/history.go index e1fb3dd0a5..5f5d889e97 100644 --- a/internal/command/history/history.go +++ b/internal/command/history/history.go @@ -30,6 +30,8 @@ events and their results. command.RequireSession, command.RequireAppName, ) + cmd.Deprecated = "Use `flyctl apps releases` instead" + cmd.Hidden = true cmd.Args = cobra.NoArgs diff --git a/internal/command/move/move.go b/internal/command/move/move.go index 8c38cb9c22..d67eac67f3 100644 --- a/internal/command/move/move.go +++ b/internal/command/move/move.go @@ -20,6 +20,8 @@ organization the current user belongs to. move := command.New(usage, short, long, apps.RunMove, command.RequireSession) + move.Hidden = true + move.Deprecated = "use `fly apps move` instead" move.Args = cobra.ExactArgs(1) diff --git a/internal/command/open/open.go b/internal/command/open/open.go index c0009a2968..2c22eed804 100644 --- a/internal/command/open/open.go +++ b/internal/command/open/open.go @@ -8,5 +8,8 @@ import ( // TODO: deprecate func New() *cobra.Command { - return apps.NewOpen() + cmd := apps.NewOpen() + cmd.Deprecated = "use `fly apps open` instead" + cmd.Hidden = true + return cmd } diff --git a/internal/command/resume/resume.go b/internal/command/resume/resume.go index 7ef6521889..d487843438 100644 --- a/internal/command/resume/resume.go +++ b/internal/command/resume/resume.go @@ -21,6 +21,8 @@ the number of configured instances. resume := command.New(usage, short, long, apps.RunResume, command.RequireSession) + resume.Hidden = true + resume.Deprecated = "use `fly scale count` instead" resume.Args = cobra.ExactArgs(1) diff --git a/internal/command/root/root.go b/internal/command/root/root.go index 830b1e9ba1..a70eb670e3 100644 --- a/internal/command/root/root.go +++ b/internal/command/root/root.go @@ -2,8 +2,13 @@ package root import ( + "context" + + "github.com/kr/text" + "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" + "github.com/superfly/flyctl/client" "github.com/superfly/flyctl/flyctl" "github.com/superfly/flyctl/internal/command" "github.com/superfly/flyctl/internal/command/agent" @@ -26,7 +31,6 @@ import ( "github.com/superfly/flyctl/internal/command/doctor" "github.com/superfly/flyctl/internal/command/domains" "github.com/superfly/flyctl/internal/command/extensions" - "github.com/superfly/flyctl/internal/command/help" "github.com/superfly/flyctl/internal/command/history" "github.com/superfly/flyctl/internal/command/image" "github.com/superfly/flyctl/internal/command/info" @@ -48,7 +52,6 @@ import ( "github.com/superfly/flyctl/internal/command/redis" "github.com/superfly/flyctl/internal/command/regions" "github.com/superfly/flyctl/internal/command/releases" - "github.com/superfly/flyctl/internal/command/restart" "github.com/superfly/flyctl/internal/command/resume" "github.com/superfly/flyctl/internal/command/scale" "github.com/superfly/flyctl/internal/command/secrets" @@ -69,23 +72,11 @@ import ( // New initializes and returns a reference to a new root command. func New() *cobra.Command { const ( - long = `flyctl is a command line interface to the Fly.io platform. - -It allows users to manage authentication, application launch, -deployment, network configuration, logging and more with just the -one command. - -* Launch an app with the launch command -* Deploy an app with the deploy command -* View a deployed web application with the open command -* Check the status of an application with the status command - -To read more, use the docs command to view Fly's help on the web. - ` - short = "The Fly CLI" + long = `This is flyctl, the Fly.io command line interface.` + short = "The Fly.io command line interface" ) - root := command.New("flyctl", short, long, nil) + root := command.New("flyctl", short, long, run) root.PersistentPreRun = func(cmd *cobra.Command, args []string) { cmd.SilenceUsage = true cmd.SilenceErrors = true @@ -98,76 +89,153 @@ To read more, use the docs command to view Fly's help on the web. flyctl.InitConfig() - // what follows is a hack in order to achieve compatibility with what exists - // already. the commented out code above, is what should remain after the - // migration is complete. - - // newCommands is the set of commands which work with the new way root.AddCommand( + group(apps.New(), "deploy"), + group(machine.New(), "deploy"), version.New(), - apps.New(), - create.New(), // TODO: deprecate - destroy.New(), // TODO: deprecate - move.New(), // TODO: deprecate - suspend.New(), // TODO: deprecate - resume.New(), // TODO: deprecate - restart.New(), // TODO: deprecate - orgs.New(), - auth.New(), - open.New(), // TODO: deprecate - curl.New(), - platform.New(), - docs.New(), - releases.New(), - deploy.New(), - history.New(), - status.New(), - logs.New(), - doctor.New(), - dig.New(), - volumes.New(), + group(orgs.New(), "acl"), + group(auth.New(), "acl"), + group(platform.New(), "more_help"), + group(docs.New(), "more_help"), + group(releases.New(), "upkeep"), + group(deploy.New(), "deploy"), + group(history.New(), "upkeep"), + group(status.New(), "deploy"), + group(logs.New(), "upkeep"), + group(doctor.New(), "more_help"), + group(dig.New(), "upkeep"), + group(volumes.New(), "configuring"), agent.New(), - image.New(), - ping.New(), - proxy.New(), - machine.New(), - monitor.New(), - postgres.New(), - ips.New(), - secrets.New(), - ssh.New(), - ssh.NewSFTP(), - redis.New(), - vm.New(), - checks.New(), - launch.New(), - info.New(), + group(image.New(), "configuring"), + group(ping.New(), "upkeep"), + group(proxy.New(), "upkeep"), + group(monitor.New(), "apps_v1"), + group(postgres.New(), "dbs_and_extensions"), + group(ips.New(), "configuring"), + group(secrets.New(), "configuring"), + group(ssh.New(), "upkeep"), + group(ssh.NewSFTP(), "upkeep"), + group(redis.New(), "dbs_and_extensions"), + group(vm.New(), "apps_v1"), + group(checks.New(), "upkeep"), + group(launch.New(), "deploy"), + group(info.New(), "upkeep"), jobs.New(), turboku.New(), - services.New(), - config.New(), - scale.New(), - migrate_to_v2.New(), - tokens.New(), - extensions.New(), - consul.New(), - regions.New(), - dnsrecords.New(), - certificates.New(), - dashboard.New(), - wireguard.New(), - autoscale.New(), - domains.New(), - console.New(), + group(services.New(), "upkeep"), + group(config.New(), "configuring"), + group(scale.New(), "configuring"), + group(migrate_to_v2.New(), "apps_v1"), + group(tokens.New(), "acl"), + group(extensions.New(), "dbs_and_extensions"), + group(consul.New(), "dbs_and_extensions"), + group(regions.New(), "apps_v1"), + group(certificates.New(), "configuring"), + group(dashboard.New(), "upkeep"), + group(wireguard.New(), "upkeep"), + group(autoscale.New(), "apps_v1"), + group(console.New(), "upkeep"), settings.New(), - mysql.New(), + group(mysql.New(), "dbs_and_extensions"), + curl.New(), // TODO: deprecate + domains.New(), // TODO: deprecate + open.New(), // TODO: deprecate + create.New(), // TODO: deprecate + destroy.New(), // TODO: deprecate + move.New(), // TODO: deprecate + suspend.New(), // TODO: deprecate + resume.New(), // TODO: deprecate + dnsrecords.New(), // TODO: deprecate ) // if os.Getenv("DEV") != "" { // newCommands = append(newCommands, services.New()) // } - root.SetHelpCommand(help.New(root)) - root.RunE = help.NewRootHelp().RunE + // root.SetHelpCommand(help.New(root)) + // root.RunE = help.NewRootHelp().RunE + + root.AddGroup(&cobra.Group{ + ID: "deploy", + Title: "Deploying apps & machines", + }) + root.AddGroup(&cobra.Group{ + ID: "configuring", + Title: "Configuration & scaling", + }) + root.AddGroup(&cobra.Group{ + ID: "upkeep", + Title: "Monitoring & managing things", + }) + root.AddGroup(&cobra.Group{ + ID: "dbs_and_extensions", + Title: "Databases & extensions", + }) + root.AddGroup(&cobra.Group{ + ID: "acl", + Title: "Access control", + }) + root.AddGroup(&cobra.Group{ + ID: "more_help", + Title: "Help & troubleshooting", + }) + root.AddGroup(&cobra.Group{ + ID: "apps_v1", + Title: "Apps v1 (deprecated)", + }) + return root } + +func run(ctx context.Context) error { + cmd := command.FromContext(ctx) + + cmd.Println(cmd.Long) + cmd.Println() + cmd.Println("Usage:") + cmd.Printf(" %s\n", cmd.UseLine()) + cmd.Printf(" %s\n", "flyctl [command]") + cmd.Println() + + if !client.FromContext(ctx).Authenticated() { + msg := `It doesn't look like you're logged in. Try "fly auth signup" to create an account, or "fly auth login" to log in to an existing account.` + cmd.Println(text.Wrap(msg, 80)) + cmd.Println() + } + + cmd.Println("Here's a few commands to get you started:") + + importantCommands := [][]string{ + {"launch"}, + {"status"}, + {"deploy"}, + {"logs"}, + {"apps"}, + {"machines"}, + } + + for _, path := range importantCommands { + c, _, err := cmd.Traverse(path) + if err != nil { + panic(err) + } + cmd.Printf(" %s %s\n", tablewriter.PadRight(c.CommandPath(), " ", 16), c.Short) + } + + cmd.Println() + + cmd.Println("If you need help along the way:") + cmd.Println(" Use `fly docs` to open the Fly.io documentation, or visit https://fly.io/docs.") + cmd.Println(" Use `fly --help` for more information about a command.") + cmd.Println(" Visit https://community.fly.io to get help from the Fly.io community.") + + cmd.Println() + cmd.Println("For a full list of commands, run `fly help`.") + + return nil +} + +func group(cmd *cobra.Command, id string) *cobra.Command { + cmd.GroupID = id + return cmd +} diff --git a/internal/command/suspend/suspend.go b/internal/command/suspend/suspend.go index 44e2b7c4ec..d9c70227b6 100644 --- a/internal/command/suspend/suspend.go +++ b/internal/command/suspend/suspend.go @@ -21,6 +21,8 @@ for details on restarting it. suspend := command.New(usage, short, long, apps.RunSuspend, command.RequireSession) + suspend.Hidden = true + suspend.Deprecated = "use `fly scale count` instead" return suspend } diff --git a/internal/flag/flag.go b/internal/flag/flag.go index 7d8737526e..398a1224c9 100644 --- a/internal/flag/flag.go +++ b/internal/flag/flag.go @@ -487,7 +487,7 @@ func NoCache() Bool { func BuildSecret() StringArray { return StringArray{ Name: "build-secret", - Description: "Set of build secrets of NAME=VALUE pairs. Can be specified multiple times. See https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information", + Description: "Set of build secrets of NAME=VALUE pairs. Can be specified multiple times. See https://docs.docker.com/engine/reference/commandline/buildx_build/#secret", } }