From 668308a5287606dda53a0dbfee71504cf2958ad4 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sun, 15 Jan 2023 01:03:08 +0100 Subject: [PATCH] Add doctor command, minor changes --- README.md | 2 - cmd/cli/main.go | 63 +++++++-------- cmd/collect/main.go | 4 +- cmd/control/cmd/doctor.go | 151 +++++++++++++++++++++++++++++++++++ cmd/control/cmd/root.go | 11 ++- cmd/control/cmd/update.go | 4 +- cmd/control/cmd/version.go | 2 + cmd/install-utils/main.go | 4 +- cmd/install-utils/migrate.go | 30 +++---- cmd/postcollect/main.go | 4 +- internal/check/check.go | 90 +++++++++++++++++++++ internal/collect/collect.go | 8 +- internal/device/device.go | 15 ++-- internal/output/output.go | 64 ++++++++++++--- scripts/install.sh | 9 ++- 15 files changed, 376 insertions(+), 85 deletions(-) create mode 100644 cmd/control/cmd/doctor.go create mode 100644 internal/check/check.go diff --git a/README.md b/README.md index ca859d0..4c95acf 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,6 @@ Context-based replacement/enhancement for zsh and bash shell history ### Prerequisites -Nohup - Standard stuff: `bash(4.3+)`, `curl`, `tar`, ... MacOS: `coreutils` (`brew install coreutils`) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index dccfc18..6433d9d 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -62,34 +62,31 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) { pwd := flags.String("pwd", "", "$PWD - present working directory") gitOriginRemote := flags.String("git-origin-remote", "<<>>", "> git origin remote") query := flags.String("query", "", "Search query") - // TODO: Do we still need this? - testHistory := flags.String("test-history", "", "Load history from a file instead from the daemon (for testing purposes only!)") - testHistoryLines := flags.Int("test-lines", 0, "The number of lines to load from a file passed with --test-history (for testing purposes only!)") flags.Parse(args) // TODO: These errors should tell the user that they should not be running the command directly errMsg := "Failed to get required command-line arguments" if *sessionID == "" { - out.Fatal(errMsg, errors.New("missing required option --session-id")) + out.FatalE(errMsg, errors.New("missing required option --session-id")) } if *pwd == "" { - out.Fatal(errMsg, errors.New("missing required option --pwd")) + out.FatalE(errMsg, errors.New("missing required option --pwd")) } if *gitOriginRemote == "<<>>" { - out.Fatal(errMsg, errors.New("missing required option --git-origin-remote")) + out.FatalE(errMsg, errors.New("missing required option --git-origin-remote")) } dataDir, err := datadir.GetPath() if err != nil { - out.Fatal("Could not get user data directory", err) + out.FatalE("Could not get user data directory", err) } deviceName, err := device.GetName(dataDir) if err != nil { - out.Fatal("Could not get device name", err) + out.FatalE("Could not get device name", err) } g, err := gocui.NewGui(gocui.OutputNormal, false) if err != nil { - out.Fatal("Failed to launch TUI", err) + out.FatalE("Failed to launch TUI", err) } defer g.Close() @@ -99,15 +96,11 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) { g.Highlight = true var resp msg.CliResponse - if *testHistory == "" { - mess := msg.CliMsg{ - SessionID: *sessionID, - PWD: *pwd, - } - resp = SendCliMsg(out, mess, strconv.Itoa(config.Port)) - } else { - resp = searchapp.LoadHistoryFromFile(out.Logger.Sugar(), *testHistory, *testHistoryLines) + mess := msg.CliMsg{ + SessionID: *sessionID, + PWD: *pwd, } + resp = SendCliMsg(out, mess, strconv.Itoa(config.Port)) st := state{ // lock sync.Mutex @@ -129,46 +122,46 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) { errMsg = "Failed to set keybindings" if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, layout.Next); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, layout.Next); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyCtrlN, gocui.ModNone, layout.Next); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, layout.Prev); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyCtrlP, gocui.ModNone, layout.Prev); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, layout.SelectPaste); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, layout.SelectExecute); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyCtrlG, gocui.ModNone, layout.AbortPaste); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, quit); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } if err := g.SetKeybinding("", gocui.KeyCtrlR, gocui.ModNone, layout.SwitchModes); err != nil { - out.Fatal(errMsg, err) + out.FatalE(errMsg, err) } layout.UpdateData(*query) layout.UpdateRawData(*query) err = g.MainLoop() if err != nil && !errors.Is(err, gocui.ErrQuit) { - out.Fatal("Main application loop finished with error", err) + out.FatalE("Main application loop finished with error", err) } return layout.s.output, layout.s.exitCode } @@ -393,7 +386,7 @@ func (m manager) Layout(g *gocui.Gui) error { v, err := g.SetView("input", 0, 0, maxX-1, 2, b) if err != nil && !errors.Is(err, gocui.ErrUnknownView) { - m.out.Fatal("Failed to set view 'input'", err) + m.out.FatalE("Failed to set view 'input'", err) } v.Editable = true @@ -416,7 +409,7 @@ func (m manager) Layout(g *gocui.Gui) error { v, err = g.SetView("body", 0, 2, maxX-1, maxY, b) if err != nil && !errors.Is(err, gocui.ErrUnknownView) { - m.out.Fatal("Failed to set view 'body'", err) + m.out.FatalE("Failed to set view 'body'", err) } v.Frame = false v.Autoscroll = false @@ -579,7 +572,7 @@ func SendCliMsg(out *output.Output, m msg.CliMsg, port string) msg.CliResponse { sugar := out.Logger.Sugar() recJSON, err := json.Marshal(m) if err != nil { - out.Fatal("Failed to marshal message", err) + out.FatalE("Failed to marshal message", err) } req, err := http.NewRequest( @@ -587,7 +580,7 @@ func SendCliMsg(out *output.Output, m msg.CliMsg, port string) msg.CliResponse { "http://localhost:"+port+"/dump", bytes.NewBuffer(recJSON)) if err != nil { - out.Fatal("Failed to build request", err) + out.FatalE("Failed to build request", err) } req.Header.Set("Content-Type", "application/json") @@ -602,13 +595,13 @@ func SendCliMsg(out *output.Output, m msg.CliMsg, port string) msg.CliResponse { defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - out.Fatal("Failed read response", err) + out.FatalE("Failed read response", err) } // sugar.Println(string(body)) response := msg.CliResponse{} err = json.Unmarshal(body, &response) if err != nil { - out.Fatal("Failed decode response", err) + out.FatalE("Failed decode response", err) } sugar.Debugw("Received records from daemon", "recordCount", len(response.Records), diff --git a/cmd/collect/main.go b/cmd/collect/main.go index 0294958..cd8bb2f 100644 --- a/cmd/collect/main.go +++ b/cmd/collect/main.go @@ -52,12 +52,12 @@ func main() { time, err := strconv.ParseFloat(*timeStr, 64) if err != nil { - out.Fatal("Error while parsing flag --time", err) + out.FatalE("Error while parsing flag --time", err) } realPwd, err := filepath.EvalSymlinks(*pwd) if err != nil { - out.Error("Error while evaluating symlinks in PWD", err) + out.ErrorE("Error while evaluating symlinks in PWD", err) realPwd = "" } diff --git a/cmd/control/cmd/doctor.go b/cmd/control/cmd/doctor.go new file mode 100644 index 0000000..5d683e3 --- /dev/null +++ b/cmd/control/cmd/doctor.go @@ -0,0 +1,151 @@ +package cmd + +import ( + "fmt" + "os" + "os/exec" + "time" + + "github.com/curusarn/resh/internal/cfg" + "github.com/curusarn/resh/internal/check" + "github.com/curusarn/resh/internal/msg" + "github.com/curusarn/resh/internal/status" + "github.com/spf13/cobra" + "go.uber.org/zap" +) + +func doctorCmdFunc(config cfg.Config) func(*cobra.Command, []string) { + return func(cmd *cobra.Command, args []string) { + allOK := true + if !checkDaemon(config) { + allOK = false + printDivider() + } + if !checkShellSession() { + allOK = false + printDivider() + } + if !checkShells() { + allOK = false + printDivider() + } + + if allOK { + out.Info("Everything looks good.") + } + } +} + +func printDivider() { + fmt.Printf("\n") +} + +var msgFailedDaemonStart = `Failed to start RESH daemon. + + -> Start RESH daemon manually - run: resh-daemon-start + -> Or restart this terminal window to bring RESH daemon back up + -> You can check logs: ~/.local/share/resh/log.json (or ~/$XDG_DATA_HOME/resh/log.json) + -> You can create an issue at: https://github.com/curusarn/resh/issues + +` + +func checkDaemon(config cfg.Config) bool { + ok := true + resp, err := status.GetDaemonStatus(config.Port) + if err != nil { + out.InfoE("RESH Daemon is not running", err) + out.Info("Attempting to start RESH daemon ...") + resp, err = startDaemon(config.Port, 5, 200*time.Millisecond) + if err != nil { + out.InfoE(msgFailedDaemonStart, err) + return false + } + ok = false + out.Info("Successfully started daemon.") + } + if version != resp.Version { + out.InfoDaemonVersionMismatch(version, resp.Version) + return false + } + return ok +} + +func startDaemon(port int, maxRetries int, backoff time.Duration) (*msg.StatusResponse, error) { + err := exec.Command("resh-daemon-start").Run() + if err != nil { + return nil, err + } + var resp *msg.StatusResponse + retry := 0 + for { + time.Sleep(backoff) + resp, err = status.GetDaemonStatus(port) + if err == nil { + break + } + if retry == maxRetries { + return nil, err + } + out.Logger.Error("Failed to get daemon status - retrying", zap.Error(err), zap.Int("retry", retry)) + retry++ + continue + } + return resp, nil +} + +var msgShellFilesNotLoaded = `RESH shell files were not properly loaded in this terminal. + + -> Try restarting this terminal to see if the issue persists + -> Check your shell rc files (e.g. .zshrc, .bashrc, ...) + -> You can create an issue at: https://github.com/curusarn/resh/issues + +` + +func checkShellSession() bool { + versionEnv, found := os.LookupEnv("__RESH_VERSION") + if !found { + out.Info(msgShellFilesNotLoaded) + return false + } + if version != versionEnv { + out.InfoTerminalVersionMismatch(version, versionEnv) + return false + } + return true +} + +func checkShells() bool { + allOK := true + + msg, err := check.LoginShell() + if err != nil { + out.InfoE("Failed to get login shell", err) + allOK = false + } + if msg != "" { + out.Info(msg) + allOK = false + } + + msg, err = check.ZshVersion() + if err != nil { + out.InfoE("Failed to check zsh version", err) + allOK = false + } + if msg != "" { + out.Info(msg) + allOK = false + } + + msg, err = check.BashVersion() + if err != nil { + out.InfoE("Failed to check bash version", err) + allOK = false + } + if msg != "" { + out.Info(msg) + allOK = false + } + + return allOK +} diff --git a/cmd/control/cmd/root.go b/cmd/control/cmd/root.go index 3c8cd45..cf567c8 100644 --- a/cmd/control/cmd/root.go +++ b/cmd/control/cmd/root.go @@ -33,7 +33,7 @@ func Execute(ver, com, development string) { defer logger.Sync() // flushes buffer, if any out = output.New(logger, "ERROR") if errCfg != nil { - out.Error("Error while getting configuration", errCfg) + out.ErrorE("Error while getting configuration", errCfg) } var versionCmd = cobra.Command{ @@ -43,10 +43,17 @@ func Execute(ver, com, development string) { } rootCmd.AddCommand(&versionCmd) + doctorCmd := cobra.Command{ + Use: "doctor", + Short: "check common problems", + Run: doctorCmdFunc(config), + } + rootCmd.AddCommand(&doctorCmd) + updateCmd.Flags().BoolVar(&betaFlag, "beta", false, "Update to latest version even if it's beta.") rootCmd.AddCommand(updateCmd) if err := rootCmd.Execute(); err != nil { - out.Fatal("Command ended with error", err) + out.FatalE("Command ended with error", err) } } diff --git a/cmd/control/cmd/update.go b/cmd/control/cmd/update.go index 6263656..c74c555 100644 --- a/cmd/control/cmd/update.go +++ b/cmd/control/cmd/update.go @@ -15,7 +15,7 @@ var updateCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { homeDir, err := os.UserHomeDir() if err != nil { - out.Fatal("Could not get user home dir", err) + out.FatalE("Could not get user home dir", err) } rawinstallPath := filepath.Join(homeDir, ".resh/rawinstall.sh") execArgs := []string{rawinstallPath} @@ -27,7 +27,7 @@ var updateCmd = &cobra.Command{ execCmd.Stderr = os.Stderr err = execCmd.Run() if err != nil { - out.Fatal("Update ended with error", err) + out.FatalE("Update ended with error", err) } }, } diff --git a/cmd/control/cmd/version.go b/cmd/control/cmd/version.go index b7e9077..11f884f 100644 --- a/cmd/control/cmd/version.go +++ b/cmd/control/cmd/version.go @@ -11,6 +11,7 @@ import ( func versionCmdFunc(config cfg.Config) func(*cobra.Command, []string) { return func(cmd *cobra.Command, args []string) { + fmt.Printf("Installed: %s\n", version) versionEnv := getEnvVarWithDefault("__RESH_VERSION", "") @@ -18,6 +19,7 @@ func versionCmdFunc(config cfg.Config) func(*cobra.Command, []string) { resp, err := status.GetDaemonStatus(config.Port) if err != nil { + fmt.Printf("Running checks: %s\n", version) out.ErrorDaemonNotRunning(err) return } diff --git a/cmd/install-utils/main.go b/cmd/install-utils/main.go index 7f81f6e..7a5db51 100644 --- a/cmd/install-utils/main.go +++ b/cmd/install-utils/main.go @@ -33,7 +33,7 @@ func main() { out := output.New(logger, "install-utils ERROR") if len(os.Args) < 2 { - out.ErrorWOErr("ERROR: Not enough arguments\n") + out.Error("ERROR: Not enough arguments\n") printUsage(os.Stderr) os.Exit(1) } @@ -52,7 +52,7 @@ func main() { case "help": printUsage(os.Stdout) default: - out.ErrorWOErr(fmt.Sprintf("ERROR: Unknown command: %s\n", command)) + out.Error(fmt.Sprintf("ERROR: Unknown command: %s\n", command)) printUsage(os.Stderr) os.Exit(1) } diff --git a/cmd/install-utils/migrate.go b/cmd/install-utils/migrate.go index 5154cd8..d2eb2da 100644 --- a/cmd/install-utils/migrate.go +++ b/cmd/install-utils/migrate.go @@ -15,11 +15,11 @@ import ( func migrateConfig(out *output.Output) { err := cfg.Touch() if err != nil { - out.Fatal("ERROR: Failed to touch config file", err) + out.FatalE("ERROR: Failed to touch config file", err) } changes, err := cfg.Migrate() if err != nil { - out.Fatal("ERROR: Failed to update config file", err) + out.FatalE("ERROR: Failed to update config file", err) } if changes { out.Info("RESH config file format has changed since last update - your config was updated to reflect the changes.") @@ -36,14 +36,14 @@ func migrateHistory(out *output.Output) { func migrateHistoryLocation(out *output.Output) { dataDir, err := datadir.MakePath() if err != nil { - out.Fatal("ERROR: Failed to get data directory", err) + out.FatalE("ERROR: Failed to get data directory", err) } // TODO: de-hardcode this historyPath := path.Join(dataDir, "resh/history.reshjson") exists, err := futil.FileExists(historyPath) if err != nil { - out.Fatal("ERROR: Failed to check history file", err) + out.FatalE("ERROR: Failed to check history file", err) } if exists { out.Info(fmt.Sprintf("Found history file in '%s'", historyPath)) @@ -52,7 +52,7 @@ func migrateHistoryLocation(out *output.Output) { homeDir, err := os.UserHomeDir() if err != nil { - out.Fatal("ERROR: Failed to get user home directory", err) + out.FatalE("ERROR: Failed to get user home directory", err) } legacyHistoryPaths := []string{ @@ -62,13 +62,13 @@ func migrateHistoryLocation(out *output.Output) { for _, path := range legacyHistoryPaths { exists, err = futil.FileExists(path) if err != nil { - out.Fatal("ERROR: Failed to check legacy history file", err) + out.FatalE("ERROR: Failed to check legacy history file", err) } if exists { out.Info(fmt.Sprintf("Copying history file to new location: '%s' -> '%s' ...", path, historyPath)) err = futil.CopyFile(path, historyPath) if err != nil { - out.Fatal("ERROR: Failed to copy history file", err) + out.FatalE("ERROR: Failed to copy history file", err) } out.Info("History file copied successfully") return @@ -79,7 +79,7 @@ func migrateHistoryLocation(out *output.Output) { func migrateHistoryFormat(out *output.Output) { dataDir, err := datadir.MakePath() if err != nil { - out.Fatal("ERROR: Could not get user data directory", err) + out.FatalE("ERROR: Could not get user data directory", err) } // TODO: de-hardcode this historyPath := path.Join(dataDir, "history.reshjson") @@ -87,35 +87,35 @@ func migrateHistoryFormat(out *output.Output) { exists, err := futil.FileExists(historyPath) if err != nil { - out.Fatal("ERROR: Failed to check existence of history file", err) + out.FatalE("ERROR: Failed to check existence of history file", err) } if !exists { - out.ErrorWOErr("There is no history file - this is normal if you are installing RESH for the first time on this device") + out.Error("There is no history file - this is normal if you are installing RESH for the first time on this device") err = futil.TouchFile(historyPath) if err != nil { - out.Fatal("ERROR: Failed to touch history file", err) + out.FatalE("ERROR: Failed to touch history file", err) } os.Exit(0) } err = futil.CopyFile(historyPath, historyPathBak) if err != nil { - out.Fatal("ERROR: Could not back up history file", err) + out.FatalE("ERROR: Could not back up history file", err) } rio := recio.New(out.Logger.Sugar()) recs, err := rio.ReadAndFixFile(historyPath, 3) if err != nil { - out.Fatal("ERROR: Could not load history file", err) + out.FatalE("ERROR: Could not load history file", err) } err = rio.OverwriteFile(historyPath, recs) if err != nil { - out.Error("ERROR: Could not update format of history file", err) + out.ErrorE("ERROR: Could not update format of history file", err) err = futil.CopyFile(historyPathBak, historyPath) if err != nil { - out.Fatal("ERROR: Could not restore history file from backup!", err) + out.FatalE("ERROR: Could not restore history file from backup!", err) // TODO: history restoration tutorial } out.Info("History file was restored to the original form") diff --git a/cmd/postcollect/main.go b/cmd/postcollect/main.go index 29067fd..e67e93b 100644 --- a/cmd/postcollect/main.go +++ b/cmd/postcollect/main.go @@ -47,11 +47,11 @@ func main() { timeAfter, err := strconv.ParseFloat(*rta, 64) if err != nil { - out.Fatal("Error while parsing flag --time-after", err) + out.FatalE("Error while parsing flag --time-after", err) } timeBefore, err := strconv.ParseFloat(*rtb, 64) if err != nil { - out.Fatal("Error while parsing flag --time-before", err) + out.FatalE("Error while parsing flag --time-before", err) } duration := timeAfter - timeBefore diff --git a/internal/check/check.go b/internal/check/check.go new file mode 100644 index 0000000..997b995 --- /dev/null +++ b/internal/check/check.go @@ -0,0 +1,90 @@ +package check + +import ( + "fmt" + "os" + "os/exec" + "strconv" + "strings" +) + +func LoginShell() (string, error) { + shellPath, found := os.LookupEnv("SHELL") + if !found { + return "", fmt.Errorf("env variable $SHELL is not set") + } + parts := strings.Split(shellPath, "/") + shell := parts[len(parts)-1] + if shell != "bash" && shell != "zsh" { + return fmt.Sprintf("Current shell (%s) is unsupported\n", shell), nil + } + return "", nil +} + +func msgShellVersion(shell, expectedVer, actualVer string) string { + return fmt.Sprintf(`Minimal supported %s version is %s. You have %s. + + -> Update your %s if you want to use RESH with it. +`, shell, expectedVer, actualVer, shell) +} + +func BashVersion() (string, error) { + out, err := exec.Command("bash", "-c", "echo $BASH_VERSION").Output() + if err != nil { + return "", fmt.Errorf("command failed: %w", err) + } + verStr := strings.TrimSuffix(string(out), "\n") + ver, err := parseVersion(verStr) + if err != nil { + return "", fmt.Errorf("failed to parse version: %w", err) + } + + if ver.Major < 4 || (ver.Major == 4 && ver.Minor < 3) { + return msgShellVersion("bash", "4.3", verStr), nil + } + return "", nil +} + +func ZshVersion() (string, error) { + out, err := exec.Command("zsh", "-c", "echo $ZSH_VERSION").Output() + if err != nil { + return "", fmt.Errorf("command failed: %w", err) + } + verStr := strings.TrimSuffix(string(out), "\n") + ver, err := parseVersion(string(out)) + if err != nil { + return "", fmt.Errorf("failed to parse version: %w", err) + } + + if ver.Major < 5 { + return msgShellVersion("zsh", "5.0", verStr), nil + } + return "", nil +} + +type version struct { + Major int + Minor int + Rest string +} + +func parseVersion(str string) (version, error) { + parts := strings.SplitN(str, ".", 3) + if len(parts) < 3 { + return version{}, fmt.Errorf("not enough parts") + } + major, err := strconv.Atoi(parts[0]) + if err != nil { + return version{}, fmt.Errorf("failed to parse major version: %w", err) + } + minor, err := strconv.Atoi(parts[1]) + if err != nil { + return version{}, fmt.Errorf("failed to parse minor version: %w", err) + } + ver := version{ + Major: major, + Minor: minor, + Rest: parts[2], + } + return ver, nil +} diff --git a/internal/collect/collect.go b/internal/collect/collect.go index 45a619b..1d215cf 100644 --- a/internal/collect/collect.go +++ b/internal/collect/collect.go @@ -23,13 +23,13 @@ func SendRecord(out *output.Output, r recordint.Collect, port, path string) { ) recJSON, err := json.Marshal(r) if err != nil { - out.Fatal("Error while encoding record", err) + out.FatalE("Error while encoding record", err) } req, err := http.NewRequest("POST", "http://localhost:"+port+path, bytes.NewBuffer(recJSON)) if err != nil { - out.Fatal("Error while sending record", err) + out.FatalE("Error while sending record", err) } req.Header.Set("Content-Type", "application/json") @@ -50,13 +50,13 @@ func SendSessionInit(out *output.Output, r recordint.SessionInit, port string) { ) recJSON, err := json.Marshal(r) if err != nil { - out.Fatal("Error while encoding record", err) + out.FatalE("Error while encoding record", err) } req, err := http.NewRequest("POST", "http://localhost:"+port+"/session_init", bytes.NewBuffer(recJSON)) if err != nil { - out.Fatal("Error while sending record", err) + out.FatalE("Error while sending record", err) } req.Header.Set("Content-Type", "application/json") diff --git a/internal/device/device.go b/internal/device/device.go index 3bc49bf..1e9fdab 100644 --- a/internal/device/device.go +++ b/internal/device/device.go @@ -2,6 +2,7 @@ package device import ( + "bufio" "fmt" "os" "path" @@ -127,12 +128,16 @@ func promptForName(out *output.Output, fpath string) (string, error) { hostStub := strings.Split(host, ".")[0] fmt.Printf("\nPlease choose a short name for this device (default: '%s'): ", hostStub) var input string - n, err := fmt.Scanln(&input) - if n != 1 { - return "", fmt.Errorf("expected 1 value from prompt got %d", n) + scanner := bufio.NewScanner(os.Stdin) + if scanner.Scan() { + input = scanner.Text() } - if err != nil { - return "", fmt.Errorf("scanln error: %w", err) + if err = scanner.Err(); err != nil { + return "", fmt.Errorf("scanner error: %w", err) + } + if input == "" { + out.Info("Got no input - using default ...") + input = hostStub } out.Info(fmt.Sprintf("Device name set to '%s'", input)) fmt.Printf("You can change the device name at any time by editing '%s' file\n", fpath) diff --git a/internal/output/output.go b/internal/output/output.go index a0759ea..93f403b 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -21,48 +21,72 @@ func New(logger *zap.Logger, prefix string) *Output { } } +// Info outputs string to stdout and to log (as info) +// This is how we write output to users from interactive commands +// This way we have full record in logs func (f *Output) Info(msg string) { fmt.Printf("%s\n", msg) f.Logger.Info(msg) } -func (f *Output) Error(msg string, err error) { - fmt.Fprintf(os.Stderr, "%s: %s: %v\n", f.ErrPrefix, msg, err) +// InfoE outputs string to stdout and to log (as error) +// Passed error is only written to log +// This is how we output errors to users from interactive commands +// This way we have errors in logs +func (f *Output) InfoE(msg string, err error) { + fmt.Printf("%s\n", msg) f.Logger.Error(msg, zap.Error(err)) } -func (f *Output) ErrorWOErr(msg string) { +// Error outputs string to stderr and to log (as error) +// This is how we output errors from non-interactive commands +func (f *Output) Error(msg string) { fmt.Fprintf(os.Stderr, "%s: %s\n", f.ErrPrefix, msg) f.Logger.Error(msg) } -func (f *Output) Fatal(msg string, err error) { +// ErrorE outputs string and error to stderr and to log (as error) +// This is how we output errors from non-interactive commands +func (f *Output) ErrorE(msg string, err error) { + fmt.Fprintf(os.Stderr, "%s: %s: %v\n", f.ErrPrefix, msg, err) + f.Logger.Error(msg, zap.Error(err)) +} + +// FatalE outputs string and error to stderr and to log (as fatal) +// This is how we raise fatal errors from non-interactive commands +func (f *Output) FatalE(msg string, err error) { fmt.Fprintf(os.Stderr, "%s: %s: %v\n", f.ErrPrefix, msg, err) f.Logger.Fatal(msg, zap.Error(err)) } -var msgDaemonNotRunning = `Resh-daemon didn't respond - it's probably not running. +var msgDaemonNotRunning = `RESH daemon didn't respond - it's probably not running. - -> Try restarting this terminal window to bring resh-daemon back up - -> If the problem persists you can check resh-daemon logs: ~/.local/share/resh/log.json (or ~/$XDG_DATA_HOME/resh/log.json) + -> Start RESH daemon manually - run: resh-daemon-start + -> Or restart this terminal window to bring RESH daemon back up + -> You can check logs: ~/.local/share/resh/log.json (or ~/$XDG_DATA_HOME/resh/log.json) -> You can create an issue at: https://github.com/curusarn/resh/issues ` -var msgTerminalVersionMismatch = `This terminal session was started with different resh version than is installed now. -It looks like you updated resh and didn't restart this terminal. +var msgTerminalVersionMismatch = `This terminal session was started with different RESH version than is installed now. +It looks like you updated RESH and didn't restart this terminal. -> Restart this terminal window to fix that ` -var msgDaemonVersionMismatch = `Resh-daemon is running in different version than is installed now. -It looks like something went wrong during resh update. +var msgDaemonVersionMismatch = `RESH daemon is running in different version than is installed now. +It looks like something went wrong during RESH update. - -> Kill resh-daemon and then launch a new terminal window to fix that: pkill resh-daemon + -> Kill resh-daemon and then launch a new terminal window to fix that: killall resh-daemon -> You can create an issue at: https://github.com/curusarn/resh/issues ` +func (f *Output) InfoDaemonNotRunning(err error) { + fmt.Printf("%s", msgDaemonNotRunning) + f.Logger.Error("Daemon is not running", zap.Error(err)) +} + func (f *Output) ErrorDaemonNotRunning(err error) { fmt.Fprintf(os.Stderr, "%s: %s", f.ErrPrefix, msgDaemonNotRunning) f.Logger.Error("Daemon is not running", zap.Error(err)) @@ -73,6 +97,14 @@ func (f *Output) FatalDaemonNotRunning(err error) { f.Logger.Fatal("Daemon is not running", zap.Error(err)) } +func (f *Output) InfoTerminalVersionMismatch(installedVer, terminalVer string) { + fmt.Printf("%s(installed version: %s, this terminal version: %s)\n\n", + msgTerminalVersionMismatch, installedVer, terminalVer) + f.Logger.Fatal("Version mismatch", + zap.String("installed", installedVer), + zap.String("terminal", terminalVer)) +} + func (f *Output) ErrorTerminalVersionMismatch(installedVer, terminalVer string) { fmt.Fprintf(os.Stderr, "%s: %s(installed version: %s, this terminal version: %s)\n\n", f.ErrPrefix, msgTerminalVersionMismatch, installedVer, terminalVer) @@ -89,6 +121,14 @@ func (f *Output) FatalTerminalVersionMismatch(installedVer, terminalVer string) zap.String("terminal", terminalVer)) } +func (f *Output) InfoDaemonVersionMismatch(installedVer, daemonVer string) { + fmt.Printf("%s(installed version: %s, running daemon version: %s)\n\n", + msgDaemonVersionMismatch, installedVer, daemonVer) + f.Logger.Error("Version mismatch", + zap.String("installed", installedVer), + zap.String("daemon", daemonVer)) +} + func (f *Output) ErrorDaemonVersionMismatch(installedVer, daemonVer string) { fmt.Fprintf(os.Stderr, "%s: %s(installed version: %s, running daemon version: %s)\n\n", f.ErrPrefix, msgDaemonVersionMismatch, installedVer, daemonVer) diff --git a/scripts/install.sh b/scripts/install.sh index ef82451..9068118 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -115,10 +115,10 @@ failed_to_kill() { } if [ -f "$pid_file" ]; then - kill -SIGKILL "$pid_file" || failed_to_kill + kill -SIGTERM "$pid_file" || failed_to_kill rm "$pid_file" else - pkill -SIGKILL "resh-daemon" || failed_to_kill + killall -SIGTERM resh-daemon || failed_to_kill fi echo "Creating/updating config file ..." @@ -152,6 +152,9 @@ fi echo "Launching resh daemon ..." ~/.resh/bin/resh-daemon-start +# FIXME: Figure out how to give people enough time to read everything +# sleep 1 + info="---- Scroll down using arrow keys ---- ##################################### # ____ _____ ____ _ _ # @@ -188,6 +191,7 @@ ISSUES & FEEDBACK Please report issues to: https://github.com/curusarn/resh/issues Feedback and suggestions are very welcome! " +# Show banner if RESH is not loaded in the terminal if [ -z "${__RESH_VERSION:-}" ]; then info="$info ############################################################## # # @@ -206,6 +210,7 @@ echo "Thank you for using RESH" echo "Report issues here: https://github.com/curusarn/resh/issues" echo "Ctrl+R launches the RESH SEARCH app" +# Show banner if RESH is not loaded in the terminal if [ -z "${__RESH_VERSION:-}" ]; then printf " ############################################################## # #