mirror of https://github.com/curusarn/resh
parent
dd573cac03
commit
668308a528
@ -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 |
||||
} |
||||
@ -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 |
||||
} |
||||
Loading…
Reference in new issue