From 4c6cae9e8d6627c25324018ccf8e67d37bad6032 Mon Sep 17 00:00:00 2001 From: Michael Dwan Date: Fri, 12 Mar 2021 12:53:20 -0700 Subject: [PATCH] iostreams --- cmd/apps.go | 19 +- cmd/auth.go | 16 +- cmd/autoscaling.go | 16 +- cmd/builds.go | 10 +- cmd/builtins.go | 12 +- cmd/certificates.go | 16 +- cmd/checks.go | 16 +- cmd/command.go | 9 +- cmd/config.go | 12 +- cmd/curl.go | 6 +- cmd/dashboard.go | 8 +- cmd/deploy.go | 9 +- cmd/destroy.go | 6 +- cmd/dns.go | 11 +- cmd/docs.go | 7 +- cmd/domains.go | 14 +- cmd/history.go | 7 +- cmd/info.go | 7 +- cmd/init.go | 5 +- cmd/ips.go | 16 +- cmd/launch.go | 6 +- cmd/list.go | 10 +- cmd/logs.go | 6 +- cmd/monitor.go | 6 +- cmd/move.go | 6 +- cmd/open.go | 6 +- cmd/orgs.go | 20 +-- cmd/platform.go | 12 +- cmd/postgres.go | 21 +-- cmd/regions.go | 17 +- cmd/releases.go | 7 +- cmd/restart.go | 6 +- cmd/resume.go | 5 +- cmd/root.go | 128 ++++++------- cmd/scale.go | 14 +- cmd/secrets.go | 13 +- cmd/ssh.go | 9 +- cmd/status.go | 11 +- cmd/suspend.go | 5 +- cmd/version.go | 27 +-- cmd/vm.go | 12 +- cmd/volumes.go | 14 +- cmd/wireguard.go | 7 +- cmdctx/cmdcontext.go | 26 +-- doc/main.go | 4 +- go.mod | 5 + go.sum | 8 + internal/client/client.go | 7 +- main.go | 56 +++++- pkg/iostreams/color.go | 168 +++++++++++++++++ pkg/iostreams/color_test.go | 145 +++++++++++++++ pkg/iostreams/iostreams.go | 349 ++++++++++++++++++++++++++++++++++++ 52 files changed, 1032 insertions(+), 326 deletions(-) create mode 100644 pkg/iostreams/color.go create mode 100644 pkg/iostreams/color_test.go create mode 100644 pkg/iostreams/iostreams.go diff --git a/cmd/apps.go b/cmd/apps.go index 13f894e857..8af1084bf9 100644 --- a/cmd/apps.go +++ b/cmd/apps.go @@ -1,16 +1,15 @@ package cmd import ( - "os" - "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/spf13/cobra" "github.com/superfly/flyctl/cmd/presenters" "github.com/superfly/flyctl/docstrings" ) -func newAppsCommand() *Command { +func newAppsCommand(client *client.Client) *Command { appsStrings := docstrings.Get("apps") @@ -24,11 +23,11 @@ func newAppsCommand() *Command { appsListStrings := docstrings.Get("apps.list") - BuildCommand(cmd, runAppsList, appsListStrings.Usage, appsListStrings.Short, appsListStrings.Long, os.Stdout, requireSession) + BuildCommand(cmd, runAppsList, appsListStrings.Usage, appsListStrings.Short, appsListStrings.Long, client, requireSession) appsCreateStrings := docstrings.Get("apps.create") - create := BuildCommand(cmd, runInit, appsCreateStrings.Usage, appsCreateStrings.Short, appsCreateStrings.Long, os.Stdout, requireSession) + create := BuildCommand(cmd, runInit, appsCreateStrings.Usage, appsCreateStrings.Short, appsCreateStrings.Long, client, requireSession) create.Args = cobra.RangeArgs(0, 1) // TODO: Move flag descriptions into the docStrings @@ -54,13 +53,13 @@ func newAppsCommand() *Command { }) appsDestroyStrings := docstrings.Get("apps.destroy") - destroy := BuildCommand(cmd, runDestroy, appsDestroyStrings.Usage, appsDestroyStrings.Short, appsDestroyStrings.Long, os.Stdout, requireSession) + destroy := BuildCommand(cmd, runDestroy, appsDestroyStrings.Usage, appsDestroyStrings.Short, appsDestroyStrings.Long, client, requireSession) destroy.Args = cobra.ExactArgs(1) // TODO: Move flag descriptions into the docStrings destroy.AddBoolFlag(BoolFlagOpts{Name: "yes", Shorthand: "y", Description: "Accept all confirmations"}) appsMoveStrings := docstrings.Get("apps.move") - move := BuildCommand(cmd, runMove, appsMoveStrings.Usage, appsMoveStrings.Short, appsMoveStrings.Long, os.Stdout, requireSession) + move := BuildCommand(cmd, runMove, appsMoveStrings.Usage, appsMoveStrings.Short, appsMoveStrings.Long, client, requireSession) move.Args = cobra.ExactArgs(1) // TODO: Move flag descriptions into the docStrings move.AddBoolFlag(BoolFlagOpts{Name: "yes", Shorthand: "y", Description: "Accept all confirmations"}) @@ -70,15 +69,15 @@ func newAppsCommand() *Command { }) appsSuspendStrings := docstrings.Get("apps.suspend") - appsSuspendCmd := BuildCommand(cmd, runSuspend, appsSuspendStrings.Usage, appsSuspendStrings.Short, appsSuspendStrings.Long, os.Stdout, requireSession, requireAppNameAsArg) + appsSuspendCmd := BuildCommand(cmd, runSuspend, appsSuspendStrings.Usage, appsSuspendStrings.Short, appsSuspendStrings.Long, client, requireSession, requireAppNameAsArg) appsSuspendCmd.Args = cobra.RangeArgs(0, 1) appsResumeStrings := docstrings.Get("apps.resume") - appsResumeCmd := BuildCommand(cmd, runResume, appsResumeStrings.Usage, appsResumeStrings.Short, appsResumeStrings.Long, os.Stdout, requireSession, requireAppNameAsArg) + appsResumeCmd := BuildCommand(cmd, runResume, appsResumeStrings.Usage, appsResumeStrings.Short, appsResumeStrings.Long, client, requireSession, requireAppNameAsArg) appsResumeCmd.Args = cobra.RangeArgs(0, 1) appsRestartStrings := docstrings.Get("apps.restart") - appsRestartCmd := BuildCommand(cmd, runRestart, appsRestartStrings.Usage, appsRestartStrings.Short, appsRestartStrings.Long, os.Stdout, requireSession, requireAppNameAsArg) + appsRestartCmd := BuildCommand(cmd, runRestart, appsRestartStrings.Usage, appsRestartStrings.Short, appsRestartStrings.Long, client, requireSession, requireAppNameAsArg) appsRestartCmd.Args = cobra.RangeArgs(0, 1) return cmd diff --git a/cmd/auth.go b/cmd/auth.go index cd1287ab35..0d5d7eb9ed 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -22,23 +22,23 @@ import ( "github.com/superfly/flyctl/terminal" ) -func newAuthCommand() *Command { +func newAuthCommand(client *client.Client) *Command { authStrings := docstrings.Get("auth") - cmd := BuildCommandKS(nil, nil, authStrings, os.Stdout) + cmd := BuildCommandKS(nil, nil, authStrings, client) authWhoamiStrings := docstrings.Get("auth.whoami") - BuildCommand(cmd, runWhoami, authWhoamiStrings.Usage, authWhoamiStrings.Short, authWhoamiStrings.Long, os.Stdout, requireSession) + BuildCommand(cmd, runWhoami, authWhoamiStrings.Usage, authWhoamiStrings.Short, authWhoamiStrings.Long, client, requireSession) authTokenStrings := docstrings.Get("auth.token") - BuildCommand(cmd, runAuthToken, authTokenStrings.Usage, authTokenStrings.Short, authTokenStrings.Long, os.Stdout, requireSession) + BuildCommand(cmd, runAuthToken, authTokenStrings.Usage, authTokenStrings.Short, authTokenStrings.Long, client, requireSession) authLoginStrings := docstrings.Get("auth.login") - login := BuildCommand(cmd, runLogin, authLoginStrings.Usage, authLoginStrings.Short, authLoginStrings.Long, os.Stdout) + login := BuildCommand(cmd, runLogin, authLoginStrings.Usage, authLoginStrings.Short, authLoginStrings.Long, client) authDockerStrings := docstrings.Get("auth.docker") - BuildCommand(cmd, runAuthDocker, authDockerStrings.Usage, authDockerStrings.Short, authDockerStrings.Long, os.Stdout) + BuildCommand(cmd, runAuthDocker, authDockerStrings.Usage, authDockerStrings.Short, authDockerStrings.Long, client) // TODO: Move flag descriptions into the docStrings login.AddBoolFlag(BoolFlagOpts{ @@ -60,10 +60,10 @@ func newAuthCommand() *Command { }) authLogoutStrings := docstrings.Get("auth.logout") - BuildCommand(cmd, runLogout, authLogoutStrings.Usage, authLogoutStrings.Short, authLogoutStrings.Long, os.Stdout, requireSession) + BuildCommand(cmd, runLogout, authLogoutStrings.Usage, authLogoutStrings.Short, authLogoutStrings.Long, client, requireSession) authSignupStrings := docstrings.Get("auth.signup") - BuildCommand(cmd, runSignup, authSignupStrings.Usage, authSignupStrings.Short, authSignupStrings.Long, os.Stdout) + BuildCommand(cmd, runSignup, authSignupStrings.Usage, authSignupStrings.Short, authSignupStrings.Long, client) return cmd } diff --git a/cmd/autoscaling.go b/cmd/autoscaling.go index fef0f8ae78..bb11f13466 100644 --- a/cmd/autoscaling.go +++ b/cmd/autoscaling.go @@ -3,11 +3,11 @@ package cmd import ( "errors" "fmt" - "os" "strconv" "strings" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/api" "github.com/superfly/flyctl/docstrings" @@ -15,30 +15,30 @@ import ( "github.com/spf13/cobra" ) -func newAutoscaleCommand() *Command { +func newAutoscaleCommand(client *client.Client) *Command { autoscaleStrings := docstrings.Get("autoscale") - cmd := BuildCommandKS(nil, nil, autoscaleStrings, os.Stdout, requireSession, requireAppName) + cmd := BuildCommandKS(nil, nil, autoscaleStrings, client, requireSession, requireAppName) //cmd.Deprecated = "use `flyctl scale` instead" disableCmdStrings := docstrings.Get("autoscale.disable") - disableCmd := BuildCommand(cmd, runDisableAutoscaling, disableCmdStrings.Usage, disableCmdStrings.Short, disableCmdStrings.Long, os.Stdout, requireSession, requireAppName) + disableCmd := BuildCommand(cmd, runDisableAutoscaling, disableCmdStrings.Usage, disableCmdStrings.Short, disableCmdStrings.Long, client, requireSession, requireAppName) disableCmd.Args = cobra.RangeArgs(0, 2) balanceCmdStrings := docstrings.Get("autoscale.balanced") - balanceCmd := BuildCommand(cmd, runBalanceScale, balanceCmdStrings.Usage, balanceCmdStrings.Short, balanceCmdStrings.Long, os.Stdout, requireSession, requireAppName) + balanceCmd := BuildCommand(cmd, runBalanceScale, balanceCmdStrings.Usage, balanceCmdStrings.Short, balanceCmdStrings.Long, client, requireSession, requireAppName) balanceCmd.Args = cobra.RangeArgs(0, 2) standardCmdStrings := docstrings.Get("autoscale.standard") - standardCmd := BuildCommand(cmd, runStandardScale, standardCmdStrings.Usage, standardCmdStrings.Short, standardCmdStrings.Long, os.Stdout, requireSession, requireAppName) + standardCmd := BuildCommand(cmd, runStandardScale, standardCmdStrings.Usage, standardCmdStrings.Short, standardCmdStrings.Long, client, requireSession, requireAppName) standardCmd.Args = cobra.RangeArgs(0, 2) setCmdStrings := docstrings.Get("autoscale.set") - setCmd := BuildCommand(cmd, runSetParamsOnly, setCmdStrings.Usage, setCmdStrings.Short, setCmdStrings.Long, os.Stdout, requireSession, requireAppName) + setCmd := BuildCommand(cmd, runSetParamsOnly, setCmdStrings.Usage, setCmdStrings.Short, setCmdStrings.Long, client, requireSession, requireAppName) setCmd.Args = cobra.RangeArgs(0, 2) showCmdStrings := docstrings.Get("autoscale.show") - BuildCommand(cmd, runAutoscalingShow, showCmdStrings.Usage, showCmdStrings.Short, showCmdStrings.Long, os.Stdout, requireSession, requireAppName) + BuildCommand(cmd, runAutoscalingShow, showCmdStrings.Usage, showCmdStrings.Short, showCmdStrings.Long, client, requireSession, requireAppName) return cmd } diff --git a/cmd/builds.go b/cmd/builds.go index caa2d7821f..f8bdfa0b76 100644 --- a/cmd/builds.go +++ b/cmd/builds.go @@ -2,26 +2,26 @@ package cmd import ( "fmt" - "os" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" "github.com/superfly/flyctl/internal/builds" + "github.com/superfly/flyctl/internal/client" "github.com/spf13/cobra" "github.com/superfly/flyctl/cmd/presenters" ) -func newBuildsCommand() *Command { +func newBuildsCommand(client *client.Client) *Command { buildsStrings := docstrings.Get("builds") - cmd := BuildCommandKS(nil, nil, buildsStrings, os.Stdout, requireSession, requireAppName) + cmd := BuildCommandKS(nil, nil, buildsStrings, client, requireSession, requireAppName) buildsListStrings := docstrings.Get("builds.list") - BuildCommandKS(cmd, runListBuilds, buildsListStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runListBuilds, buildsListStrings, client, requireSession, requireAppName) buildsLogsStrings := docstrings.Get("builds.logs") - logs := BuildCommandKS(cmd, runBuildLogs, buildsLogsStrings, os.Stdout, requireSession, requireAppName) + logs := BuildCommandKS(cmd, runBuildLogs, buildsLogsStrings, client, requireSession, requireAppName) logs.Command.Args = cobra.ExactArgs(1) return cmd diff --git a/cmd/builtins.go b/cmd/builtins.go index 55211aeb1b..548e969e44 100644 --- a/cmd/builtins.go +++ b/cmd/builtins.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "os" "sort" "strings" @@ -11,21 +10,22 @@ import ( "github.com/superfly/flyctl/builtinsupport" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/helpers" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" ) -func newBuiltinsCommand() *Command { +func newBuiltinsCommand(client *client.Client) *Command { builtinsStrings := docstrings.Get("builtins") - cmd := BuildCommandKS(nil, nil, builtinsStrings, os.Stdout) + cmd := BuildCommandKS(nil, nil, builtinsStrings, client) builtinsListStrings := docstrings.Get("builtins.list") - BuildCommandKS(cmd, runListBuiltins, builtinsListStrings, os.Stdout) + BuildCommandKS(cmd, runListBuiltins, builtinsListStrings, client) builtinShowStrings := docstrings.Get("builtins.show") - BuildCommandKS(cmd, runShowBuiltin, builtinShowStrings, os.Stdout) + BuildCommandKS(cmd, runShowBuiltin, builtinShowStrings, client) builtinShowAppStrings := docstrings.Get("builtins.show-app") - showappcmd := BuildCommandKS(cmd, runShowAppBuiltin, builtinShowAppStrings, os.Stdout, requireAppName) + showappcmd := BuildCommandKS(cmd, runShowAppBuiltin, builtinShowAppStrings, client, requireAppName) showappcmd.Args = cobra.MaximumNArgs(0) return cmd diff --git a/cmd/certificates.go b/cmd/certificates.go index b63e9139e0..dc1b7a52fb 100644 --- a/cmd/certificates.go +++ b/cmd/certificates.go @@ -3,12 +3,12 @@ package cmd import ( "fmt" "net" - "os" "strings" "github.com/dustin/go-humanize" "github.com/superfly/flyctl/api" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" @@ -17,31 +17,31 @@ import ( "golang.org/x/net/publicsuffix" ) -func newCertificatesCommand() *Command { +func newCertificatesCommand(client *client.Client) *Command { certsStrings := docstrings.Get("certs") - cmd := BuildCommandKS(nil, nil, certsStrings, os.Stdout, requireAppName, requireSession) + cmd := BuildCommandKS(nil, nil, certsStrings, client, requireAppName, requireSession) certsListStrings := docstrings.Get("certs.list") - BuildCommandKS(cmd, runCertsList, certsListStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runCertsList, certsListStrings, client, requireSession, requireAppName) certsCreateStrings := docstrings.Get("certs.add") - createCmd := BuildCommandKS(cmd, runCertAdd, certsCreateStrings, os.Stdout, requireSession, requireAppName) + createCmd := BuildCommandKS(cmd, runCertAdd, certsCreateStrings, client, requireSession, requireAppName) createCmd.Aliases = []string{"create"} createCmd.Command.Args = cobra.ExactArgs(1) certsDeleteStrings := docstrings.Get("certs.remove") - deleteCmd := BuildCommandKS(cmd, runCertDelete, certsDeleteStrings, os.Stdout, requireSession, requireAppName) + deleteCmd := BuildCommandKS(cmd, runCertDelete, certsDeleteStrings, client, requireSession, requireAppName) deleteCmd.Aliases = []string{"delete"} deleteCmd.Command.Args = cobra.ExactArgs(1) deleteCmd.AddBoolFlag(BoolFlagOpts{Name: "yes", Shorthand: "y", Description: "accept all confirmations"}) certsShowStrings := docstrings.Get("certs.show") - show := BuildCommandKS(cmd, runCertShow, certsShowStrings, os.Stdout, requireSession, requireAppName) + show := BuildCommandKS(cmd, runCertShow, certsShowStrings, client, requireSession, requireAppName) show.Command.Args = cobra.ExactArgs(1) certsCheckStrings := docstrings.Get("certs.check") - check := BuildCommandKS(cmd, runCertCheck, certsCheckStrings, os.Stdout, requireSession, requireAppName) + check := BuildCommandKS(cmd, runCertCheck, certsCheckStrings, client, requireSession, requireAppName) check.Command.Args = cobra.ExactArgs(1) return cmd diff --git a/cmd/checks.go b/cmd/checks.go index eacea82d3f..758d45afc3 100644 --- a/cmd/checks.go +++ b/cmd/checks.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "os" "github.com/AlecAivazis/survey/v2" "github.com/spf13/cobra" @@ -11,30 +10,31 @@ import ( "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" "github.com/superfly/flyctl/helpers" + "github.com/superfly/flyctl/internal/client" ) -func newChecksCommand() *Command { +func newChecksCommand(client *client.Client) *Command { checksStrings := docstrings.Get("checks") - cmd := BuildCommandKS(nil, nil, checksStrings, os.Stdout) + cmd := BuildCommandKS(nil, nil, checksStrings, client) handlersStrings := docstrings.Get("checks.handlers") - handlersCmd := BuildCommandKS(cmd, nil, handlersStrings, os.Stdout) + handlersCmd := BuildCommandKS(cmd, nil, handlersStrings, client) handlersListStrings := docstrings.Get("checks.handlers.list") - listHandlersCmd := BuildCommandKS(handlersCmd, runListChecksHandlers, handlersListStrings, os.Stdout, requireSession) + listHandlersCmd := BuildCommandKS(handlersCmd, runListChecksHandlers, handlersListStrings, client, requireSession) listHandlersCmd.Args = cobra.ExactArgs(1) handlersCreateStrings := docstrings.Get("checks.handlers.create") - createHandlersCmd := BuildCommandKS(handlersCmd, runCreateChecksHandler, handlersCreateStrings, os.Stdout, requireSession) + createHandlersCmd := BuildCommandKS(handlersCmd, runCreateChecksHandler, handlersCreateStrings, client, requireSession) createHandlersCmd.AddStringFlag(StringFlagOpts{Name: "type", Description: "The type of handler to create, can be slack or pagerduty"}) createHandlersCmd.AddStringFlag(StringFlagOpts{Name: "organization", Shorthand: "o", Description: "The organization to add the handler to"}) handlersDeleteStrings := docstrings.Get("checks.handlers.delete") - deleteHandlerCmd := BuildCommandKS(handlersCmd, runDeleteChecksHandler, handlersDeleteStrings, os.Stdout, requireSession) + deleteHandlerCmd := BuildCommandKS(handlersCmd, runDeleteChecksHandler, handlersDeleteStrings, client, requireSession) deleteHandlerCmd.Args = cobra.ExactArgs(2) checksListStrings := docstrings.Get("checks.list") - listChecksCmd := BuildCommandKS(cmd, runAppCheckList, checksListStrings, os.Stdout, requireSession, requireAppName) + listChecksCmd := BuildCommandKS(cmd, runAppCheckList, checksListStrings, client, requireSession, requireAppName) listChecksCmd.AddStringFlag(StringFlagOpts{Name: "check-name", Description: "Filter checks by name"}) return cmd diff --git a/cmd/command.go b/cmd/command.go index 58c740898d..1a405d20a3 100644 --- a/cmd/command.go +++ b/cmd/command.go @@ -4,7 +4,6 @@ import ( "bufio" "context" "fmt" - "io" "os" "os/signal" "path" @@ -162,12 +161,12 @@ type Option func(*Command) Initializer type InitializerFn func(*cmdctx.CmdContext) error // BuildCommandKS - A wrapper for BuildCommand which takes the docs.KeyStrings bundle instead of the coder having to manually unwrap it -func BuildCommandKS(parent *Command, fn RunFn, keystrings docstrings.KeyStrings, out io.Writer, options ...Option) *Command { - return BuildCommand(parent, fn, keystrings.Usage, keystrings.Short, keystrings.Long, out, options...) +func BuildCommandKS(parent *Command, fn RunFn, keystrings docstrings.KeyStrings, client *client.Client, options ...Option) *Command { + return BuildCommand(parent, fn, keystrings.Usage, keystrings.Short, keystrings.Long, client, options...) } // BuildCommand - builds a functioning Command using all the initializers -func BuildCommand(parent *Command, fn RunFn, usageText string, shortHelpText string, longHelpText string, out io.Writer, options ...Option) *Command { +func BuildCommand(parent *Command, fn RunFn, usageText string, shortHelpText string, longHelpText string, client *client.Client, options ...Option) *Command { flycmd := &Command{ Command: &cobra.Command{ Use: usageText, @@ -190,7 +189,7 @@ func BuildCommand(parent *Command, fn RunFn, usageText string, shortHelpText str if fn != nil { flycmd.Run = func(cmd *cobra.Command, args []string) { - ctx, err := cmdctx.NewCmdContext(flyctlClient, namespace(cmd), out, args) + ctx, err := cmdctx.NewCmdContext(client, namespace(cmd), args) checkErr(err) for _, init := range initializers { diff --git a/cmd/config.go b/cmd/config.go index c1d921caee..f9df9e4d17 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -3,9 +3,9 @@ package cmd import ( "errors" "fmt" - "os" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" @@ -15,20 +15,20 @@ import ( "github.com/superfly/flyctl/helpers" ) -func newConfigCommand() *Command { +func newConfigCommand(client *client.Client) *Command { configStrings := docstrings.Get("config") - cmd := BuildCommandKS(nil, nil, configStrings, os.Stdout, requireSession, requireAppName) + cmd := BuildCommandKS(nil, nil, configStrings, client, requireSession, requireAppName) configDisplayStrings := docstrings.Get("config.display") - BuildCommandKS(cmd, runDisplayConfig, configDisplayStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runDisplayConfig, configDisplayStrings, client, requireSession, requireAppName) configSaveStrings := docstrings.Get("config.save") - BuildCommandKS(cmd, runSaveConfig, configSaveStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runSaveConfig, configSaveStrings, client, requireSession, requireAppName) configValidateStrings := docstrings.Get("config.validate") - BuildCommandKS(cmd, runValidateConfig, configValidateStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runValidateConfig, configValidateStrings, client, requireSession, requireAppName) return cmd } diff --git a/cmd/curl.go b/cmd/curl.go index e1531851bf..8e02981a4a 100644 --- a/cmd/curl.go +++ b/cmd/curl.go @@ -7,11 +7,11 @@ import ( "fmt" "io/ioutil" "net/http" - "os" "strconv" "sync" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/dustin/go-humanize" "github.com/logrusorgru/aurora" @@ -20,9 +20,9 @@ import ( "github.com/spf13/cobra" ) -func newCurlCommand() *Command { +func newCurlCommand(client *client.Client) *Command { curlStrings := docstrings.Get("curl") - cmd := BuildCommandKS(nil, runCurl, curlStrings, os.Stdout, requireSession) + cmd := BuildCommandKS(nil, runCurl, curlStrings, client, requireSession) cmd.Args = cobra.ExactArgs(1) cmd.Hidden = true return cmd diff --git a/cmd/dashboard.go b/cmd/dashboard.go index d553aa5a61..b033b30c29 100644 --- a/cmd/dashboard.go +++ b/cmd/dashboard.go @@ -2,22 +2,22 @@ package cmd import ( "fmt" - "os" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" "github.com/skratchdot/open-golang/open" ) -func newDashboardCommand() *Command { +func newDashboardCommand(client *client.Client) *Command { dashboardStrings := docstrings.Get("dashboard") - dashboardCmd := BuildCommandKS(nil, runDashboard, dashboardStrings, os.Stdout, requireSession, requireAppName) + dashboardCmd := BuildCommandKS(nil, runDashboard, dashboardStrings, client, requireSession, requireAppName) dashboardCmd.Aliases = []string{"dash"} dashMetricsStrings := docstrings.Get("dashboard.metrics") - BuildCommandKS(dashboardCmd, runDashboardMetrics, dashMetricsStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(dashboardCmd, runDashboardMetrics, dashMetricsStrings, client, requireSession, requireAppName) return dashboardCmd } diff --git a/cmd/deploy.go b/cmd/deploy.go index 8e4548c58f..6575e71219 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -4,13 +4,11 @@ import ( "context" "errors" "fmt" - "os" "strings" "sync" "github.com/dustin/go-humanize" "github.com/logrusorgru/aurora" - "github.com/mattn/go-isatty" "github.com/morikuni/aec" "github.com/spf13/cobra" "github.com/superfly/flyctl/api" @@ -18,13 +16,14 @@ import ( "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docker" "github.com/superfly/flyctl/docstrings" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/internal/deployment" "github.com/superfly/flyctl/terminal" ) -func newDeployCommand() *Command { +func newDeployCommand(client *client.Client) *Command { deployStrings := docstrings.Get("deploy") - cmd := BuildCommandKS(nil, runDeploy, deployStrings, os.Stdout, workingDirectoryFromArg(0), requireSession, requireAppName) + cmd := BuildCommandKS(nil, runDeploy, deployStrings, client, workingDirectoryFromArg(0), requireSession, requireAppName) cmd.AddStringFlag(StringFlagOpts{ Name: "image", Shorthand: "i", @@ -278,7 +277,7 @@ func watchDeployment(ctx context.Context, cmdCtx *cmdctx.CmdContext) error { cmdCtx.Status("deploy", cmdctx.STITLE, "Monitoring Deployment") cmdCtx.Status("deploy", cmdctx.SDETAIL, "You can detach the terminal anytime without stopping the deployment") - interactive := isatty.IsTerminal(os.Stdout.Fd()) + interactive := cmdCtx.IO.IsInteractive() endmessage := "" diff --git a/cmd/destroy.go b/cmd/destroy.go index d52a902f3b..d2cf52f0f9 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -2,9 +2,9 @@ package cmd import ( "fmt" - "os" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/AlecAivazis/survey/v2" "github.com/logrusorgru/aurora" @@ -14,11 +14,11 @@ import ( //TODO: Move all output to status styled begin/done updates -func newDestroyCommand() *Command { +func newDestroyCommand(client *client.Client) *Command { destroyStrings := docstrings.Get("destroy") - destroy := BuildCommand(nil, runDestroy, destroyStrings.Usage, destroyStrings.Short, destroyStrings.Long, os.Stdout, requireSession) + destroy := BuildCommand(nil, runDestroy, destroyStrings.Usage, destroyStrings.Short, destroyStrings.Long, client, requireSession) destroy.Args = cobra.ExactArgs(1) diff --git a/cmd/dns.go b/cmd/dns.go index 693a1dd785..df03169d1f 100644 --- a/cmd/dns.go +++ b/cmd/dns.go @@ -10,18 +10,19 @@ import ( "github.com/spf13/cobra" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" + "github.com/superfly/flyctl/internal/client" ) -func newDNSCommand() *Command { +func newDNSCommand(client *client.Client) *Command { dnsStrings := docstrings.Get("dns-records") - cmd := BuildCommandKS(nil, nil, dnsStrings, os.Stdout, requireSession) + cmd := BuildCommandKS(nil, nil, dnsStrings, client, requireSession) listStrings := docstrings.Get("dns-records.list") - listCmd := BuildCommandKS(cmd, runRecordsList, listStrings, os.Stdout, requireSession) + listCmd := BuildCommandKS(cmd, runRecordsList, listStrings, client, requireSession) listCmd.Args = cobra.ExactArgs(1) recordsExportStrings := docstrings.Get("dns-records.export") - recordsExportCmd := BuildCommandKS(cmd, runRecordsExport, recordsExportStrings, os.Stdout, requireSession) + recordsExportCmd := BuildCommandKS(cmd, runRecordsExport, recordsExportStrings, client, requireSession) recordsExportCmd.Args = cobra.MinimumNArgs(1) recordsExportCmd.Args = cobra.MaximumNArgs(3) recordsExportCmd.AddBoolFlag(BoolFlagOpts{ @@ -29,7 +30,7 @@ func newDNSCommand() *Command { }) recordsImportStrings := docstrings.Get("dns-records.import") - recordsImportCmd := BuildCommandKS(cmd, runRecordsImport, recordsImportStrings, os.Stdout, requireSession) + recordsImportCmd := BuildCommandKS(cmd, runRecordsImport, recordsImportStrings, client, requireSession) recordsImportCmd.Args = cobra.MaximumNArgs(3) recordsImportCmd.Args = cobra.MinimumNArgs(1) diff --git a/cmd/docs.go b/cmd/docs.go index 4a9adb2ead..4593d4b6b9 100644 --- a/cmd/docs.go +++ b/cmd/docs.go @@ -2,17 +2,18 @@ package cmd import ( "fmt" + "github.com/superfly/flyctl/cmdctx" - "os" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" "github.com/skratchdot/open-golang/open" ) -func newDocsCommand() *Command { +func newDocsCommand(client *client.Client) *Command { docsStrings := docstrings.Get("docs") - return BuildCommand(nil, runLaunchDocs, docsStrings.Usage, docsStrings.Short, docsStrings.Long, os.Stdout) + return BuildCommand(nil, runLaunchDocs, docsStrings.Usage, docsStrings.Short, docsStrings.Long, client) } const docsURL = "https://fly.io/docs/" diff --git a/cmd/domains.go b/cmd/domains.go index be99e3ecda..efb7a1b4e9 100644 --- a/cmd/domains.go +++ b/cmd/domains.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "os" "strings" "github.com/AlecAivazis/survey/v2" @@ -14,23 +13,24 @@ import ( "github.com/superfly/flyctl/cmd/presenters" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" + "github.com/superfly/flyctl/internal/client" ) -func newDomainsCommand() *Command { +func newDomainsCommand(client *client.Client) *Command { domainsStrings := docstrings.Get("domains") - cmd := BuildCommandKS(nil, nil, domainsStrings, os.Stdout, requireSession) + cmd := BuildCommandKS(nil, nil, domainsStrings, client, requireSession) listStrings := docstrings.Get("domains.list") - listCmd := BuildCommandKS(cmd, runDomainsList, listStrings, os.Stdout, requireSession) + listCmd := BuildCommandKS(cmd, runDomainsList, listStrings, client, requireSession) listCmd.Args = cobra.MaximumNArgs(1) - showCmd := BuildCommandKS(cmd, runDomainsShow, docstrings.Get("domains.show"), os.Stdout, requireSession) + showCmd := BuildCommandKS(cmd, runDomainsShow, docstrings.Get("domains.show"), client, requireSession) showCmd.Args = cobra.ExactArgs(1) - addCmd := BuildCommandKS(cmd, runDomainsCreate, docstrings.Get("domains.add"), os.Stdout, requireSession) + addCmd := BuildCommandKS(cmd, runDomainsCreate, docstrings.Get("domains.add"), client, requireSession) addCmd.Args = cobra.MaximumNArgs(2) - registerCmd := BuildCommandKS(cmd, runDomainsRegister, docstrings.Get("domains.register"), os.Stdout, requireSession) + registerCmd := BuildCommandKS(cmd, runDomainsRegister, docstrings.Get("domains.register"), client, requireSession) registerCmd.Args = cobra.MaximumNArgs(2) return cmd diff --git a/cmd/history.go b/cmd/history.go index 494872692c..a4b0efb37e 100644 --- a/cmd/history.go +++ b/cmd/history.go @@ -1,18 +1,17 @@ package cmd import ( - "os" - "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" "github.com/superfly/flyctl/cmd/presenters" ) -func newHistoryCommand() *Command { +func newHistoryCommand(client *client.Client) *Command { historyStrings := docstrings.Get("history") - return BuildCommand(nil, runHistory, historyStrings.Usage, historyStrings.Short, historyStrings.Long, os.Stdout, requireSession, requireAppName) + return BuildCommand(nil, runHistory, historyStrings.Usage, historyStrings.Short, historyStrings.Long, client, requireSession, requireAppName) } func runHistory(commandContext *cmdctx.CmdContext) error { diff --git a/cmd/info.go b/cmd/info.go index 6e2d4a64e2..88f3b0f61d 100644 --- a/cmd/info.go +++ b/cmd/info.go @@ -1,19 +1,18 @@ package cmd import ( - "os" - "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/flyname" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" "github.com/superfly/flyctl/cmd/presenters" ) -func newInfoCommand() *Command { +func newInfoCommand(client *client.Client) *Command { ks := docstrings.Get("info") - appInfoCmd := BuildCommandKS(nil, runInfo, ks, os.Stdout, requireSession, requireAppName) + appInfoCmd := BuildCommandKS(nil, runInfo, ks, client, requireSession, requireAppName) appInfoCmd.AddBoolFlag(BoolFlagOpts{ Name: "name", diff --git a/cmd/init.go b/cmd/init.go index bb81928833..196a2b6a73 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/AlecAivazis/survey/v2" "github.com/spf13/cobra" @@ -17,11 +18,11 @@ import ( //TODO: Move all output to status styled begin/done updates -func newInitCommand() *Command { +func newInitCommand(client *client.Client) *Command { initStrings := docstrings.Get("init") - cmd := BuildCommandKS(nil, runInit, initStrings, os.Stdout, requireSession) + cmd := BuildCommandKS(nil, runInit, initStrings, client, requireSession) cmd.Args = cobra.RangeArgs(0, 1) diff --git a/cmd/ips.go b/cmd/ips.go index a44a10c5e7..00ca8c6b40 100644 --- a/cmd/ips.go +++ b/cmd/ips.go @@ -3,10 +3,10 @@ package cmd import ( "fmt" "net" - "os" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/helpers" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" @@ -15,25 +15,25 @@ import ( "github.com/superfly/flyctl/cmd/presenters" ) -func newIPAddressesCommand() *Command { +func newIPAddressesCommand(client *client.Client) *Command { ipsStrings := docstrings.Get("ips") - cmd := BuildCommandKS(nil, nil, ipsStrings, os.Stdout, requireSession, requireAppName) + cmd := BuildCommandKS(nil, nil, ipsStrings, client, requireSession, requireAppName) ipsListStrings := docstrings.Get("ips.list") - BuildCommandKS(cmd, runIPAddressesList, ipsListStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runIPAddressesList, ipsListStrings, client, requireSession, requireAppName) ipsPrivateListStrings := docstrings.Get("ips.private") - BuildCommandKS(cmd, runPrivateIPAddressesList, ipsPrivateListStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runPrivateIPAddressesList, ipsPrivateListStrings, client, requireSession, requireAppName) ipsAllocateV4Strings := docstrings.Get("ips.allocate-v4") - BuildCommandKS(cmd, runAllocateIPAddressV4, ipsAllocateV4Strings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runAllocateIPAddressV4, ipsAllocateV4Strings, client, requireSession, requireAppName) ipsAllocateV6Strings := docstrings.Get("ips.allocate-v6") - BuildCommandKS(cmd, runAllocateIPAddressV6, ipsAllocateV6Strings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runAllocateIPAddressV6, ipsAllocateV6Strings, client, requireSession, requireAppName) ipsReleaseStrings := docstrings.Get("ips.release") - release := BuildCommandKS(cmd, runReleaseIPAddress, ipsReleaseStrings, os.Stdout, requireSession, requireAppName) + release := BuildCommandKS(cmd, runReleaseIPAddress, ipsReleaseStrings, client, requireSession, requireAppName) release.Args = cobra.ExactArgs(1) return cmd diff --git a/cmd/launch.go b/cmd/launch.go index 55c8b964ba..ddd4a0c8b8 100644 --- a/cmd/launch.go +++ b/cmd/launch.go @@ -2,21 +2,21 @@ package cmd import ( "fmt" - "os" "path/filepath" "strings" "github.com/spf13/cobra" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/flyctl" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/internal/sourcecode" "github.com/superfly/flyctl/docstrings" ) -func newLaunchCommand() *Command { +func newLaunchCommand(client *client.Client) *Command { launchStrings := docstrings.Get("launch") - launchCmd := BuildCommandKS(nil, runLaunch, launchStrings, os.Stdout, requireSession) + launchCmd := BuildCommandKS(nil, runLaunch, launchStrings, client, requireSession) launchCmd.Args = cobra.NoArgs launchCmd.AddStringFlag(StringFlagOpts{Name: "path", Description: `path to app code and where a fly.toml file will be saved.`, Default: "."}) launchCmd.AddStringFlag(StringFlagOpts{Name: "org", Description: `the organization that will own the app`}) diff --git a/cmd/list.go b/cmd/list.go index 9b34722186..cd5dea34a2 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -1,7 +1,6 @@ package cmd import ( - "os" "sort" "strings" "time" @@ -9,18 +8,19 @@ import ( "github.com/dustin/go-humanize" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/helpers" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" ) -func newListCommand() *Command { +func newListCommand(client *client.Client) *Command { ks := docstrings.Get("list") - listCmd := BuildCommandKS(nil, nil, ks, os.Stdout, requireSession) + listCmd := BuildCommandKS(nil, nil, ks, client, requireSession) listCmd.Aliases = []string{"ls"} laks := docstrings.Get("list.apps") - listAppsCmd := BuildCommandKS(listCmd, runListApps, laks, os.Stdout, requireSession) + listAppsCmd := BuildCommandKS(listCmd, runListApps, laks, client, requireSession) listAppsCmd.AddStringFlag(StringFlagOpts{ Name: "org", @@ -48,7 +48,7 @@ func newListCommand() *Command { }) loks := docstrings.Get("list.orgs") - BuildCommandKS(listCmd, runListOrgs, loks, os.Stdout, requireSession) + BuildCommandKS(listCmd, runListOrgs, loks, client, requireSession) return listCmd } diff --git a/cmd/logs.go b/cmd/logs.go index c7686ddcd9..e68268e00e 100644 --- a/cmd/logs.go +++ b/cmd/logs.go @@ -2,11 +2,11 @@ package cmd import ( "math" - "os" "time" "github.com/superfly/flyctl/cmd/presenters" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" @@ -14,9 +14,9 @@ import ( "github.com/superfly/flyctl/terminal" ) -func newLogsCommand() *Command { +func newLogsCommand(client *client.Client) *Command { logsStrings := docstrings.Get("logs") - cmd := BuildCommandKS(nil, runLogs, logsStrings, os.Stdout, requireSession, requireAppName) + cmd := BuildCommandKS(nil, runLogs, logsStrings, client, requireSession, requireAppName) // TODO: Move flag descriptions into the docStrings cmd.AddStringFlag(StringFlagOpts{ diff --git a/cmd/monitor.go b/cmd/monitor.go index 584807fe64..5a770d3e38 100644 --- a/cmd/monitor.go +++ b/cmd/monitor.go @@ -3,7 +3,6 @@ package cmd import ( "context" "fmt" - "os" "sync" "github.com/superfly/flyctl/cmdctx" @@ -11,12 +10,13 @@ import ( "github.com/superfly/flyctl/api" "github.com/superfly/flyctl/cmd/presenters" "github.com/superfly/flyctl/docstrings" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/internal/deployment" ) -func newMonitorCommand() *Command { +func newMonitorCommand(client *client.Client) *Command { ks := docstrings.Get("monitor") - return BuildCommandKS(nil, runMonitor, ks, os.Stdout, requireSession, requireAppName) + return BuildCommandKS(nil, runMonitor, ks, client, requireSession, requireAppName) } func runMonitor(commandContext *cmdctx.CmdContext) error { diff --git a/cmd/move.go b/cmd/move.go index 95b12524d0..9ec22f766d 100644 --- a/cmd/move.go +++ b/cmd/move.go @@ -2,9 +2,9 @@ package cmd import ( "fmt" - "os" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/AlecAivazis/survey/v2" "github.com/logrusorgru/aurora" @@ -15,10 +15,10 @@ import ( //TODO: Move all output to status styled begin/done updates -func newMoveCommand() *Command { +func newMoveCommand(client *client.Client) *Command { moveStrings := docstrings.Get("move") - moveCmd := BuildCommandKS(nil, runMove, moveStrings, os.Stdout, requireSession) + moveCmd := BuildCommandKS(nil, runMove, moveStrings, client, requireSession) moveCmd.Args = cobra.ExactArgs(1) // TODO: Move flag descriptions into the docStrings moveCmd.AddBoolFlag(BoolFlagOpts{Name: "yes", Shorthand: "y", Description: "Accept all confirmations"}) diff --git a/cmd/open.go b/cmd/open.go index 88a697f27a..94c9e4d6cb 100644 --- a/cmd/open.go +++ b/cmd/open.go @@ -2,19 +2,19 @@ package cmd import ( "fmt" - "os" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/flyname" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" "github.com/skratchdot/open-golang/open" ) -func newOpenCommand() *Command { +func newOpenCommand(client *client.Client) *Command { ks := docstrings.Get("open") - opencommand := BuildCommandKS(nil, runOpen, ks, os.Stdout, requireSession, requireAppName) + opencommand := BuildCommandKS(nil, runOpen, ks, client, requireSession, requireAppName) return opencommand } diff --git a/cmd/orgs.go b/cmd/orgs.go index 6259ded1b7..16c4d258bc 100644 --- a/cmd/orgs.go +++ b/cmd/orgs.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "os" "github.com/AlecAivazis/survey/v2" "github.com/olekukonko/tablewriter" @@ -10,37 +9,38 @@ import ( "github.com/superfly/flyctl/api" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" + "github.com/superfly/flyctl/internal/client" ) -func newOrgsCommand() *Command { +func newOrgsCommand(client *client.Client) *Command { orgsStrings := docstrings.Get("orgs") - orgscmd := BuildCommandKS(nil, nil, orgsStrings, os.Stdout, requireSession) + orgscmd := BuildCommandKS(nil, nil, orgsStrings, client, requireSession) orgsListStrings := docstrings.Get("orgs.list") - BuildCommandKS(orgscmd, runOrgsList, orgsListStrings, os.Stdout, requireSession) + BuildCommandKS(orgscmd, runOrgsList, orgsListStrings, client, requireSession) orgsShowStrings := docstrings.Get("orgs.show") - orgsShowCommand := BuildCommandKS(orgscmd, runOrgsShow, orgsShowStrings, os.Stdout, requireSession) + orgsShowCommand := BuildCommandKS(orgscmd, runOrgsShow, orgsShowStrings, client, requireSession) orgsShowCommand.Args = cobra.ExactArgs(1) orgsInviteStrings := docstrings.Get("orgs.invite") - orgsInviteCommand := BuildCommandKS(orgscmd, runOrgsInvite, orgsInviteStrings, os.Stdout, requireSession) + orgsInviteCommand := BuildCommandKS(orgscmd, runOrgsInvite, orgsInviteStrings, client, requireSession) orgsInviteCommand.Args = cobra.MaximumNArgs(2) orgsRevokeStrings := docstrings.Get("orgs.revoke") - orgsRevokeCommand := BuildCommandKS(orgscmd, runOrgsRevoke, orgsRevokeStrings, os.Stdout, requireSession) + orgsRevokeCommand := BuildCommandKS(orgscmd, runOrgsRevoke, orgsRevokeStrings, client, requireSession) orgsRevokeCommand.Args = cobra.MaximumNArgs(2) orgsRemoveStrings := docstrings.Get("orgs.remove") - orgsRemoveCommand := BuildCommandKS(orgscmd, runOrgsRemove, orgsRemoveStrings, os.Stdout, requireSession) + orgsRemoveCommand := BuildCommandKS(orgscmd, runOrgsRemove, orgsRemoveStrings, client, requireSession) orgsRemoveCommand.Args = cobra.MaximumNArgs(2) orgsCreateStrings := docstrings.Get("orgs.create") - orgsCreateCommand := BuildCommandKS(orgscmd, runOrgsCreate, orgsCreateStrings, os.Stdout, requireSession) + orgsCreateCommand := BuildCommandKS(orgscmd, runOrgsCreate, orgsCreateStrings, client, requireSession) orgsCreateCommand.Args = cobra.RangeArgs(0, 1) orgsDeleteStrings := docstrings.Get("orgs.delete") - orgsDeleteCommand := BuildCommandKS(orgscmd, runOrgsDelete, orgsDeleteStrings, os.Stdout, requireSession) + orgsDeleteCommand := BuildCommandKS(orgscmd, runOrgsDelete, orgsDeleteStrings, client, requireSession) orgsDeleteCommand.Args = cobra.ExactArgs(1) return orgscmd diff --git a/cmd/platform.go b/cmd/platform.go index 10a004beb3..818a117365 100644 --- a/cmd/platform.go +++ b/cmd/platform.go @@ -2,29 +2,29 @@ package cmd import ( "fmt" - "os" "github.com/skratchdot/open-golang/open" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" "github.com/superfly/flyctl/cmd/presenters" ) -func newPlatformCommand() *Command { +func newPlatformCommand(client *client.Client) *Command { platformStrings := docstrings.Get("platform") - cmd := BuildCommandKS(nil, nil, platformStrings, os.Stdout, requireAppName) + cmd := BuildCommandKS(nil, nil, platformStrings, client, requireAppName) regionsStrings := docstrings.Get("platform.regions") - BuildCommandKS(cmd, runPlatformRegions, regionsStrings, os.Stdout, requireSession) + BuildCommandKS(cmd, runPlatformRegions, regionsStrings, client, requireSession) vmSizesStrings := docstrings.Get("platform.vmsizes") - BuildCommandKS(cmd, runPlatformVMSizes, vmSizesStrings, os.Stdout, requireSession) + BuildCommandKS(cmd, runPlatformVMSizes, vmSizesStrings, client, requireSession) statusStrings := docstrings.Get("platform.status") - BuildCommandKS(cmd, runPlatformStatus, statusStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runPlatformStatus, statusStrings, client, requireSession, requireAppName) return cmd } diff --git a/cmd/postgres.go b/cmd/postgres.go index f43a93a31e..75530847b6 100644 --- a/cmd/postgres.go +++ b/cmd/postgres.go @@ -15,19 +15,20 @@ import ( "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" "github.com/superfly/flyctl/helpers" + "github.com/superfly/flyctl/internal/client" ) -func newPostgresCommand() *Command { +func newPostgresCommand(client *client.Client) *Command { domainsStrings := docstrings.Get("postgres") - cmd := BuildCommandKS(nil, nil, domainsStrings, os.Stdout, requireSession) + cmd := BuildCommandKS(nil, nil, domainsStrings, client, requireSession) cmd.Aliases = []string{"pg"} listStrings := docstrings.Get("postgres.list") - listCmd := BuildCommandKS(cmd, runPostgresList, listStrings, os.Stdout, requireSession) + listCmd := BuildCommandKS(cmd, runPostgresList, listStrings, client, requireSession) listCmd.Args = cobra.MaximumNArgs(1) createStrings := docstrings.Get("postgres.create") - createCmd := BuildCommandKS(cmd, runCreatePostgresCluster, createStrings, os.Stdout, requireSession) + createCmd := BuildCommandKS(cmd, runCreatePostgresCluster, createStrings, client, requireSession) createCmd.AddStringFlag(StringFlagOpts{Name: "organization", Description: "the organization that will own the app"}) createCmd.AddStringFlag(StringFlagOpts{Name: "name", Description: "the name of the new app"}) createCmd.AddStringFlag(StringFlagOpts{Name: "region", Description: "the region to launch the new app in"}) @@ -36,27 +37,27 @@ func newPostgresCommand() *Command { createCmd.AddStringFlag(StringFlagOpts{Name: "vm-size", Description: "the size of the VM"}) attachStrngs := docstrings.Get("postgres.attach") - attachCmd := BuildCommandKS(cmd, runAttachPostgresCluster, attachStrngs, os.Stdout, requireSession, requireAppName) + attachCmd := BuildCommandKS(cmd, runAttachPostgresCluster, attachStrngs, client, requireSession, requireAppName) attachCmd.AddStringFlag(StringFlagOpts{Name: "postgres-app", Description: "the postgres cluster to attach to the app"}) attachCmd.AddStringFlag(StringFlagOpts{Name: "database-name", Description: "database to use, defaults to a new database with the same name as the app"}) attachCmd.AddStringFlag(StringFlagOpts{Name: "variable-name", Description: "the env variable name that will be added to the app. Defaults to DATABASE_URL"}) detachStrngs := docstrings.Get("postgres.detach") - detachCmd := BuildCommandKS(cmd, runDetachPostgresCluster, detachStrngs, os.Stdout, requireSession, requireAppName) + detachCmd := BuildCommandKS(cmd, runDetachPostgresCluster, detachStrngs, client, requireSession, requireAppName) detachCmd.AddStringFlag(StringFlagOpts{Name: "postgres-app", Description: "the postgres cluster to detach from the app"}) dbStrings := docstrings.Get("postgres.db") - dbCmd := BuildCommandKS(cmd, nil, dbStrings, os.Stdout, requireSession) + dbCmd := BuildCommandKS(cmd, nil, dbStrings, client, requireSession) listDBStrings := docstrings.Get("postgres.db.list") - listDBCmd := BuildCommandKS(dbCmd, runListPostgresDatabases, listDBStrings, os.Stdout, requireSession, requireAppNameAsArg) + listDBCmd := BuildCommandKS(dbCmd, runListPostgresDatabases, listDBStrings, client, requireSession, requireAppNameAsArg) listDBCmd.Args = cobra.ExactArgs(1) usersStrings := docstrings.Get("postgres.users") - usersCmd := BuildCommandKS(cmd, nil, usersStrings, os.Stdout, requireSession) + usersCmd := BuildCommandKS(cmd, nil, usersStrings, client, requireSession) usersListStrings := docstrings.Get("postgres.users.list") - usersListCmd := BuildCommandKS(usersCmd, runListPostgresUsers, usersListStrings, os.Stdout, requireSession, requireAppNameAsArg) + usersListCmd := BuildCommandKS(usersCmd, runListPostgresUsers, usersListStrings, client, requireSession, requireAppNameAsArg) usersListCmd.Args = cobra.ExactArgs(1) return cmd diff --git a/cmd/regions.go b/cmd/regions.go index 7f605dab42..31ad9f7bc3 100644 --- a/cmd/regions.go +++ b/cmd/regions.go @@ -1,9 +1,8 @@ package cmd import ( - "os" - "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/api" "github.com/superfly/flyctl/docstrings" @@ -11,29 +10,29 @@ import ( "github.com/spf13/cobra" ) -func newRegionsCommand() *Command { +func newRegionsCommand(client *client.Client) *Command { regionsStrings := docstrings.Get("regions") - cmd := BuildCommandKS(nil, nil, regionsStrings, os.Stdout, requireAppName, requireSession) + cmd := BuildCommandKS(nil, nil, regionsStrings, client, requireAppName, requireSession) addStrings := docstrings.Get("regions.add") - addCmd := BuildCommandKS(cmd, runRegionsAdd, addStrings, os.Stdout, requireSession, requireAppName) + addCmd := BuildCommandKS(cmd, runRegionsAdd, addStrings, client, requireSession, requireAppName) addCmd.Args = cobra.MinimumNArgs(1) removeStrings := docstrings.Get("regions.remove") - removeCmd := BuildCommandKS(cmd, runRegionsRemove, removeStrings, os.Stdout, requireSession, requireAppName) + removeCmd := BuildCommandKS(cmd, runRegionsRemove, removeStrings, client, requireSession, requireAppName) removeCmd.Args = cobra.MinimumNArgs(1) setStrings := docstrings.Get("regions.set") - setCmd := BuildCommandKS(cmd, runRegionsSet, setStrings, os.Stdout, requireSession, requireAppName) + setCmd := BuildCommandKS(cmd, runRegionsSet, setStrings, client, requireSession, requireAppName) setCmd.Args = cobra.MinimumNArgs(1) setBackupStrings := docstrings.Get("regions.backup") - setBackupCmd := BuildCommand(cmd, runBackupRegionsSet, setBackupStrings.Usage, setBackupStrings.Short, setBackupStrings.Long, os.Stdout, requireSession, requireAppName) + setBackupCmd := BuildCommand(cmd, runBackupRegionsSet, setBackupStrings.Usage, setBackupStrings.Short, setBackupStrings.Long, client, requireSession, requireAppName) setBackupCmd.Args = cobra.MinimumNArgs(1) listStrings := docstrings.Get("regions.list") - BuildCommand(cmd, runRegionsList, listStrings.Usage, listStrings.Short, listStrings.Long, os.Stdout, requireSession, requireAppName) + BuildCommand(cmd, runRegionsList, listStrings.Usage, listStrings.Short, listStrings.Long, client, requireSession, requireAppName) return cmd } diff --git a/cmd/releases.go b/cmd/releases.go index 87990a554e..fdb9816315 100644 --- a/cmd/releases.go +++ b/cmd/releases.go @@ -1,18 +1,17 @@ package cmd import ( - "os" - "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" "github.com/superfly/flyctl/cmd/presenters" ) -func newReleasesCommand() *Command { +func newReleasesCommand(client *client.Client) *Command { releasesStrings := docstrings.Get("releases") - cmd := BuildCommandKS(nil, runReleases, releasesStrings, os.Stdout, requireSession, requireAppName) + cmd := BuildCommandKS(nil, runReleases, releasesStrings, client, requireSession, requireAppName) return cmd } diff --git a/cmd/restart.go b/cmd/restart.go index c51205363e..2d1f84ec1e 100644 --- a/cmd/restart.go +++ b/cmd/restart.go @@ -2,9 +2,9 @@ package cmd import ( "fmt" - "os" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/spf13/cobra" "github.com/superfly/flyctl/docstrings" @@ -12,9 +12,9 @@ import ( //TODO: Move all output to status styled begin/done updates -func newRestartCommand() *Command { +func newRestartCommand(client *client.Client) *Command { restartStrings := docstrings.Get("restart") - restartCmd := BuildCommandKS(nil, runRestart, restartStrings, os.Stdout, requireSession, requireAppNameAsArg) + restartCmd := BuildCommandKS(nil, runRestart, restartStrings, client, requireSession, requireAppNameAsArg) restartCmd.Args = cobra.RangeArgs(0, 1) return restartCmd diff --git a/cmd/resume.go b/cmd/resume.go index c3acaeb1b3..911fb382fd 100644 --- a/cmd/resume.go +++ b/cmd/resume.go @@ -7,6 +7,7 @@ import ( "github.com/briandowns/spinner" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/spf13/cobra" "github.com/superfly/flyctl/docstrings" @@ -14,10 +15,10 @@ import ( //TODO: Move all output to status styled begin/done updates -func newResumeCommand() *Command { +func newResumeCommand(client *client.Client) *Command { resumeStrings := docstrings.Get("resume") - resumeCmd := BuildCommandKS(nil, runResume, resumeStrings, os.Stdout, requireSession, requireAppNameAsArg) + resumeCmd := BuildCommandKS(nil, runResume, resumeStrings, client, requireSession, requireAppNameAsArg) resumeCmd.Args = cobra.RangeArgs(0, 1) return resumeCmd diff --git a/cmd/root.go b/cmd/root.go index ba7f6aabe3..7869a03aa2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,37 +17,21 @@ import ( // ErrAbort - Error generated when application aborts var ErrAbort = errors.New("abort") -var flyctlClient *client.Client - -var rootStrings = docstrings.Get("flyctl") -var rootCmd = &Command{ - Command: &cobra.Command{ - Use: rootStrings.Usage, - Short: rootStrings.Short, - Long: rootStrings.Long, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - cmd.SilenceUsage = true - cmd.SilenceErrors = true - - flyctlClient = client.NewClient() - }, - }, -} -// GetRootCommand - root for commands -func GetRootCommand() *cobra.Command { - return rootCmd.Command -} - -// Execute - root command execution -func Execute() { - defer flyctl.BackgroundTaskWG.Wait() - - err := rootCmd.Execute() - checkErr(err) -} +func NewRootCmd(client *client.Client) *cobra.Command { + rootStrings := docstrings.Get("flyctl") + rootCmd := &Command{ + Command: &cobra.Command{ + Use: rootStrings.Usage, + Short: rootStrings.Short, + Long: rootStrings.Long, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + cmd.SilenceUsage = true + cmd.SilenceErrors = true + }, + }, + } -func init() { rootCmd.PersistentFlags().StringP("access-token", "t", "", "Fly API Access Token") err := viper.BindPFlag(flyctl.ConfigAPIToken, rootCmd.PersistentFlags().Lookup("access-token")) checkErr(err) @@ -68,53 +52,53 @@ func init() { checkErr(err) rootCmd.AddCommand( - newAppsCommand(), - newAuthCommand(), - newBuildsCommand(), - newCurlCommand(), - newCertificatesCommand(), - newConfigCommand(), - newDashboardCommand(), - newDeployCommand(), - newDestroyCommand(), - newDocsCommand(), - newHistoryCommand(), - newInfoCommand(), - newInitCommand(), - newIPAddressesCommand(), - newListCommand(), - newLogsCommand(), - newMonitorCommand(), - newMoveCommand(), - newOpenCommand(), - newPlatformCommand(), - newRegionsCommand(), - newReleasesCommand(), - newRestartCommand(), - newResumeCommand(), - newScaleCommand(), - newAutoscaleCommand(), - newSecretsCommand(), - newStatusCommand(), - newSuspendCommand(), - newVersionCommand(), - newDNSCommand(), - newDomainsCommand(), - newOrgsCommand(), - newBuiltinsCommand(), - newVolumesCommand(), - newWireGuardCommand(), - newSSHCommand(), - newChecksCommand(), - newPostgresCommand(), - newVMCommand(), - newLaunchCommand(), + newAppsCommand(client), + newAuthCommand(client), + newBuildsCommand(client), + newCurlCommand(client), + newCertificatesCommand(client), + newConfigCommand(client), + newDashboardCommand(client), + newDeployCommand(client), + newDestroyCommand(client), + newDocsCommand(client), + newHistoryCommand(client), + newInfoCommand(client), + newInitCommand(client), + newIPAddressesCommand(client), + newListCommand(client), + newLogsCommand(client), + newMonitorCommand(client), + newMoveCommand(client), + newOpenCommand(client), + newPlatformCommand(client), + newRegionsCommand(client), + newReleasesCommand(client), + newRestartCommand(client), + newResumeCommand(client), + newScaleCommand(client), + newAutoscaleCommand(client), + newSecretsCommand(client), + newStatusCommand(client), + newSuspendCommand(client), + newVersionCommand(client), + newDNSCommand(client), + newDomainsCommand(client), + newOrgsCommand(client), + newBuiltinsCommand(client), + newVolumesCommand(client), + newWireGuardCommand(client), + newSSHCommand(client), + newChecksCommand(client), + newPostgresCommand(client), + newVMCommand(client), + newLaunchCommand(client), ) - initConfig() + return rootCmd.Command } -func initConfig() { +func init() { flyctl.InitConfig() flyctl.CheckForUpdate(false, false) // allow skipping, don't be silent } diff --git a/cmd/scale.go b/cmd/scale.go index dc3397c038..bbd6837202 100644 --- a/cmd/scale.go +++ b/cmd/scale.go @@ -3,10 +3,10 @@ package cmd import ( "encoding/json" "fmt" - "os" "strconv" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/api" "github.com/superfly/flyctl/docstrings" @@ -14,13 +14,13 @@ import ( "github.com/spf13/cobra" ) -func newScaleCommand() *Command { +func newScaleCommand(client *client.Client) *Command { scaleStrings := docstrings.Get("scale") - cmd := BuildCommandKS(nil, nil, scaleStrings, os.Stdout, requireSession, requireAppName) + cmd := BuildCommandKS(nil, nil, scaleStrings, client, requireSession, requireAppName) vmCmdStrings := docstrings.Get("scale.vm") - vmCmd := BuildCommand(cmd, runScaleVM, vmCmdStrings.Usage, vmCmdStrings.Short, vmCmdStrings.Long, os.Stdout, requireSession, requireAppName) + vmCmd := BuildCommand(cmd, runScaleVM, vmCmdStrings.Usage, vmCmdStrings.Short, vmCmdStrings.Long, client, requireSession, requireAppName) vmCmd.Args = cobra.ExactArgs(1) vmCmd.AddIntFlag(IntFlagOpts{ Name: "memory", @@ -29,15 +29,15 @@ func newScaleCommand() *Command { }) memoryCmdStrings := docstrings.Get("scale.memory") - memoryCmd := BuildCommandKS(cmd, runScaleMemory, memoryCmdStrings, os.Stdout, requireSession, requireAppName) + memoryCmd := BuildCommandKS(cmd, runScaleMemory, memoryCmdStrings, client, requireSession, requireAppName) memoryCmd.Args = cobra.ExactArgs(1) countCmdStrings := docstrings.Get("scale.count") - countCmd := BuildCommand(cmd, runScaleCount, countCmdStrings.Usage, countCmdStrings.Short, countCmdStrings.Long, os.Stdout, requireSession, requireAppName) + countCmd := BuildCommand(cmd, runScaleCount, countCmdStrings.Usage, countCmdStrings.Short, countCmdStrings.Long, client, requireSession, requireAppName) countCmd.Args = cobra.ExactArgs(1) showCmdStrings := docstrings.Get("scale.show") - BuildCommand(cmd, runScaleShow, showCmdStrings.Usage, showCmdStrings.Short, showCmdStrings.Long, os.Stdout, requireSession, requireAppName) + BuildCommand(cmd, runScaleShow, showCmdStrings.Usage, showCmdStrings.Short, showCmdStrings.Long, client, requireSession, requireAppName) return cmd } diff --git a/cmd/secrets.go b/cmd/secrets.go index 532e790346..96c9d76dda 100644 --- a/cmd/secrets.go +++ b/cmd/secrets.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" @@ -16,16 +17,16 @@ import ( "github.com/superfly/flyctl/helpers" ) -func newSecretsCommand() *Command { +func newSecretsCommand(client *client.Client) *Command { secretsStrings := docstrings.Get("secrets") - cmd := BuildCommandKS(nil, nil, secretsStrings, os.Stdout, requireSession, requireAppName) + cmd := BuildCommandKS(nil, nil, secretsStrings, client, requireSession, requireAppName) secretsListStrings := docstrings.Get("secrets.list") - BuildCommandKS(cmd, runListSecrets, secretsListStrings, os.Stdout, requireSession, requireAppName) + BuildCommandKS(cmd, runListSecrets, secretsListStrings, client, requireSession, requireAppName) secretsSetStrings := docstrings.Get("secrets.set") - set := BuildCommandKS(cmd, runSetSecrets, secretsSetStrings, os.Stdout, requireSession, requireAppName) + set := BuildCommandKS(cmd, runSetSecrets, secretsSetStrings, client, requireSession, requireAppName) //TODO: Move examples into docstrings set.Command.Example = `flyctl secrets set FLY_ENV=production LOG_LEVEL=info @@ -39,14 +40,14 @@ func newSecretsCommand() *Command { }) secretsImportStrings := docstrings.Get("secrets.import") - importCmd := BuildCommandKS(cmd, runImportSecrets, secretsImportStrings, os.Stdout, requireSession, requireAppName) + importCmd := BuildCommandKS(cmd, runImportSecrets, secretsImportStrings, client, requireSession, requireAppName) importCmd.AddBoolFlag(BoolFlagOpts{ Name: "detach", Description: "Return immediately instead of monitoring deployment progress", }) secretsUnsetStrings := docstrings.Get("secrets.unset") - unset := BuildCommandKS(cmd, runSecretsUnset, secretsUnsetStrings, os.Stdout, requireSession, requireAppName) + unset := BuildCommandKS(cmd, runSecretsUnset, secretsUnsetStrings, client, requireSession, requireAppName) unset.Command.Args = cobra.MinimumNArgs(1) unset.AddBoolFlag(BoolFlagOpts{ diff --git a/cmd/ssh.go b/cmd/ssh.go index e7a4837c31..fab20dd3af 100644 --- a/cmd/ssh.go +++ b/cmd/ssh.go @@ -19,16 +19,17 @@ import ( "github.com/superfly/flyctl/api" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" + "github.com/superfly/flyctl/internal/client" "golang.org/x/crypto/ed25519" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" ) -func newSSHCommand() *Command { - cmd := BuildCommandKS(nil, nil, docstrings.Get("ssh"), os.Stdout, requireSession) +func newSSHCommand(client *client.Client) *Command { + cmd := BuildCommandKS(nil, nil, docstrings.Get("ssh"), client, requireSession) child := func(parent *Command, fn RunFn, ds string) *Command { - return BuildCommandKS(parent, fn, docstrings.Get(ds), os.Stdout, requireSession) + return BuildCommandKS(parent, fn, docstrings.Get(ds), client, requireSession) } child(cmd, runSSHLog, "ssh.log").Args = cobra.MaximumNArgs(1) @@ -37,7 +38,7 @@ func newSSHCommand() *Command { console := BuildCommandKS(cmd, runSSHConsole, docstrings.Get("ssh.console"), - os.Stdout, + client, requireSession, requireAppName) console.Args = cobra.MaximumNArgs(1) diff --git a/cmd/status.go b/cmd/status.go index da6682cb3c..b156e75d97 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -3,11 +3,11 @@ package cmd import ( "fmt" "io" - "os" "time" "github.com/inancgumus/screen" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/segmentio/textio" "github.com/spf13/cobra" @@ -18,18 +18,21 @@ import ( "github.com/superfly/flyctl/cmd/presenters" ) -func newStatusCommand() *Command { +func newStatusCommand(client *client.Client) *Command { statusStrings := docstrings.Get("status") - cmd := BuildCommandKS(nil, runStatus, statusStrings, os.Stdout, requireSession, requireAppNameAsArg) + cmd := BuildCommandKS(nil, runStatus, statusStrings, client, requireSession, requireAppNameAsArg) //TODO: Move flag descriptions to docstrings cmd.AddBoolFlag(BoolFlagOpts{Name: "all", Description: "Show completed instances"}) cmd.AddBoolFlag(BoolFlagOpts{Name: "deployment", Description: "Always show deployment status"}) cmd.AddBoolFlag(BoolFlagOpts{Name: "watch", Description: "Refresh details"}) cmd.AddIntFlag(IntFlagOpts{Name: "rate", Description: "Refresh Rate for --watch", Default: 5}) + cmd.Command.Flags().String("wtf", "defaultwtf", "wtf usage") + + // cmd.Command.Flag() allocStatusStrings := docstrings.Get("status.instance") - allocStatusCmd := BuildCommand(cmd, runAllocStatus, allocStatusStrings.Usage, allocStatusStrings.Short, allocStatusStrings.Long, os.Stdout, requireSession, requireAppName) + allocStatusCmd := BuildCommand(cmd, runAllocStatus, allocStatusStrings.Usage, allocStatusStrings.Short, allocStatusStrings.Long, client, requireSession, requireAppName) allocStatusCmd.Args = cobra.ExactArgs(1) return cmd } diff --git a/cmd/suspend.go b/cmd/suspend.go index d35edfae9d..f95434becd 100644 --- a/cmd/suspend.go +++ b/cmd/suspend.go @@ -6,6 +6,7 @@ import ( "time" "github.com/superfly/flyctl/cmdctx" + "github.com/superfly/flyctl/internal/client" "github.com/briandowns/spinner" "github.com/spf13/cobra" @@ -14,11 +15,11 @@ import ( //TODO: Move all output to status styled begin/done updates -func newSuspendCommand() *Command { +func newSuspendCommand(client *client.Client) *Command { suspendStrings := docstrings.Get("suspend") - suspendCmd := BuildCommandKS(nil, runSuspend, suspendStrings, os.Stdout, requireSession, requireAppNameAsArg) + suspendCmd := BuildCommandKS(nil, runSuspend, suspendStrings, client, requireSession, requireAppNameAsArg) suspendCmd.Args = cobra.RangeArgs(0, 1) return suspendCmd diff --git a/cmd/version.go b/cmd/version.go index 483d0945a2..deec308e32 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -10,19 +10,14 @@ import ( "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/flyctl" "github.com/superfly/flyctl/flyname" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" ) -func newVersionCommand() *Command { +func newVersionCommand(client *client.Client) *Command { versionStrings := docstrings.Get("version") - version := BuildCommandKS(nil, runVersion, versionStrings, os.Stdout) - version.AddStringFlag(StringFlagOpts{ - Name: "completions", - Shorthand: "c", - Description: "Generate completions for supported shells bash/zsh)", - }) - + version := BuildCommandKS(nil, runVersion, versionStrings, client) version.AddStringFlag(StringFlagOpts{ Name: "saveinstall", Shorthand: "s", @@ -31,26 +26,12 @@ func newVersionCommand() *Command { version.Flag("saveinstall").Hidden = true updateStrings := docstrings.Get("version.update") - BuildCommandKS(version, runUpdate, updateStrings, os.Stdout) + BuildCommandKS(version, runUpdate, updateStrings, client) return version } func runVersion(ctx *cmdctx.CmdContext) error { - - shellType, _ := ctx.Config.GetString("completions") - - if shellType != "" { - switch shellType { - case "bash": - return GetRootCommand().GenBashCompletion(os.Stdout) - case "zsh": - return GetRootCommand().GenZshCompletion(os.Stdout) - default: - return fmt.Errorf("unable to generate %s completions", shellType) - } - } - saveInstall, _ := ctx.Config.GetString("saveinstall") if saveInstall != "" { diff --git a/cmd/vm.go b/cmd/vm.go index 34dedab730..625ebbc26a 100644 --- a/cmd/vm.go +++ b/cmd/vm.go @@ -2,23 +2,23 @@ package cmd import ( "fmt" - "os" "github.com/spf13/cobra" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" + "github.com/superfly/flyctl/internal/client" ) -func newVMCommand() *Command { - vmCmd := BuildCommandKS(nil, nil, docstrings.Get("vm"), os.Stdout) +func newVMCommand(client *client.Client) *Command { + vmCmd := BuildCommandKS(nil, nil, docstrings.Get("vm"), client) - vmRestartCmd := BuildCommandKS(vmCmd, runVMRestart, docstrings.Get("vm.restart"), os.Stdout, requireSession, requireAppName) + vmRestartCmd := BuildCommandKS(vmCmd, runVMRestart, docstrings.Get("vm.restart"), client, requireSession, requireAppName) vmRestartCmd.Args = cobra.ExactArgs(1) - vmStopCmd := BuildCommandKS(vmCmd, runVMStop, docstrings.Get("vm.stop"), os.Stdout, requireSession, requireAppName) + vmStopCmd := BuildCommandKS(vmCmd, runVMStop, docstrings.Get("vm.stop"), client, requireSession, requireAppName) vmStopCmd.Args = cobra.ExactArgs(1) - vmStatusCmd := BuildCommandKS(vmCmd, runAllocStatus, docstrings.Get("vm.status"), os.Stdout, requireSession, requireAppName) + vmStatusCmd := BuildCommandKS(vmCmd, runAllocStatus, docstrings.Get("vm.status"), client, requireSession, requireAppName) vmStatusCmd.Args = cobra.ExactArgs(1) return vmCmd diff --git a/cmd/volumes.go b/cmd/volumes.go index 14e4fc7f51..77d978c64f 100644 --- a/cmd/volumes.go +++ b/cmd/volumes.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "os" "strconv" "time" @@ -10,20 +9,21 @@ import ( "github.com/spf13/cobra" "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/helpers" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/docstrings" ) -func newVolumesCommand() *Command { +func newVolumesCommand(client *client.Client) *Command { volumesStrings := docstrings.Get("volumes") - volumesCmd := BuildCommandKS(nil, nil, volumesStrings, os.Stdout, requireAppName, requireSession) + volumesCmd := BuildCommandKS(nil, nil, volumesStrings, client, requireAppName, requireSession) volumesCmd.Aliases = []string{"vol"} listStrings := docstrings.Get("volumes.list") - BuildCommandKS(volumesCmd, runListVolumes, listStrings, os.Stdout, requireAppName, requireSession) + BuildCommandKS(volumesCmd, runListVolumes, listStrings, client, requireAppName, requireSession) createStrings := docstrings.Get("volumes.create") - createCmd := BuildCommandKS(volumesCmd, runCreateVolume, createStrings, os.Stdout, requireAppName, requireSession) + createCmd := BuildCommandKS(volumesCmd, runCreateVolume, createStrings, client, requireAppName, requireSession) createCmd.Args = cobra.ExactArgs(1) createCmd.AddStringFlag(StringFlagOpts{ @@ -44,11 +44,11 @@ func newVolumesCommand() *Command { }) deleteStrings := docstrings.Get("volumes.delete") - deleteCmd := BuildCommandKS(volumesCmd, runDestroyVolume, deleteStrings, os.Stdout, requireSession) + deleteCmd := BuildCommandKS(volumesCmd, runDestroyVolume, deleteStrings, client, requireSession) deleteCmd.Args = cobra.ExactArgs(1) showStrings := docstrings.Get("volumes.show") - showCmd := BuildCommandKS(volumesCmd, runShowVolume, showStrings, os.Stdout, requireSession) + showCmd := BuildCommandKS(volumesCmd, runShowVolume, showStrings, client, requireSession) showCmd.Args = cobra.ExactArgs(1) return volumesCmd diff --git a/cmd/wireguard.go b/cmd/wireguard.go index edf9a4ca89..efa0dce92e 100644 --- a/cmd/wireguard.go +++ b/cmd/wireguard.go @@ -23,16 +23,17 @@ import ( "github.com/superfly/flyctl/cmdctx" "github.com/superfly/flyctl/docstrings" "github.com/superfly/flyctl/flyctl" + "github.com/superfly/flyctl/internal/client" "github.com/superfly/flyctl/pkg/wg" "golang.org/x/crypto/curve25519" ) -func newWireGuardCommand() *Command { - cmd := BuildCommandKS(nil, nil, docstrings.Get("wireguard"), os.Stdout, requireSession) +func newWireGuardCommand(client *client.Client) *Command { + cmd := BuildCommandKS(nil, nil, docstrings.Get("wireguard"), client, requireSession) cmd.Aliases = []string{"wg"} child := func(parent *Command, fn RunFn, ds string) *Command { - return BuildCommandKS(parent, fn, docstrings.Get(ds), os.Stdout, requireSession) + return BuildCommandKS(parent, fn, docstrings.Get(ds), client, requireSession) } child(cmd, runWireGuardList, "wireguard.list").Args = cobra.MaximumNArgs(1) diff --git a/cmdctx/cmdcontext.go b/cmdctx/cmdcontext.go index 94809fe44f..302b65a72b 100644 --- a/cmdctx/cmdcontext.go +++ b/cmdctx/cmdcontext.go @@ -14,18 +14,18 @@ import ( "github.com/superfly/flyctl/cmd/presenters" "github.com/superfly/flyctl/flyctl" "github.com/superfly/flyctl/internal/client" - "github.com/superfly/flyctl/terminal" + "github.com/superfly/flyctl/pkg/iostreams" ) // CmdContext - context passed to commands being run type CmdContext struct { + IO *iostreams.IOStreams Client *client.Client Config flyctl.Config GlobalConfig flyctl.Config NS string Args []string Out io.Writer - Terminal *terminal.Terminal WorkingDir string ConfigFile string AppName string @@ -53,15 +53,15 @@ const SBEGIN = "begin" const SDONE = "done" const SERROR = "error" -func NewCmdContext(flyctlClient *client.Client, ns string, out io.Writer, args []string) (*CmdContext, error) { +func NewCmdContext(flyctlClient *client.Client, ns string, args []string) (*CmdContext, error) { ctx := &CmdContext{ + IO: flyctlClient.IO, Client: flyctlClient, NS: ns, Config: flyctl.ConfigNS(ns), GlobalConfig: flyctl.FlyConfig, - Out: out, Args: args, - Terminal: terminal.NewTerminal(out), + Out: flyctlClient.IO.Out, } cwd, err := os.Getwd() @@ -116,13 +116,13 @@ func (commandContext *CmdContext) Frender(views ...PresenterOption) error { } } - return commandContext.render(commandContext.Out, views...) + return commandContext.render(commandContext.IO.Out, views...) } // FrenderPrefix - render a view to a Writer func (commandContext *CmdContext) FrenderPrefix(prefix string, views ...PresenterOption) error { // If JSON output wanted, set in all views - p := textio.NewPrefixWriter(commandContext.Out, " ") + p := textio.NewPrefixWriter(commandContext.IO.Out, " ") if commandContext.OutputJSON() { for i := range views { @@ -148,7 +148,7 @@ func (commandContext *CmdContext) StatusLn() { return } - fmt.Fprintln(commandContext.Out) + fmt.Fprintln(commandContext.IO.Out) } func (commandContext *CmdContext) Status(source string, status string, args ...interface{}) { @@ -169,10 +169,10 @@ func (commandContext *CmdContext) Status(source string, status string, args ...i Status: status, Message: message.String()} outbuf, _ := json.Marshal(outstruct) - fmt.Fprintln(commandContext.Out, string(outbuf)) + fmt.Fprintln(commandContext.IO.Out, string(outbuf)) return } else { - fmt.Fprintln(commandContext.Out, statusToEffect(status, message.String())) + fmt.Fprintln(commandContext.IO.Out, statusToEffect(status, message.String())) } } @@ -207,16 +207,16 @@ func (commandContext *CmdContext) Statusf(source string, status string, format s Source: source, Status: status, Message: message}) - fmt.Fprintln(commandContext.Out, string(outbuf)) + fmt.Fprintln(commandContext.IO.Out, string(outbuf)) return } else { - fmt.Fprint(commandContext.Out, statusToEffect(status, message)) + fmt.Fprint(commandContext.IO.Out, statusToEffect(status, message)) } } func (commandContext *CmdContext) WriteJSON(myData interface{}) { outBuf, _ := json.MarshalIndent(myData, "", " ") - fmt.Fprintln(commandContext.Out, string(outBuf)) + fmt.Fprintln(commandContext.IO.Out, string(outBuf)) } func (commandContext *CmdContext) OutputJSON() bool { diff --git a/doc/main.go b/doc/main.go index 604c2020d1..bf99982e9c 100644 --- a/doc/main.go +++ b/doc/main.go @@ -18,10 +18,12 @@ import ( "github.com/spf13/cobra" "github.com/superfly/flyctl/cmd" + "github.com/superfly/flyctl/internal/cmdutil" ) func main() { - cmd := cmd.GetRootCommand() + cc := cmdutil.NewContext() + cmd := cmd.NewRootCmd(cc) cmd.DisableAutoGenTag = true filePrepender := func(filename string) string { diff --git a/go.mod b/go.mod index 2209c17180..8189334613 100644 --- a/go.mod +++ b/go.mod @@ -12,22 +12,27 @@ require ( github.com/blang/semver v3.5.1+incompatible github.com/briandowns/spinner v1.12.0 github.com/buildpacks/pack v0.17.0 + github.com/cli/safeexec v1.0.0 // indirect github.com/containerd/console v1.0.1 github.com/docker/cli v20.10.4+incompatible github.com/docker/docker v20.10.0-beta1.0.20201110211921-af34b94a78a1+incompatible github.com/dustin/go-humanize v1.0.0 github.com/ejcx/sshcert v1.0.1 github.com/getsentry/sentry-go v0.9.0 + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/hashicorp/go-multierror v1.1.0 github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 github.com/jpillora/backoff v1.0.0 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/machinebox/graphql v0.2.3-0.20181106130121-3a9253180225 github.com/matryer/is v1.3.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.12 + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/moby/buildkit v0.8.1 github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect github.com/morikuni/aec v1.0.0 + github.com/muesli/termenv v0.7.4 // indirect github.com/novln/docker-parser v1.0.0 github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml v1.8.1 diff --git a/go.sum b/go.sum index bb78dc3d4f..245379a3b2 100644 --- a/go.sum +++ b/go.sum @@ -220,6 +220,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= +github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -739,6 +741,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/machinebox/graphql v0.2.3-0.20181106130121-3a9253180225 h1:guHWmqIKr4G+gQ4uYU5vcZjsUhhklRA2uOcGVfcfqis= github.com/machinebox/graphql v0.2.3-0.20181106130121-3a9253180225/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -785,6 +789,8 @@ github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88J github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/michaeldwan/toml v0.3.2-0.20191213213541-3c5ced72b6f3 h1:fO9bKICUp3uSft7wDhdeAbXrwHsmko2JE/OewVp/jVc= github.com/michaeldwan/toml v0.3.2-0.20191213213541-3c5ced72b6f3/go.mod h1:zs/AcS/gLJIa42aCC4p6n7sJIQIAV5LNLBu79EjGjt4= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= @@ -834,6 +840,8 @@ github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOA github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= +github.com/muesli/termenv v0.7.4 h1:/pBqvU5CpkY53tU0vVn+xgs2ZTX63aH5nY+SSps5Xa8= +github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= diff --git a/internal/client/client.go b/internal/client/client.go index 43e2d67e4e..853668539b 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -5,12 +5,15 @@ import ( "github.com/superfly/flyctl/api" "github.com/superfly/flyctl/flyctl" + "github.com/superfly/flyctl/pkg/iostreams" ) var ErrNoAuthToken = errors.New("No access token available. Please login with 'flyctl auth login'") func NewClient() *Client { - client := &Client{} + client := &Client{ + IO: iostreams.System(), + } client.InitApi() @@ -18,6 +21,8 @@ func NewClient() *Client { } type Client struct { + IO *iostreams.IOStreams + api *api.Client } diff --git a/main.go b/main.go index acf40e4dfa..53cb7887b3 100644 --- a/main.go +++ b/main.go @@ -1,16 +1,19 @@ package main import ( + "context" "fmt" "os" "runtime/debug" "time" "github.com/getsentry/sentry-go" + "github.com/hashicorp/go-multierror" "github.com/logrusorgru/aurora" "github.com/superfly/flyctl/cmd" "github.com/superfly/flyctl/flyctl" "github.com/superfly/flyctl/flyname" + "github.com/superfly/flyctl/internal/client" ) func main() { @@ -51,5 +54,56 @@ func main() { } }() - cmd.Execute() + defer flyctl.BackgroundTaskWG.Wait() + + client := client.NewClient() + + if !client.IO.ColorEnabled() { + // disable colors + } + + root := cmd.NewRootCmd(client) + + // cmd, _, err := root.Traverse(os.Args[1:]) + // fmt.Println("resolved to", cmd.Use) + // checkErr(err) + + _, err := root.ExecuteC() + checkErr(err) +} + +func checkErr(err error) { + if err == nil { + return + } + + if !isCancelledError(err) { + fmt.Println(aurora.Red("Error"), err) + } + + safeExit() +} + +func isCancelledError(err error) bool { + if err == cmd.ErrAbort { + return true + } + + if err == context.Canceled { + return true + } + + if merr, ok := err.(*multierror.Error); ok { + if len(merr.Errors) == 1 && merr.Errors[0] == context.Canceled { + return true + } + } + + return false +} + +func safeExit() { + flyctl.BackgroundTaskWG.Wait() + + os.Exit(1) } diff --git a/pkg/iostreams/color.go b/pkg/iostreams/color.go new file mode 100644 index 0000000000..46cfc3a3cb --- /dev/null +++ b/pkg/iostreams/color.go @@ -0,0 +1,168 @@ +package iostreams + +import ( + "fmt" + "os" + "strings" + + "github.com/mgutz/ansi" +) + +var ( + magenta = ansi.ColorFunc("magenta") + cyan = ansi.ColorFunc("cyan") + red = ansi.ColorFunc("red") + yellow = ansi.ColorFunc("yellow") + blue = ansi.ColorFunc("blue") + green = ansi.ColorFunc("green") + gray = ansi.ColorFunc("black+h") + bold = ansi.ColorFunc("default+b") + cyanBold = ansi.ColorFunc("cyan+b") + + gray256 = func(t string) string { + return fmt.Sprintf("\x1b[%d;5;%dm%s\x1b[m", 38, 242, t) + } +) + +func EnvColorDisabled() bool { + return os.Getenv("NO_COLOR") != "" || os.Getenv("CLICOLOR") == "0" +} + +func EnvColorForced() bool { + return os.Getenv("CLICOLOR_FORCE") != "" && os.Getenv("CLICOLOR_FORCE") != "0" +} + +func Is256ColorSupported() bool { + term := os.Getenv("TERM") + colorterm := os.Getenv("COLORTERM") + + return strings.Contains(term, "256") || + strings.Contains(term, "24bit") || + strings.Contains(term, "truecolor") || + strings.Contains(colorterm, "256") || + strings.Contains(colorterm, "24bit") || + strings.Contains(colorterm, "truecolor") +} + +func NewColorScheme(enabled, is256enabled bool) *ColorScheme { + return &ColorScheme{ + enabled: enabled, + is256enabled: is256enabled, + } +} + +type ColorScheme struct { + enabled bool + is256enabled bool +} + +func (c *ColorScheme) Bold(t string) string { + if !c.enabled { + return t + } + return bold(t) +} + +func (c *ColorScheme) Red(t string) string { + if !c.enabled { + return t + } + return red(t) +} + +func (c *ColorScheme) Yellow(t string) string { + if !c.enabled { + return t + } + return yellow(t) +} + +func (c *ColorScheme) Green(t string) string { + if !c.enabled { + return t + } + return green(t) +} + +func (c *ColorScheme) Gray(t string) string { + if !c.enabled { + return t + } + if c.is256enabled { + return gray256(t) + } + return gray(t) +} + +func (c *ColorScheme) Magenta(t string) string { + if !c.enabled { + return t + } + return magenta(t) +} + +func (c *ColorScheme) Cyan(t string) string { + if !c.enabled { + return t + } + return cyan(t) +} + +func (c *ColorScheme) CyanBold(t string) string { + if !c.enabled { + return t + } + return cyanBold(t) +} + +func (c *ColorScheme) Blue(t string) string { + if !c.enabled { + return t + } + return blue(t) +} + +func (c *ColorScheme) SuccessIcon() string { + return c.SuccessIconWithColor(c.Green) +} + +func (c *ColorScheme) SuccessIconWithColor(colo func(string) string) string { + return colo("✓") +} + +func (c *ColorScheme) WarningIcon() string { + return c.Yellow("!") +} + +func (c *ColorScheme) FailureIcon() string { + return c.Red("X") +} + +func (c *ColorScheme) ColorFromString(s string) func(string) string { + s = strings.ToLower(s) + var fn func(string) string + switch s { + case "bold": + fn = c.Bold + case "red": + fn = c.Red + case "yellow": + fn = c.Yellow + case "green": + fn = c.Green + case "gray": + fn = c.Gray + case "magenta": + fn = c.Magenta + case "cyan": + fn = c.Cyan + case "blue": + fn = c.Blue + default: + fn = func(s string) string { + return s + } + } + + return fn +} diff --git a/pkg/iostreams/color_test.go b/pkg/iostreams/color_test.go new file mode 100644 index 0000000000..90b3ae0245 --- /dev/null +++ b/pkg/iostreams/color_test.go @@ -0,0 +1,145 @@ +package iostreams + +import ( + "os" + "testing" +) + +func TestEnvColorDisabled(t *testing.T) { + orig_NO_COLOR := os.Getenv("NO_COLOR") + orig_CLICOLOR := os.Getenv("CLICOLOR") + orig_CLICOLOR_FORCE := os.Getenv("CLICOLOR_FORCE") + t.Cleanup(func() { + os.Setenv("NO_COLOR", orig_NO_COLOR) + os.Setenv("CLICOLOR", orig_CLICOLOR) + os.Setenv("CLICOLOR_FORCE", orig_CLICOLOR_FORCE) + }) + + tests := []struct { + name string + NO_COLOR string + CLICOLOR string + CLICOLOR_FORCE string + want bool + }{ + { + name: "pristine env", + NO_COLOR: "", + CLICOLOR: "", + CLICOLOR_FORCE: "", + want: false, + }, + { + name: "NO_COLOR enabled", + NO_COLOR: "1", + CLICOLOR: "", + CLICOLOR_FORCE: "", + want: true, + }, + { + name: "CLICOLOR disabled", + NO_COLOR: "", + CLICOLOR: "0", + CLICOLOR_FORCE: "", + want: true, + }, + { + name: "CLICOLOR enabled", + NO_COLOR: "", + CLICOLOR: "1", + CLICOLOR_FORCE: "", + want: false, + }, + { + name: "CLICOLOR_FORCE has no effect", + NO_COLOR: "", + CLICOLOR: "", + CLICOLOR_FORCE: "1", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + os.Setenv("NO_COLOR", tt.NO_COLOR) + os.Setenv("CLICOLOR", tt.CLICOLOR) + os.Setenv("CLICOLOR_FORCE", tt.CLICOLOR_FORCE) + + if got := EnvColorDisabled(); got != tt.want { + t.Errorf("EnvColorDisabled(): want %v, got %v", tt.want, got) + } + }) + } +} + +func TestEnvColorForced(t *testing.T) { + orig_NO_COLOR := os.Getenv("NO_COLOR") + orig_CLICOLOR := os.Getenv("CLICOLOR") + orig_CLICOLOR_FORCE := os.Getenv("CLICOLOR_FORCE") + t.Cleanup(func() { + os.Setenv("NO_COLOR", orig_NO_COLOR) + os.Setenv("CLICOLOR", orig_CLICOLOR) + os.Setenv("CLICOLOR_FORCE", orig_CLICOLOR_FORCE) + }) + + tests := []struct { + name string + NO_COLOR string + CLICOLOR string + CLICOLOR_FORCE string + want bool + }{ + { + name: "pristine env", + NO_COLOR: "", + CLICOLOR: "", + CLICOLOR_FORCE: "", + want: false, + }, + { + name: "NO_COLOR enabled", + NO_COLOR: "1", + CLICOLOR: "", + CLICOLOR_FORCE: "", + want: false, + }, + { + name: "CLICOLOR disabled", + NO_COLOR: "", + CLICOLOR: "0", + CLICOLOR_FORCE: "", + want: false, + }, + { + name: "CLICOLOR enabled", + NO_COLOR: "", + CLICOLOR: "1", + CLICOLOR_FORCE: "", + want: false, + }, + { + name: "CLICOLOR_FORCE enabled", + NO_COLOR: "", + CLICOLOR: "", + CLICOLOR_FORCE: "1", + want: true, + }, + { + name: "CLICOLOR_FORCE disabled", + NO_COLOR: "", + CLICOLOR: "", + CLICOLOR_FORCE: "0", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + os.Setenv("NO_COLOR", tt.NO_COLOR) + os.Setenv("CLICOLOR", tt.CLICOLOR) + os.Setenv("CLICOLOR_FORCE", tt.CLICOLOR_FORCE) + + if got := EnvColorForced(); got != tt.want { + t.Errorf("EnvColorForced(): want %v, got %v", tt.want, got) + } + }) + } +} diff --git a/pkg/iostreams/iostreams.go b/pkg/iostreams/iostreams.go new file mode 100644 index 0000000000..3b852971d8 --- /dev/null +++ b/pkg/iostreams/iostreams.go @@ -0,0 +1,349 @@ +// forked from https://github.com/cli/cli/tree/trunk/pkg/iostreams + +package iostreams + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/briandowns/spinner" + "github.com/cli/safeexec" + "github.com/google/shlex" + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" + "github.com/muesli/termenv" + "golang.org/x/crypto/ssh/terminal" +) + +type IOStreams struct { + In io.ReadCloser + Out io.Writer + ErrOut io.Writer + + // the original (non-colorable) output stream + originalOut io.Writer + colorEnabled bool + is256enabled bool + terminalTheme string + + progressIndicatorEnabled bool + progressIndicator *spinner.Spinner + + stdinTTYOverride bool + stdinIsTTY bool + stdoutTTYOverride bool + stdoutIsTTY bool + stderrTTYOverride bool + stderrIsTTY bool + + pagerCommand string + pagerProcess *os.Process + + neverPrompt bool + + TempFileOverride *os.File +} + +func (s *IOStreams) ColorEnabled() bool { + return s.colorEnabled +} + +func (s *IOStreams) ColorSupport256() bool { + return s.is256enabled +} + +func (s *IOStreams) DetectTerminalTheme() string { + if !s.ColorEnabled() { + s.terminalTheme = "none" + return "none" + } + + if s.pagerProcess != nil { + s.terminalTheme = "none" + return "none" + } + + style := os.Getenv("GLAMOUR_STYLE") + if style != "" && style != "auto" { + s.terminalTheme = "none" + return "none" + } + + if termenv.HasDarkBackground() { + s.terminalTheme = "dark" + return "dark" + } + + s.terminalTheme = "light" + return "light" +} + +func (s *IOStreams) TerminalTheme() string { + if s.terminalTheme == "" { + return "none" + } + + return s.terminalTheme +} + +func (s *IOStreams) SetStdinTTY(isTTY bool) { + s.stdinTTYOverride = true + s.stdinIsTTY = isTTY +} + +func (s *IOStreams) IsStdinTTY() bool { + if s.stdinTTYOverride { + return s.stdinIsTTY + } + if stdin, ok := s.In.(*os.File); ok { + return isTerminal(stdin) + } + return false +} + +func (s *IOStreams) SetStdoutTTY(isTTY bool) { + s.stdoutTTYOverride = true + s.stdoutIsTTY = isTTY +} + +func (s *IOStreams) IsStdoutTTY() bool { + if s.stdoutTTYOverride { + return s.stdoutIsTTY + } + if stdout, ok := s.Out.(*os.File); ok { + return isTerminal(stdout) + } + return false +} + +func (s *IOStreams) SetStderrTTY(isTTY bool) { + s.stderrTTYOverride = true + s.stderrIsTTY = isTTY +} + +func (s *IOStreams) IsStderrTTY() bool { + if s.stderrTTYOverride { + return s.stderrIsTTY + } + if stderr, ok := s.ErrOut.(*os.File); ok { + return isTerminal(stderr) + } + return false +} + +func (s *IOStreams) IsInteractive() bool { + return s.IsStdinTTY() && s.IsStdoutTTY() +} + +func (s *IOStreams) SetPager(cmd string) { + s.pagerCommand = cmd +} + +func (s *IOStreams) StartPager() error { + if s.pagerCommand == "" || s.pagerCommand == "cat" || !s.IsStdoutTTY() { + return nil + } + + pagerArgs, err := shlex.Split(s.pagerCommand) + if err != nil { + return err + } + + pagerEnv := os.Environ() + for i := len(pagerEnv) - 1; i >= 0; i-- { + if strings.HasPrefix(pagerEnv[i], "PAGER=") { + pagerEnv = append(pagerEnv[0:i], pagerEnv[i+1:]...) + } + } + if _, ok := os.LookupEnv("LESS"); !ok { + pagerEnv = append(pagerEnv, "LESS=FRX") + } + if _, ok := os.LookupEnv("LV"); !ok { + pagerEnv = append(pagerEnv, "LV=-c") + } + + pagerExe, err := safeexec.LookPath(pagerArgs[0]) + if err != nil { + return err + } + pagerCmd := exec.Command(pagerExe, pagerArgs[1:]...) + pagerCmd.Env = pagerEnv + pagerCmd.Stdout = s.Out + pagerCmd.Stderr = s.ErrOut + pagedOut, err := pagerCmd.StdinPipe() + if err != nil { + return err + } + s.Out = pagedOut + err = pagerCmd.Start() + if err != nil { + return err + } + s.pagerProcess = pagerCmd.Process + return nil +} + +func (s *IOStreams) StopPager() { + if s.pagerProcess == nil { + return + } + + s.Out.(io.ReadCloser).Close() + _, _ = s.pagerProcess.Wait() + s.pagerProcess = nil +} + +func (s *IOStreams) CanPrompt() bool { + if s.neverPrompt { + return false + } + + return s.IsInteractive() +} + +func (s *IOStreams) SetNeverPrompt(v bool) { + s.neverPrompt = v +} + +func (s *IOStreams) StartProgressIndicator() { + s.StartProgressIndicatorMsg("") +} + +func (s *IOStreams) StartProgressIndicatorMsg(msg string) { + if !s.progressIndicatorEnabled { + return + } + sp := spinner.New(spinner.CharSets[11], 400*time.Millisecond, spinner.WithWriter(s.ErrOut)) + sp.Prefix = msg + sp.Start() + s.progressIndicator = sp +} + +func (s *IOStreams) StopProgressIndicatorMsg(msg string) { + if s.progressIndicator == nil { + return + } + s.progressIndicator.FinalMSG = msg + s.progressIndicator.Stop() + s.progressIndicator = nil +} + +func (s *IOStreams) StopProgressIndicator() { + s.StopProgressIndicatorMsg("") +} + +func (s *IOStreams) TerminalWidth() int { + defaultWidth := 80 + out := s.Out + if s.originalOut != nil { + out = s.originalOut + } + + if w, _, err := terminalSize(out); err == nil { + return w + } + + if isCygwinTerminal(out) { + tputExe, err := safeexec.LookPath("tput") + if err != nil { + return defaultWidth + } + tputCmd := exec.Command(tputExe, "cols") + tputCmd.Stdin = os.Stdin + if out, err := tputCmd.Output(); err == nil { + if w, err := strconv.Atoi(strings.TrimSpace(string(out))); err == nil { + return w + } + } + } + + return defaultWidth +} + +func (s *IOStreams) ColorScheme() *ColorScheme { + return NewColorScheme(s.ColorEnabled(), s.ColorSupport256()) +} + +func (s *IOStreams) ReadUserFile(fn string) ([]byte, error) { + var r io.ReadCloser + if fn == "-" { + r = s.In + } else { + var err error + r, err = os.Open(fn) + if err != nil { + return nil, err + } + } + defer r.Close() + return ioutil.ReadAll(r) +} + +func (s *IOStreams) TempFile(dir, pattern string) (*os.File, error) { + if s.TempFileOverride != nil { + return s.TempFileOverride, nil + } + return ioutil.TempFile(dir, pattern) +} + +func System() *IOStreams { + stdoutIsTTY := isTerminal(os.Stdout) + stderrIsTTY := isTerminal(os.Stderr) + + pagerCommand := os.Getenv("PAGER") + + io := &IOStreams{ + In: os.Stdin, + originalOut: os.Stdout, + Out: colorable.NewColorable(os.Stdout), + ErrOut: colorable.NewColorable(os.Stderr), + colorEnabled: EnvColorForced() || (!EnvColorDisabled() && stdoutIsTTY), + is256enabled: Is256ColorSupported(), + pagerCommand: pagerCommand, + } + + if stdoutIsTTY && stderrIsTTY { + io.progressIndicatorEnabled = true + } + + // prevent duplicate isTerminal queries now that we know the answer + io.SetStdoutTTY(stdoutIsTTY) + io.SetStderrTTY(stderrIsTTY) + return io +} + +func Test() (*IOStreams, *bytes.Buffer, *bytes.Buffer, *bytes.Buffer) { + in := &bytes.Buffer{} + out := &bytes.Buffer{} + errOut := &bytes.Buffer{} + return &IOStreams{ + In: ioutil.NopCloser(in), + Out: out, + ErrOut: errOut, + }, in, out, errOut +} + +func isTerminal(f *os.File) bool { + return isatty.IsTerminal(f.Fd()) || isatty.IsCygwinTerminal(f.Fd()) +} + +func isCygwinTerminal(w io.Writer) bool { + if f, isFile := w.(*os.File); isFile { + return isatty.IsCygwinTerminal(f.Fd()) + } + return false +} + +func terminalSize(w io.Writer) (int, int, error) { + if f, isFile := w.(*os.File); isFile { + return terminal.GetSize(int(f.Fd())) + } + return 0, 0, fmt.Errorf("%v is not a file", w) +}