From 67466f685f15086e914c037a192c7869b0c5cc77 Mon Sep 17 00:00:00 2001 From: Marwan Hawari Date: Mon, 7 Mar 2022 19:40:25 -0800 Subject: [PATCH 1/3] update default stewBinPath, add config prompt on initial use, add warning if stewBinPath not in PATH --- cmd/browse.go | 5 +--- cmd/config.go | 29 ++++++++++-------- cmd/install.go | 5 +--- cmd/list.go | 6 +--- cmd/rename.go | 5 +--- cmd/uninstall.go | 6 +--- cmd/upgrade.go | 5 +--- install.sh | 13 +++++--- lib/config.go | 77 ++++++++++++++++++++++++++++++++++++------------ lib/util.go | 27 +++++++++++++---- 10 files changed, 112 insertions(+), 66 deletions(-) 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 } From 047fd52948080aa3b8ec35f12686ce851ede4e31 Mon Sep 17 00:00:00 2001 From: Marwan Hawari Date: Mon, 7 Mar 2022 21:17:36 -0800 Subject: [PATCH 2/3] update docs --- README.md | 64 ++++++++++++++++++++++++++++++++++++++++++++----------- config.md | 23 ++++++++++++++++++++ 2 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 config.md diff --git a/README.md b/README.md index d94e1d8..9498db3 100644 --- a/README.md +++ b/README.md @@ -36,16 +36,41 @@ * Easily distribute binaries across teams and private repositories. * Get the latest releases ahead of other package managers. * Rapidly browse, install, and experiment with different projects. -* Isolated `~/.stew/` directory. +* [Configure](https://github.com/marwanhawari/stew/blob/main/config.md) where to install binaries. * No need for `sudo`. * Portable [`Stewfile`](https://github.com/marwanhawari/stew/blob/main/examples/Stewfile) with optional pinned versioning. ![demo](https://github.com/marwanhawari/stew/raw/main/assets/demo.gif) # Installation -Stew supports Linux, macOS, and Windows: +Stew supports Linux, macOS, and Windows. + +### Install a pre-compiled binary: + +* Install using `curl`: +``` +curl -fsSL https://raw.githubusercontent.com/marwanhawari/stew/main/install.sh | bash +``` + +* Install using `brew`: +``` +brew install marwanhawari/tap/stew +``` + +* Download a pre-compiled binary from the [releases page](https://github.com/marwanhawari/stew/releases). + +### Install from source: + +* Install the latest released version: ``` -curl -fsSL https://raw.githubusercontent.com/marwanhawari/stew/main/install.sh | sh +go install github.com/marwanhawari/stew@latest +``` + +* Install the latest unreleased source: +``` +git clone https://github.com/marwanhawari/stew +cd stew +go install . ``` # Usage @@ -89,6 +114,12 @@ stew uninstall rg # Uninstall using the name of the binary directly stew uninstall --all # Uninstall all binaries ``` +### Rename +```sh +# Rename an installed binary using an interactive UI +stew rename rg # Rename using the name of the binary directly +``` + ### List ```sh # List installed binaries @@ -98,19 +129,28 @@ stew list --tags > Stewfile # Pin tags stew list --tags --assets > Stewfile # Pin tags and assets ``` +### Config +```sh +# Configure the stew file paths using an interactive UI +stew config # Automatically updates the stew.config.json +``` + # FAQ ### Why couldn't `stew` automatically find any binaries for X repo? The repo probably uses an unconventional naming scheme for their binaries. You can always manually select the release asset. -### I've installed `stew` but the command is still not found. -The `stew` [install script](https://github.com/marwanhawari/stew/blob/main/install.sh) attempts to add `~/.stew/bin` to `PATH` in your `.zshrc` or `.bashrc` file. You will also need to start a new terminal session for the changes to take effect. Make sure that `~/.stew/bin` is in your `PATH` environment variable. - ### Will `stew` work with private GitHub repositories? Yes, `stew` will automatically detect if you have a `GITHUB_TOKEN` environment variable and allow you to access binaries from your private repositories. -### How do I uninstall `stew`? -Simply run `rm -rf $HOME/.stew/` and optionally remove this line -``` -export PATH="$HOME/.stew/bin:$PATH" -``` -from your `.zshrc` or `.bashrc` file. +### Where does `stew` install binaries? +The default installation path will depend on your OS: +| Linux/macOS | Windows | +| ------------ | ---------- | +| `~/.local/bin` | `~/AppData/Local/stew/bin` | + +However, this location can be [configured](https://github.com/marwanhawari/stew/blob/main/config.md). + +Make sure that the installation path is in your `PATH` environment variable. Otherwise, you won't be able to use any of the binaries installed by `stew`. + +### I've installed `stew` but the command is still not found. +If you install `stew` with the [curl install script](https://github.com/marwanhawari/stew/blob/main/install.sh), the `stew` binary will be installed in the `stewBinPath` you provided. The install script also attempts to add the `stewBinPath` to `PATH` in your `.zshrc` or `.bashrc` file. You will need to start a new terminal session for the changes to take effect. Make sure that the `stewBinPath` is in your `PATH` environment variable. diff --git a/config.md b/config.md new file mode 100644 index 0000000..8f964ce --- /dev/null +++ b/config.md @@ -0,0 +1,23 @@ +# Configuration +`stew` can be configured with a `stew.config.json` file. The location of this file will depend on your OS: +|Linux/macOS | Windows | +| ------------ | ---------- | +| `$XDG_CONFIG_HOME/stew` or `~/.config/stew` | `~/AppData/Local/stew/Config` | + +You can configure 2 aspects of `stew`: +1. The `stewPath`: this is where `stew` data is stored. +2. The `stewBinPath`: this is where `stew` installs binaries + +The default locations for these are: +| | Linux/macOS | Windows | +| ------------ | ------------ | ---------- | +| `stewPath` | `$XDG_DATA_HOME/stew` or `~/.local/share/stew` | `~/AppData/Local/stew` | +| `stewBinPath` | `~/.local/bin` | `~/AppData/Local/stew/bin` | + +There are multiple ways to configure these: +* When first installing `stew`: + * If you install with the [curl install script](https://github.com/marwanhawari/stew/blob/main/install.sh) you will be immediately prompted to set these. This will create a `stew.config.json` file. + * If you install the `stew` binary itself (without the curl install script - i.e. with `brew` or by downloading it directly from the releases page), `stew` will look for a `stew.config.json` file. If it cannot find one, then you will be prompted to set the configuration values. + +* After `stew` is installed, you can use the `stew config` command to set the configuration values. +* At any time, you can manually create or edit the `stew.config.json` file. It should have values for `stewPath` and `stewBinPath`. \ No newline at end of file From 90c747ea28af35cd021cb1ab450f37793cebbc5e Mon Sep 17 00:00:00 2001 From: Marwan Hawari Date: Mon, 7 Mar 2022 21:21:52 -0800 Subject: [PATCH 3/3] add brew to goreleaser --- .github/workflows/release.yml | 2 +- .goreleaser.yaml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06e4257..dc00d8f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,4 +54,4 @@ jobs: version: latest args: release --rm-dist env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_PAT }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index e3e1ed5..e2f96ad 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -19,3 +19,14 @@ checksum: name_template: "checksums.txt" changelog: skip: true +brews: + - tap: + owner: marwanhawari + name: homebrew-tap + folder: Formula + commit_author: + name: Marwan Hawari + email: marwan.hawari@gmail.com + license: "MIT" + homepage: "https://github.com/marwanhawari/stew" + description: "An independent package manager for compiled binaries"