diff --git a/cmd/browse.go b/cmd/browse.go index aef4aa2..fcb1bf4 100644 --- a/cmd/browse.go +++ b/cmd/browse.go @@ -13,10 +13,7 @@ import ( func Browse(cliInput string) { userOS, userArch, _, systemInfo, err := stew.Initialize() - if err != nil { - fmt.Println(err) - os.Exit(1) - } + stew.CatchAndExit(err) sp := constants.LoadingSpinner diff --git a/cmd/config.go b/cmd/config.go index 6b86da3..2b98696 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "os" + "runtime" "github.com/marwanhawari/stew/constants" stew "github.com/marwanhawari/stew/lib" @@ -10,28 +11,32 @@ import ( func Config() { - userOS, _, stewConfig, _, err := stew.Initialize() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - + userOS := runtime.GOOS stewConfigFilePath, err := stew.GetStewConfigFilePath(userOS) stew.CatchAndExit(err) + configExists, err := stew.PathExists(stewConfigFilePath) + stew.CatchAndExit(err) - inputStewPath, err := stew.PromptInput("Set the stewPath. This will contain all stew data other than the binaries.", stewConfig.StewPath) + if !configExists { + _, err := stew.NewStewConfig(userOS) + stew.CatchAndExit(err) + return + } + + defaultStewPath, err := stew.GetDefaultStewPath(userOS) stew.CatchAndExit(err) - inputStewBinPath, err := stew.PromptInput("Set the stewBinPath. This is where the binaries will be installed by stew.", stewConfig.StewBinPath) + defaultStewBinPath, err := stew.GetDefaultStewBinPath(userOS) stew.CatchAndExit(err) - fullStewPath, err := stew.ResolveTilde(inputStewPath) - stew.CatchAndExit(err) - fullStewBinPath, err := stew.ResolveTilde(inputStewBinPath) + newStewPath, newStewBinPath, err := stew.PromptConfig(defaultStewPath, defaultStewBinPath) stew.CatchAndExit(err) - newStewConfig := stew.StewConfig{StewPath: fullStewPath, StewBinPath: fullStewBinPath} + newStewConfig := stew.StewConfig{StewPath: newStewPath, StewBinPath: newStewBinPath} err = stew.WriteStewConfigJSON(newStewConfig, stewConfigFilePath) stew.CatchAndExit(err) fmt.Printf("📄 Updated %v\n", constants.GreenColor(stewConfigFilePath)) + + pathVariable := os.Getenv("PATH") + stew.ValidateStewBinPath(newStewBinPath, pathVariable) } diff --git a/cmd/install.go b/cmd/install.go index 137d4ac..a4eea48 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -15,10 +15,7 @@ func Install(cliInputs []string) { var err error userOS, userArch, _, systemInfo, err := stew.Initialize() - if err != nil { - fmt.Println(err) - os.Exit(1) - } + stew.CatchAndExit(err) for _, cliInput := range cliInputs { if strings.Contains(cliInput, "Stewfile") { diff --git a/cmd/list.go b/cmd/list.go index 7490bce..5f22632 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "os" stew "github.com/marwanhawari/stew/lib" ) @@ -11,10 +10,7 @@ import ( func List(cliTagsFlag bool, cliAssetsFlag bool) { userOS, userArch, _, systemInfo, err := stew.Initialize() - if err != nil { - fmt.Println(err) - os.Exit(1) - } + stew.CatchAndExit(err) stewLockFilePath := systemInfo.StewLockFilePath diff --git a/cmd/rename.go b/cmd/rename.go index 7f91e9a..8f2928a 100644 --- a/cmd/rename.go +++ b/cmd/rename.go @@ -13,10 +13,7 @@ import ( func Rename(cliInput string) { userOS, userArch, _, systemInfo, err := stew.Initialize() - if err != nil { - fmt.Println(err) - os.Exit(1) - } + stew.CatchAndExit(err) err = stew.ValidateCLIInput(cliInput) stew.CatchAndExit(err) diff --git a/cmd/uninstall.go b/cmd/uninstall.go index 9dd2f42..37e6d2d 100644 --- a/cmd/uninstall.go +++ b/cmd/uninstall.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "os" "github.com/marwanhawari/stew/constants" stew "github.com/marwanhawari/stew/lib" @@ -12,10 +11,7 @@ import ( func Uninstall(cliFlag bool, binaryName string) { userOS, userArch, _, systemInfo, err := stew.Initialize() - if err != nil { - fmt.Println(err) - os.Exit(1) - } + stew.CatchAndExit(err) if cliFlag && binaryName != "" { stew.CatchAndExit(stew.CLIFlagAndInputError{}) diff --git a/cmd/upgrade.go b/cmd/upgrade.go index fecaefc..6c4e76e 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -13,10 +13,7 @@ import ( func Upgrade(cliFlag bool, binaryName string) { userOS, userArch, _, systemInfo, err := stew.Initialize() - if err != nil { - fmt.Println(err) - os.Exit(1) - } + stew.CatchAndExit(err) if cliFlag && binaryName != "" { stew.CatchAndExit(stew.CLIFlagAndInputError{}) diff --git a/install.sh b/install.sh index b5dfd9e..50339b9 100755 --- a/install.sh +++ b/install.sh @@ -9,6 +9,7 @@ os="" arch="" exe="" defaultStewPath="" +defaultStewBinPath="" configPath="" # Detect os @@ -23,6 +24,8 @@ case "$(uname -s)" in defaultStewPath="$XDG_DATA_HOME/stew" fi + defaultStewBinPath="$HOME/.local/bin" + if [ -z "$XDG_CONFIG_HOME" ] then configPath="$HOME/.config/stew" @@ -39,6 +42,8 @@ case "$(uname -s)" in defaultStewPath="$XDG_DATA_HOME/stew" fi + defaultStewBinPath="$HOME/.local/bin" + if [ -z "$XDG_CONFIG_HOME" ] then configPath="$HOME/.config/stew" @@ -50,6 +55,7 @@ case "$(uname -s)" in os="windows" exe=".exe" defaultStewPath="$HOME/AppData/Local/stew" + defaultStewBinPath="$HOME/AppData/Local/stew/bin" configPath="$HOME/AppData/Local/stew/Config" ;; esac @@ -97,10 +103,10 @@ else fi fi -read -r -t 60 -p "Set the stewBinPath. This is where the binaries will be installed by stew. (${defaultStewPath}/bin): " stewBinPathInput +read -r -t 60 -p "Set the stewBinPath. This is where the binaries will be installed by stew. (${defaultStewBinPath}): " stewBinPathInput if [ -z "$stewBinPathInput" ] then - stewBinPath="${defaultStewPath}/bin" + stewBinPath="${defaultStewBinPath}" else stewBinPath="${stewBinPathInput/#~/$HOME}" stewBinPath="${stewBinPath/#\$HOME/$HOME}" @@ -111,7 +117,6 @@ else fi fi -mkdir -p "${stewPath}/bin" mkdir -p "${stewPath}/pkg" mkdir -p "${stewBinPath}" mkdir -p "${configPath}" @@ -119,7 +124,7 @@ mkdir -p "${configPath}" echo "{ \"stewPath\": \"${stewPath}\", \"stewBinPath\": \"${stewBinPath}\" -}" > "${configPath}/config.json" +}" > "${configPath}/stew.config.json" # 2. Download the stew binary curl -o "${stewBinPath}/stew${exe}" -fsSL https://github.com/marwanhawari/stew/releases/latest/download/stew-${os}-${arch}${exe} diff --git a/lib/config.go b/lib/config.go index ea20078..07a52c0 100644 --- a/lib/config.go +++ b/lib/config.go @@ -2,10 +2,14 @@ package stew import ( "encoding/json" + "fmt" "io/ioutil" "os" "path/filepath" "runtime" + "strings" + + "github.com/marwanhawari/stew/constants" ) // GetDefaultStewPath will return the default path to the top-level stew directory @@ -46,13 +50,8 @@ func GetDefaultStewBinPath(userOS string) (string, error) { case "windows": stewBinPath = filepath.Join(homeDir, "AppData", "Local", "stew", "bin") default: - xdgDataHomePath := os.Getenv("XDG_DATA_HOME") - if xdgDataHomePath == "" { - stewBinPath = filepath.Join(homeDir, ".local", "share", "stew", "bin") - } else { - stewBinPath = filepath.Join(xdgDataHomePath, "stew", "bin") + stewBinPath = filepath.Join(homeDir, ".local", "bin") - } } return stewBinPath, nil @@ -69,13 +68,13 @@ func GetStewConfigFilePath(userOS string) (string, error) { var stewConfigFilePath string switch userOS { case "windows": - stewConfigFilePath = filepath.Join(homeDir, "AppData", "Local", "stew", "Config", "config.json") + stewConfigFilePath = filepath.Join(homeDir, "AppData", "Local", "stew", "Config", "stew.config.json") default: xdgConfigHomePath := os.Getenv("XDG_CONFIG_HOME") if xdgConfigHomePath == "" { - stewConfigFilePath = filepath.Join(homeDir, ".config", "stew", "config.json") + stewConfigFilePath = filepath.Join(homeDir, ".config", "stew", "stew.config.json") } else { - stewConfigFilePath = filepath.Join(xdgConfigHomePath, "stew", "config.json") + stewConfigFilePath = filepath.Join(xdgConfigHomePath, "stew", "stew.config.json") } } @@ -146,16 +145,27 @@ func NewStewConfig(userOS string) (StewConfig, error) { if err != nil { return StewConfig{}, err } - } - if stewConfig.StewPath == "" { - stewConfig.StewPath = defaultStewPath - } + if stewConfig.StewPath == "" { + stewConfig.StewPath = defaultStewPath + } - if stewConfig.StewBinPath == "" { - stewConfig.StewBinPath = defaultStewBinPath + if stewConfig.StewBinPath == "" { + stewConfig.StewBinPath = defaultStewBinPath + } + } else { + selectedStewPath, selectedStewBinPath, err := PromptConfig(defaultStewPath, defaultStewBinPath) + if err != nil { + return StewConfig{}, err + } + stewConfig.StewPath = selectedStewPath + stewConfig.StewBinPath = selectedStewBinPath + fmt.Printf("📄 Updated %v\n", constants.GreenColor(stewConfigFilePath)) } + pathVariable := os.Getenv("PATH") + ValidateStewBinPath(stewConfig.StewBinPath, pathVariable) + err = createStewDirsAndFiles(stewConfig, stewConfigFilePath) if err != nil { return StewConfig{}, err @@ -171,10 +181,6 @@ func createStewDirsAndFiles(stewConfig StewConfig, stewConfigFilePath string) er if err != nil { return err } - err = os.MkdirAll(filepath.Join(stewConfig.StewPath, "bin"), 0755) - if err != nil { - return err - } err = os.MkdirAll(filepath.Join(stewConfig.StewPath, "pkg"), 0755) if err != nil { return err @@ -229,3 +235,36 @@ func Initialize() (string, string, StewConfig, SystemInfo, error) { return userOS, userArch, stewConfig, systemInfo, nil } + +// PromptConfig launches an interactive UI for setting the stew config values. It returns the resolved stewPath and stewBinPath. +func PromptConfig(suggestedStewPath, suggestedStewBinPath string) (string, string, error) { + inputStewPath, err := PromptInput("Set the stewPath. This will contain all stew data other than the binaries.", suggestedStewPath) + if err != nil { + return "", "", err + } + inputStewBinPath, err := PromptInput("Set the stewBinPath. This is where the binaries will be installed by stew.", suggestedStewBinPath) + if err != nil { + return "", "", err + } + + fullStewPath, err := ResolvePath(inputStewPath) + if err != nil { + return "", "", err + } + fullStewBinPath, err := ResolvePath(inputStewBinPath) + if err != nil { + return "", "", err + } + + return fullStewPath, fullStewBinPath, nil +} + +func ValidateStewBinPath(stewBinPath, pathVariable string) bool { + if !strings.Contains(pathVariable, stewBinPath) { + fmt.Printf("%v The stewBinPath %v is not in your PATH variable.\nYou need to add %v to PATH.\n", constants.YellowColor("WARNING:"), constants.YellowColor(stewBinPath), constants.YellowColor(stewBinPath)) + fmt.Printf("Add the following line to your ~/.zshrc or ~/.bashrc file then start a new terminal session:\n\nexport PATH=\"%v:$PATH\"\n\n", stewBinPath) + return false + } + + return true +} diff --git a/lib/util.go b/lib/util.go index ad08a4d..e4084a5 100644 --- a/lib/util.go +++ b/lib/util.go @@ -378,16 +378,33 @@ func PromptRenameBinary(originalBinaryName string) (string, error) { return renamedBinaryName, nil } -// ResolveTilde will resolve the full path for an input path beginning with ~ -func ResolveTilde(filePath string) (string, error) { - if strings.HasPrefix(filePath, "~") { +// ResolvePath will resolve the full path for an input path +func ResolvePath(filePath string) (string, error) { + var resolvedPath string + var err error + resolvedPath = filePath + + resolvedPath = strings.ReplaceAll(resolvedPath, "\"", "") + + if strings.HasPrefix(resolvedPath, "~") { homeDir, err := os.UserHomeDir() if err != nil { return "", err } - return filepath.Join(homeDir, filePath[2:]), nil + resolvedPath = filepath.Join(homeDir, strings.TrimLeft(resolvedPath, "~")) + } + + resolvedPath = os.ExpandEnv(resolvedPath) + + if !filepath.IsAbs(resolvedPath) { + resolvedPath, err = filepath.Abs(resolvedPath) + if err != nil { + return "", err + } } - return filePath, nil + resolvedPath = strings.TrimRight(resolvedPath, "/") + + return resolvedPath, nil }