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