diff --git a/src/cmd/scopeProject.go b/src/cmd/scopeProject.go index 91890fc6..0e5f4b66 100644 --- a/src/cmd/scopeProject.go +++ b/src/cmd/scopeProject.go @@ -19,6 +19,7 @@ func scopeProjectCmd() *cmdBuilder.Cmd { Use("project"). Short(i18n.T(i18n.CmdScopeProject)). Arg(cmdBuilder.ProjectArgName, cmdBuilder.OptionalArg()). + HelpFlag(i18n.T(i18n.ScopeProjectHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { projectId, projectSet := cmdData.CliStorage.Data().ScopeProjectId.Get() if projectSet { diff --git a/src/cmd/scopeReset.go b/src/cmd/scopeReset.go index 2efdcddc..e32291e5 100644 --- a/src/cmd/scopeReset.go +++ b/src/cmd/scopeReset.go @@ -14,6 +14,7 @@ func scopeResetCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("reset"). Short(i18n.T(i18n.CmdScopeReset)). + HelpFlag(i18n.T(i18n.ScopeResetHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { _, err := cmdData.CliStorage.Update(func(data cliStorage.Data) cliStorage.Data { data.ScopeProjectId = uuid.ProjectIdNull{} diff --git a/src/cmd/service.go b/src/cmd/service.go index 88bca53a..785a3ef2 100644 --- a/src/cmd/service.go +++ b/src/cmd/service.go @@ -9,7 +9,7 @@ func serviceCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("service"). Short(i18n.T(i18n.CmdService)). - Short(i18n.T(i18n.ServiceHelp)). + HelpFlag(i18n.T(i18n.ServiceHelp)). AddChildrenCmd(serviceDeleteCmd()). AddChildrenCmd(serviceListCmd()). AddChildrenCmd(serviceLogCmd()). diff --git a/src/cmd/serviceDelete.go b/src/cmd/serviceDelete.go index ef2db851..bb960d58 100644 --- a/src/cmd/serviceDelete.go +++ b/src/cmd/serviceDelete.go @@ -16,7 +16,7 @@ func serviceDeleteCmd() *cmdBuilder.Cmd { ScopeLevel(cmdBuilder.Service). Arg(cmdBuilder.ServiceArgName, cmdBuilder.OptionalArg()). BoolFlag("confirm", false, i18n.T(i18n.ConfirmFlag)). - Short(i18n.T(i18n.ServiceDeleteHelp)). + HelpFlag(i18n.T(i18n.ServiceDeleteHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { if !cmdData.Params.GetBool("confirm") { err := uxHelpers.YesNoPromptDestructive(ctx, cmdData.UxBlocks, i18n.T(i18n.ServiceDeleteConfirm, cmdData.Service.Name)) diff --git a/src/cmd/serviceDeploy.go b/src/cmd/serviceDeploy.go index 1f08bbac..9a1e6e0e 100644 --- a/src/cmd/serviceDeploy.go +++ b/src/cmd/serviceDeploy.go @@ -29,7 +29,7 @@ func serviceDeployCmd() *cmdBuilder.Cmd { StringFlag("versionName", "", i18n.T(i18n.BuildVersionName)). StringFlag("zeropsYamlPath", "", i18n.T(i18n.ZeropsYamlLocation)). BoolFlag("deployGitFolder", false, i18n.T(i18n.ZeropsYamlLocation)). - Short(i18n.T(i18n.ServiceDeployHelp)). + HelpFlag(i18n.T(i18n.ServiceDeployHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks diff --git a/src/cmd/serviceList.go b/src/cmd/serviceList.go index 96c2f6f2..df107fb6 100644 --- a/src/cmd/serviceList.go +++ b/src/cmd/serviceList.go @@ -14,7 +14,7 @@ func serviceListCmd() *cmdBuilder.Cmd { Short(i18n.T(i18n.CmdProjectList)). ScopeLevel(cmdBuilder.Project). Arg(cmdBuilder.ProjectArgName, cmdBuilder.OptionalArg()). - Short(i18n.T(i18n.ServiceListHelp)). + HelpFlag(i18n.T(i18n.ServiceListHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { err := uxHelpers.PrintServiceList(ctx, cmdData.UxBlocks, cmdData.RestApiClient, *cmdData.Project) if err != nil { diff --git a/src/cmd/serviceLog.go b/src/cmd/serviceLog.go index 2c72556a..abd616d8 100644 --- a/src/cmd/serviceLog.go +++ b/src/cmd/serviceLog.go @@ -25,7 +25,7 @@ func serviceLogCmd() *cmdBuilder.Cmd { StringFlag("formatTemplate", "", i18n.T(i18n.LogFormatTemplateFlag)). BoolFlag("follow", false, i18n.T(i18n.LogFollowFlag)). BoolFlag("showBuildLogs", false, i18n.T(i18n.LogShowBuildFlag)). - Short(i18n.T(i18n.ServiceLogHelp)). + HelpFlag(i18n.T(i18n.ServiceLogHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { handler := serviceLogs.New( serviceLogs.Config{}, diff --git a/src/cmd/servicePush.go b/src/cmd/servicePush.go index 18579931..b6f94768 100644 --- a/src/cmd/servicePush.go +++ b/src/cmd/servicePush.go @@ -29,7 +29,7 @@ func servicePushCmd() *cmdBuilder.Cmd { StringFlag("source", "", i18n.T(i18n.SourceName)). StringFlag("zeropsYamlPath", "", i18n.T(i18n.ZeropsYamlLocation)). BoolFlag("deployGitFolder", false, i18n.T(i18n.UploadGitFolder)). - Short(i18n.T(i18n.ServicePushHelp)). + HelpFlag(i18n.T(i18n.ServicePushHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks diff --git a/src/cmd/serviceStart.go b/src/cmd/serviceStart.go index da00e65c..da337fe3 100644 --- a/src/cmd/serviceStart.go +++ b/src/cmd/serviceStart.go @@ -15,7 +15,7 @@ func serviceStartCmd() *cmdBuilder.Cmd { Short(i18n.T(i18n.CmdServiceStart)). ScopeLevel(cmdBuilder.Service). Arg(cmdBuilder.ServiceArgName, cmdBuilder.OptionalArg(), cmdBuilder.OptionalArgLabel("{serviceName | serviceId}")). - Short(i18n.T(i18n.ServiceStartHelp)). + HelpFlag(i18n.T(i18n.ServiceStartHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { startServiceResponse, err := cmdData.RestApiClient.PutServiceStackStart( ctx, diff --git a/src/cmd/serviceStop.go b/src/cmd/serviceStop.go index 6e50fe17..015171d9 100644 --- a/src/cmd/serviceStop.go +++ b/src/cmd/serviceStop.go @@ -16,7 +16,7 @@ func serviceStopCmd() *cmdBuilder.Cmd { Short(i18n.T(i18n.CmdServiceStop)). ScopeLevel(cmdBuilder.Service). Arg(cmdBuilder.ServiceArgName, cmdBuilder.OptionalArg()). - Short(i18n.T(i18n.ServiceStopHelp)). + HelpFlag(i18n.T(i18n.ServiceStopHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { stopServiceResponse, err := cmdData.RestApiClient.PutServiceStackStop( ctx, diff --git a/src/cmd/status.go b/src/cmd/status.go index ff547c8e..d38e1ff5 100644 --- a/src/cmd/status.go +++ b/src/cmd/status.go @@ -9,7 +9,7 @@ func statusCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("status"). Short(i18n.T(i18n.CmdStatus)). - Short(i18n.T(i18n.StatusHelp)). + HelpFlag(i18n.T(i18n.StatusHelp)). AddChildrenCmd(statusShowDebugLogsCmd()). AddChildrenCmd(statusInfoCmd()) } diff --git a/src/cmd/statusInfo.go b/src/cmd/statusInfo.go index d26d6e52..2e4094cb 100644 --- a/src/cmd/statusInfo.go +++ b/src/cmd/statusInfo.go @@ -17,7 +17,7 @@ func statusInfoCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("info"). Short(i18n.T(i18n.CmdStatusInfo)). - Short(i18n.T(i18n.StatusInfoHelp)). + HelpFlag(i18n.T(i18n.StatusInfoHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { body := &uxBlock.TableBody{} diff --git a/src/cmd/statusShowDebugLogs.go b/src/cmd/statusShowDebugLogs.go index 70725390..85e06743 100644 --- a/src/cmd/statusShowDebugLogs.go +++ b/src/cmd/statusShowDebugLogs.go @@ -16,7 +16,7 @@ func statusShowDebugLogsCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("show-debug-logs"). Short(i18n.T(i18n.CmdStatusShowDebugLogs)). - Short(i18n.T(i18n.StatusShowDebugLogsHelp)). + HelpFlag(i18n.T(i18n.StatusShowDebugLogsHelp)). GuestRunFunc(func(ctx context.Context, cmdData *cmdBuilder.GuestCmdData) error { logFilePath, err := constants.LogFilePath() if err != nil { diff --git a/src/cmd/version.go b/src/cmd/version.go index cc65075b..b34cc389 100644 --- a/src/cmd/version.go +++ b/src/cmd/version.go @@ -15,7 +15,7 @@ func versionCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("version"). Short(i18n.T(i18n.CmdVersion)). - Short(i18n.T(i18n.VersionHelp)). + HelpFlag(i18n.T(i18n.VersionHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { fmt.Printf("zcli version %s (%s) %s/%s\n", Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) diff --git a/src/cmdBuilder/cmdBuilderBuildCobraCmd.go b/src/cmdBuilder/cmdBuilderBuildCobraCmd.go index b16e5ed8..727706dd 100644 --- a/src/cmdBuilder/cmdBuilderBuildCobraCmd.go +++ b/src/cmdBuilder/cmdBuilderBuildCobraCmd.go @@ -5,10 +5,17 @@ import ( "strings" "github.com/spf13/cobra" + "github.com/zeropsio/zcli/src/cliStorage" "github.com/zeropsio/zcli/src/params" + "github.com/zeropsio/zcli/src/uxBlock" ) -func (b *CmdBuilder) buildCobraCmd(cmd *Cmd, params *params.Handler) (*cobra.Command, error) { +func (b *CmdBuilder) buildCobraCmd( + cmd *Cmd, + params *params.Handler, + uxBlocks uxBlock.UxBlocks, + cliStorage *cliStorage.Handler, +) (*cobra.Command, error) { cobraCmd := &cobra.Command{ Short: cmd.short, SilenceUsage: cmd.silenceUsage, @@ -52,11 +59,11 @@ func (b *CmdBuilder) buildCobraCmd(cmd *Cmd, params *params.Handler) (*cobra.Com } if cmd.guestRunFunc != nil || cmd.loggedUserRunFunc != nil { - cobraCmd.RunE = b.createCmdRunFunc(cmd, params) + cobraCmd.RunE = b.createCmdRunFunc(cmd, params, uxBlocks, cliStorage) } for _, childrenCmd := range cmd.childrenCmds { - cobraChildrenCmd, err := b.buildCobraCmd(childrenCmd, params) + cobraChildrenCmd, err := b.buildCobraCmd(childrenCmd, params, uxBlocks, cliStorage) if err != nil { return nil, err } diff --git a/src/cmdBuilder/cmdBuilderCreateRunFunc.go b/src/cmdBuilder/cmdBuilderCreateRunFunc.go index 8d13b4cf..ac33bf96 100644 --- a/src/cmdBuilder/cmdBuilderCreateRunFunc.go +++ b/src/cmdBuilder/cmdBuilderCreateRunFunc.go @@ -1,30 +1,16 @@ package cmdBuilder import ( - "context" "fmt" - "os" - "os/signal" - "syscall" - "github.com/mattn/go-isatty" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/zeropsio/zcli/src/cliStorage" - "github.com/zeropsio/zcli/src/constants" "github.com/zeropsio/zcli/src/entity" - "github.com/zeropsio/zcli/src/errorsx" "github.com/zeropsio/zcli/src/i18n" - "github.com/zeropsio/zcli/src/logger" "github.com/zeropsio/zcli/src/params" - "github.com/zeropsio/zcli/src/storage" - "github.com/zeropsio/zcli/src/support" "github.com/zeropsio/zcli/src/uxBlock" - "github.com/zeropsio/zcli/src/uxBlock/styles" "github.com/zeropsio/zcli/src/zeropsRestApiClient" - "github.com/zeropsio/zerops-go/apiError" - "golang.org/x/term" - "gopkg.in/yaml.v3" ) type ParamsReader interface { @@ -73,43 +59,19 @@ type LoggedUserCmdData struct { Service *entity.Service } -func (b *CmdBuilder) createCmdRunFunc(cmd *Cmd, params *params.Handler) func(*cobra.Command, []string) error { +func (b *CmdBuilder) createCmdRunFunc( + cmd *Cmd, + params *params.Handler, + uxBlocks uxBlock.UxBlocks, + cliStorage *cliStorage.Handler, +) func(*cobra.Command, []string) error { return func(cobraCmd *cobra.Command, args []string) (err error) { - ctx, cancel := context.WithCancel(context.Background()) - regSignals(cancel) - ctx = support.Context(ctx) - - isTerminal, err := isTerminal() - if err != nil { - return err - } - - width, _, err := term.GetSize(0) - if err != nil { - width = 100 - } - - outputLogger, debugFileLogger := createLoggers(isTerminal) - - uxBlocks := uxBlock.NewBlock(outputLogger, debugFileLogger, isTerminal, width, cancel) + ctx := cobraCmd.Context() uxBlocks.LogDebug(fmt.Sprintf("Command: %s", cobraCmd.CommandPath())) - defer func() { - if err != nil { - printError(err, uxBlocks) - err = errSkipErrorReporting - } - }() - - // TODO - janhajek move somewhere else? params.InitViper() - cliStorage, err := createCliStorage() - if err != nil { - return err - } - argsMap, err := convertArgs(cmd, args) if err != nil { return err @@ -187,82 +149,3 @@ func convertArgs(cmd *Cmd, args []string) (map[string][]string, error) { return argsMap, nil } - -func printError(err error, uxBlocks uxBlock.UxBlocks) { - uxBlocks.LogDebug(fmt.Sprintf("error: %+v", err)) - - if userErr := errorsx.AsUserError(err); userErr != nil { - uxBlocks.PrintError(styles.ErrorLine(err.Error())) - return - } - - var apiErr apiError.Error - if errors.As(err, &apiErr) { - uxBlocks.PrintError(styles.ErrorLine(apiErr.GetMessage())) - if apiErr.GetMeta() != nil { - meta, err := yaml.Marshal(apiErr.GetMeta()) - if err != nil { - uxBlocks.PrintError(styles.ErrorLine(fmt.Sprintf("couldn't parse meta of error: %s", apiErr.GetMessage()))) - } - uxBlocks.PrintError(styles.ErrorLine(string(meta))) - } - - return - } - - uxBlocks.PrintError(styles.ErrorLine(err.Error())) -} - -func isTerminal() (bool, error) { - switch TerminalMode(TerminalFlag) { - case TerminalModeAuto: - return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()), nil - case TerminalModeDisabled: - return false, nil - case TerminalModeEnabled: - return true, nil - default: - return false, errors.New(i18n.T(i18n.UnknownTerminalMode, TerminalFlag)) - } -} - -func createLoggers(isTerminal bool) (*logger.Handler, *logger.Handler) { - outputLogger := logger.NewOutputLogger(logger.OutputConfig{ - IsTerminal: isTerminal, - }) - - loggerFilePath, err := constants.LogFilePath() - if err != nil { - outputLogger.Warning(styles.WarningLine(err.Error())) - } - - debugFileLogger := logger.NewDebugFileLogger(logger.DebugFileConfig{ - FilePath: loggerFilePath, - }) - - return outputLogger, debugFileLogger -} - -func regSignals(contextCancel func()) { - sigs := make(chan os.Signal, 1) - - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - - go func() { - <-sigs - contextCancel() - }() -} - -func createCliStorage() (*cliStorage.Handler, error) { - filePath, err := constants.CliDataFilePath() - if err != nil { - return nil, err - } - s, err := storage.New[cliStorage.Data]( - storage.Config{ - FilePath: filePath, - }, - ) - return &cliStorage.Handler{Handler: s}, err -} diff --git a/src/cmdBuilder/cmdBuilderExecuteRootCmd.go b/src/cmdBuilder/cmdBuilderExecuteRootCmd.go index b4cb3e34..695c1a8d 100644 --- a/src/cmdBuilder/cmdBuilderExecuteRootCmd.go +++ b/src/cmdBuilder/cmdBuilderExecuteRootCmd.go @@ -1,55 +1,72 @@ package cmdBuilder import ( + "context" "fmt" + "os" + "os/signal" + "syscall" + "github.com/mattn/go-isatty" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/zeropsio/zcli/src/cliStorage" + "github.com/zeropsio/zcli/src/constants" + "github.com/zeropsio/zcli/src/errorsx" "github.com/zeropsio/zcli/src/i18n" + "github.com/zeropsio/zcli/src/logger" "github.com/zeropsio/zcli/src/params" + "github.com/zeropsio/zcli/src/storage" + "github.com/zeropsio/zcli/src/support" + "github.com/zeropsio/zcli/src/uxBlock" "github.com/zeropsio/zcli/src/uxBlock/styles" + "github.com/zeropsio/zerops-go/apiError" + "golang.org/x/term" + "gopkg.in/yaml.v3" ) -type TerminalMode string +func (b *CmdBuilder) CreateAndExecuteRootCobraCmd() (err error) { + ctx, cancel := context.WithCancel(context.Background()) + regSignals(cancel) + ctx = support.Context(ctx) -const ( - TerminalModeAuto TerminalMode = "auto" - TerminalModeDisabled TerminalMode = "disabled" - TerminalModeEnabled TerminalMode = "enabled" -) + isTerminal := isTerminal() + + width, _, err := term.GetSize(0) + if err != nil { + width = 100 + } + + outputLogger, debugFileLogger := createLoggers(isTerminal) + + uxBlocks := uxBlock.NewBlock(outputLogger, debugFileLogger, isTerminal, width, cancel) + + defer func() { + if err != nil { + printError(err, uxBlocks) + } + }() + + cliStorage, err := createCliStorage() + if err != nil { + return err + } -var TerminalFlag string - -// Chicken-and-egg problem. -// I would like to log errors at one place after the execution of the root command. -// To do that, I need to know the log file path before the execution. -// To know the log file path, I need to parse the persistent flags. -// But these flags are parsed during the execution of the root command. -// So, I moved the logging inside the root command. -// This way, it logs everything. Except the unknown command error. -// This error needs to be handled here. Simple fmt.Println(err.Error()) is enough. -// But with this line, other errors are logged twice. Once here, once in the root command. -// So, I added a special error to skip the logging after the root command. -var errSkipErrorReporting = errors.New("skipErrorReporting") - -func (b *CmdBuilder) CreateAndExecuteRootCobraCmd() error { rootCmd := createRootCommand() params := params.New() for _, cmd := range b.commands { - cobraCmd, err := b.buildCobraCmd(cmd, params) + cobraCmd, err := b.buildCobraCmd(cmd, params, uxBlocks, cliStorage) if err != nil { return err } rootCmd.AddCommand(cobraCmd) } - err := rootCmd.Execute() + err = rootCmd.ExecuteContext(ctx) if err != nil { - if !errors.Is(err, errSkipErrorReporting) { - fmt.Println(err.Error()) - } + printError(err, uxBlocks) } return nil @@ -62,8 +79,6 @@ func createRootCommand() *cobra.Command { SilenceErrors: true, } - rootCmd.PersistentFlags().StringVar(&TerminalFlag, "terminal", "auto", i18n.T(i18n.TerminalFlag)) - rootCmd.SetHelpTemplate(`` + styles.CobraSectionColor().SetString("Usage:").String() + `{{if .Runnable}} {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} @@ -92,8 +107,104 @@ func createRootCommand() *cobra.Command { ` + styles.CobraSectionColor().SetString("Additional help topics:").String() + `{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} ` + styles.CobraItemNameColor().SetString("{{rpad .CommandPath .CommandPathPadding}}").String() + ` {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} +` + styles.CobraSectionColor().SetString("Global Env Variables:").String() + ` + ` + styles.CobraItemNameColor().SetString(constants.CliLogFilePathEnvVar).String() + ` ` + i18n.T(i18n.CliLogFilePathEnvVar) + ` + ` + styles.CobraItemNameColor().SetString(constants.CliDataFilePathEnvVar).String() + ` ` + i18n.T(i18n.CliDataFilePathEnvVar) + ` + ` + styles.CobraItemNameColor().SetString(constants.CliTerminalMode).String() + ` ` + i18n.T(i18n.CliTerminalModeEnvVar) + ` + Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} `) return rootCmd } + +func printError(err error, uxBlocks uxBlock.UxBlocks) { + uxBlocks.LogDebug(fmt.Sprintf("error: %+v", err)) + + if userErr := errorsx.AsUserError(err); userErr != nil { + uxBlocks.PrintError(styles.ErrorLine(err.Error())) + return + } + + var apiErr apiError.Error + if errors.As(err, &apiErr) { + uxBlocks.PrintError(styles.ErrorLine(apiErr.GetMessage())) + if apiErr.GetMeta() != nil { + meta, err := yaml.Marshal(apiErr.GetMeta()) + if err != nil { + uxBlocks.PrintError(styles.ErrorLine(fmt.Sprintf("couldn't parse meta of error: %s", apiErr.GetMessage()))) + } + uxBlocks.PrintError(styles.ErrorLine(string(meta))) + } + + return + } + + uxBlocks.PrintError(styles.ErrorLine(err.Error())) +} + +type terminalMode string + +const ( + TerminalModeAuto terminalMode = "auto" + TerminalModeDisabled terminalMode = "disabled" + TerminalModeEnabled terminalMode = "enabled" +) + +func isTerminal() bool { + env := os.Getenv(constants.CliTerminalMode) + + switch terminalMode(env) { + case TerminalModeAuto, "": + return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) + case TerminalModeDisabled: + return false + case TerminalModeEnabled: + return true + default: + os.Stdout.WriteString(styles.WarningLine(i18n.T(i18n.UnknownTerminalMode, env)).String()) + + return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) + } +} + +func createLoggers(isTerminal bool) (*logger.Handler, *logger.Handler) { + outputLogger := logger.NewOutputLogger(logger.OutputConfig{ + IsTerminal: isTerminal, + }) + + loggerFilePath, err := constants.LogFilePath() + if err != nil { + outputLogger.Warning(styles.WarningLine(err.Error())) + } + + debugFileLogger := logger.NewDebugFileLogger(logger.DebugFileConfig{ + FilePath: loggerFilePath, + }) + + return outputLogger, debugFileLogger +} + +func regSignals(contextCancel func()) { + sigs := make(chan os.Signal, 1) + + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + go func() { + <-sigs + contextCancel() + }() +} + +func createCliStorage() (*cliStorage.Handler, error) { + filePath, err := constants.CliDataFilePath() + if err != nil { + return nil, err + } + s, err := storage.New[cliStorage.Data]( + storage.Config{ + FilePath: filePath, + }, + ) + return &cliStorage.Handler{Handler: s}, err +} diff --git a/src/constants/darwin.go b/src/constants/darwin.go index 76bfe572..09c45ca3 100644 --- a/src/constants/darwin.go +++ b/src/constants/darwin.go @@ -10,17 +10,17 @@ import ( func getDataFilePathsReceivers() []pathReceiver { return []pathReceiver{ - receiverFromEnv(cliDataFilePathEnvVar), - receiverFromOsFunc(os.UserConfigDir, zeropsDir, cliDataFileName), - receiverFromOsFunc(os.UserHomeDir, zeropsDir, cliDataFileName), + receiverFromEnv(CliDataFilePathEnvVar), + receiverFromOsFunc(os.UserConfigDir, ZeropsDir, CliDataFileName), + receiverFromOsFunc(os.UserHomeDir, ZeropsDir, CliDataFileName), } } func getLogFilePathReceivers() []pathReceiver { return []pathReceiver{ - receiverFromEnv(cliLogFilePathEnvVar), - receiverFromPath(path.Join("/usr/local/var/log/", zeropsLogFile)), - receiverFromOsFunc(os.UserConfigDir, zeropsDir, zeropsLogFile), - receiverFromOsFunc(os.UserHomeDir, zeropsDir, zeropsLogFile), + receiverFromEnv(CliLogFilePathEnvVar), + receiverFromPath(path.Join("/usr/local/var/log/", ZeropsLogFile)), + receiverFromOsFunc(os.UserConfigDir, ZeropsDir, ZeropsLogFile), + receiverFromOsFunc(os.UserHomeDir, ZeropsDir, ZeropsLogFile), } } diff --git a/src/constants/linux.go b/src/constants/linux.go index 7836fb3d..04f19886 100644 --- a/src/constants/linux.go +++ b/src/constants/linux.go @@ -10,17 +10,17 @@ import ( func getDataFilePathsReceivers() []pathReceiver { return []pathReceiver{ - receiverFromEnv(cliDataFilePathEnvVar), - receiverFromOsFunc(os.UserConfigDir, zeropsDir, cliDataFileName), - receiverFromOsFunc(os.UserHomeDir, zeropsDir, cliDataFileName), + receiverFromEnv(CliDataFilePathEnvVar), + receiverFromOsFunc(os.UserConfigDir, ZeropsDir, CliDataFileName), + receiverFromOsFunc(os.UserHomeDir, ZeropsDir, CliDataFileName), } } func getLogFilePathReceivers() []pathReceiver { return []pathReceiver{ - receiverFromEnv(cliLogFilePathEnvVar), - receiverFromPath(path.Join("/var/log/", zeropsLogFile)), - receiverFromOsFunc(os.UserConfigDir, zeropsDir, zeropsLogFile), - receiverFromOsFunc(os.UserHomeDir, zeropsDir, zeropsLogFile), + receiverFromEnv(CliLogFilePathEnvVar), + receiverFromPath(path.Join("/var/log/", ZeropsLogFile)), + receiverFromOsFunc(os.UserConfigDir, ZeropsDir, ZeropsLogFile), + receiverFromOsFunc(os.UserHomeDir, ZeropsDir, ZeropsLogFile), } } diff --git a/src/constants/windows.go b/src/constants/windows.go index 9c20367e..d501bcca 100644 --- a/src/constants/windows.go +++ b/src/constants/windows.go @@ -8,21 +8,21 @@ import ( ) // this is here to make linter happy -var _ = zeropsDir +var _ = ZeropsDir var _ = receiverFromPath func getDataFilePathsReceivers() []pathReceiver { return []pathReceiver{ - receiverFromEnv(cliDataFilePathEnvVar), - receiverFromOsFunc(os.UserConfigDir, "Zerops", cliDataFileName), - receiverFromOsFunc(os.UserHomeDir, "Zerops", cliDataFileName), + receiverFromEnv(CliDataFilePathEnvVar), + receiverFromOsFunc(os.UserConfigDir, "Zerops", CliDataFileName), + receiverFromOsFunc(os.UserHomeDir, "Zerops", CliDataFileName), } } func getLogFilePathReceivers() []pathReceiver { return []pathReceiver{ - receiverFromEnv(cliLogFilePathEnvVar), - receiverFromOsFunc(os.UserConfigDir, "Zerops", zeropsLogFile), - receiverFromOsFunc(os.UserHomeDir, "Zerops", zeropsLogFile), + receiverFromEnv(CliLogFilePathEnvVar), + receiverFromOsFunc(os.UserConfigDir, "Zerops", ZeropsLogFile), + receiverFromOsFunc(os.UserHomeDir, "Zerops", ZeropsLogFile), } } diff --git a/src/constants/zerops.go b/src/constants/zerops.go index f644c590..78b40206 100644 --- a/src/constants/zerops.go +++ b/src/constants/zerops.go @@ -12,11 +12,12 @@ import ( const ( DefaultRegionUrl = "https://api.app.zerops.io/api/rest/public/region/zcli" - zeropsDir = "zerops" - zeropsLogFile = "zerops.log" - cliDataFileName = "cli.data" - cliDataFilePathEnvVar = "ZEROPS_CLI_DATA_FILE_PATH" - cliLogFilePathEnvVar = "ZEROPS_CLI_LOG_FILE_PATH" + ZeropsDir = "zerops" + ZeropsLogFile = "zerops.log" + CliDataFileName = "cli.data" + CliDataFilePathEnvVar = "ZEROPS_CLI_DATA_FILE_PATH" + CliLogFilePathEnvVar = "ZEROPS_CLI_LOG_FILE_PATH" + CliTerminalMode = "ZEROPS_CLI_TERMINAL_MODE" ) type pathReceiver func() (path string, err error) @@ -30,7 +31,7 @@ func CliDataFilePath() (string, error) { _, err := p() paths = append(paths, err.Error()) } - return "", errors.New(i18n.T(i18n.UnableToWriteCliData, "\n"+strings.Join(paths, "\n"))) + return "", errors.New(i18n.T(i18n.UnableToWriteCliData, "\n"+strings.Join(paths, "\n")+"\n")) } return path, nil } @@ -44,7 +45,7 @@ func LogFilePath() (string, error) { _, err := p() paths = append(paths, err.Error()) } - return "", errors.New(i18n.T(i18n.UnableToWriteLogFile, "\n"+strings.Join(paths, "\n"))) + return "", errors.New(i18n.T(i18n.UnableToWriteLogFile, "\n"+strings.Join(paths, "\n")+"\n")) } return path, nil } diff --git a/src/i18n/en.go b/src/i18n/en.go index 4265068c..b1ba433f 100644 --- a/src/i18n/en.go +++ b/src/i18n/en.go @@ -92,8 +92,6 @@ var en = map[string]string{ LogFollowFlag: "If set, zCLI will continuously poll for new log messages. By default, the command will exit\nonce there are no more logs to display. To exit from this mode, use Control-C.", LogFormatFlag: "The format of returned log messages. Following formats are supported: \nFULL: This is the default format. Messages will be returned in the complete Syslog format. \nSHORT: Returns only timestamp and log message.\nJSON: Messages will be returned as one JSON object.\nJSONSTREAM: Messages will be returned as stream of JSON objects.", LogFormatTemplateFlag: "Set a custom log format. Can be used only with --format=FULL.\nExample: --formatTemplate=\"{{.timestamp}} {{.severity}} {{.facility}} {{.message}}\".\nSupports standard GoLang template format and functions.", - TerminalFlag: "If enabled provides a rich UI to communicate with a user. Possible values: auto, enabled, disabled. Default value is auto.", - LogFilePathFlag: "Path to a log file. Default value: %s.", ConfirmFlag: "If set, zCLI will not ask for confirmation of destructive operations.", ServiceIdFlag: "If you have access to more than one service, you must specify the service ID for which the\ncommand is to be executed.", ProjectIdFlag: "If you have access to more than one project, you must specify the project ID for which the\ncommand is to be executed.", @@ -219,7 +217,11 @@ var en = map[string]string{ // global // //////////// - UnknownTerminalMode: "Unknown terminal mode: %s", + CliTerminalModeEnvVar: "If enabled provides a rich UI to communicate with a user. Possible values: auto, enabled, disabled. Default value is auto.", + CliLogFilePathEnvVar: "Path to a log file.", + CliDataFilePathEnvVar: "Path to data file.", + + UnknownTerminalMode: "Unknown terminal mode: %s. Falling back to auto-discovery. Possible values: auto, enabled, disabled.", UnableToDecodeJsonFile: "Unable to decode json file: %s", UnableToWriteCliData: "Unable to write zcli data, paths tested: %s", UnableToWriteLogFile: "Unable to write zcli debug log file, paths tested: %s", diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go index 9415cf17..08db437a 100644 --- a/src/i18n/i18n.go +++ b/src/i18n/i18n.go @@ -105,8 +105,6 @@ const ( LogShowBuildFlag = "LogShowBuildFlag" LogFormatFlag = "LogFormatFlag" LogFormatTemplateFlag = "LogFormatTemplateFlag" - TerminalFlag = "TerminalFlag" - LogFilePathFlag = "LogFilePathFlag" ConfirmFlag = "ConfirmFlag" ServiceIdFlag = "ServiceIdFlag" ProjectIdFlag = "ProjectIdFlag" @@ -256,6 +254,9 @@ const ( //////////// // global // //////////// + CliTerminalModeEnvVar = "TerminalModeEnv" + CliLogFilePathEnvVar = "CliLogFilePathEnvVar" + CliDataFilePathEnvVar = "CliDataFilePathEnvVar" UnknownTerminalMode = "UnknownTerminalMode" UnableToDecodeJsonFile = "UnableToDecodeJsonFile"