From 5819e0431e9ba1a94bc6408b699f86e5e672869e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:54:49 +0000 Subject: [PATCH] feat: install brev CLI to ~/.local/bin without requiring sudo - Changed install-latest.sh and install-latest-linux.sh to install to ~/.local/bin instead of /usr/local/bin, removing the need for sudo - Added PATH warning if ~/.local/bin is not in the user's PATH - Updated README install instructions to remove sudo prefix - Removed sudo gating from the upgrade command's direct install path - Updated upgrade message to reflect new install location Co-Authored-By: Alec Fong --- README.md | 4 ++-- bin/install-latest-linux.sh | 23 ++++++++++++++++++++--- bin/install-latest.sh | 23 +++++++++++++++++++---- pkg/cmd/upgrade/runner.go | 2 +- pkg/cmd/upgrade/upgrade.go | 10 ++++------ pkg/cmd/upgrade/upgrade_test.go | 8 -------- 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index e64b2efed..3eca54d70 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ brew install brevdev/homebrew-brev/brev ### Linux ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/bin/install-latest.sh)" +bash -c "$(curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/bin/install-latest.sh)" ``` ### Windows @@ -30,7 +30,7 @@ Brev is supported on windows currently through the Windows Subsystem for Linux ( Once you have WSL installed and configured, you can install Brev by running the following command in your terminal: ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/bin/install-latest.sh)" +bash -c "$(curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/bin/install-latest.sh)" ``` ### From conda-forge diff --git a/bin/install-latest-linux.sh b/bin/install-latest-linux.sh index da516c9ae..2861b8155 100755 --- a/bin/install-latest-linux.sh +++ b/bin/install-latest-linux.sh @@ -15,7 +15,24 @@ curl -L "${DOWNLOAD_URL}" -o "${TMP_DIR}/$(basename "${DOWNLOAD_URL}")" ARCHIVE_FILE="$(find "${TMP_DIR}" -name "brev*.tar.gz" -type f)" tar -xzf "${ARCHIVE_FILE}" -C "${TMP_DIR}" -# Install the binary to system location -sudo mv "${TMP_DIR}/brev" /usr/local/bin/brev -sudo chmod +x /usr/local/bin/brev +# Install the binary to user-local location (no sudo required) +INSTALL_DIR="${HOME}/.local/bin" +mkdir -p "${INSTALL_DIR}" +mv "${TMP_DIR}/brev" "${INSTALL_DIR}/brev" +chmod +x "${INSTALL_DIR}/brev" + +echo "Successfully installed brev CLI to ${INSTALL_DIR}/brev" + +# Warn if the install directory is not in PATH +case ":${PATH}:" in + *":${INSTALL_DIR}:"*) ;; + *) + echo "" + echo "WARNING: ${INSTALL_DIR} is not in your PATH." + echo "Add it by running:" + echo "" + echo " echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.bashrc && source ~/.bashrc" + echo "" + ;; +esac diff --git a/bin/install-latest.sh b/bin/install-latest.sh index 16f5c6343..ce96bb5c0 100755 --- a/bin/install-latest.sh +++ b/bin/install-latest.sh @@ -26,8 +26,23 @@ trap 'rm -rf "${TMP_DIR}"' EXIT curl -sL "${DOWNLOAD_URL}" -o "${TMP_DIR}/brev.tar.gz" tar -xzf "${TMP_DIR}/brev.tar.gz" -C "${TMP_DIR}" -# Install the binary to system location -sudo mv "${TMP_DIR}/brev" /usr/local/bin/brev -sudo chmod +x /usr/local/bin/brev +# Install the binary to user-local location (no sudo required) +INSTALL_DIR="${HOME}/.local/bin" +mkdir -p "${INSTALL_DIR}" +mv "${TMP_DIR}/brev" "${INSTALL_DIR}/brev" +chmod +x "${INSTALL_DIR}/brev" -echo "Successfully installed brev CLI to /usr/local/bin/brev" +echo "Successfully installed brev CLI to ${INSTALL_DIR}/brev" + +# Warn if the install directory is not in PATH +case ":${PATH}:" in + *":${INSTALL_DIR}:"*) ;; + *) + echo "" + echo "WARNING: ${INSTALL_DIR} is not in your PATH." + echo "Add it by running:" + echo "" + echo " echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.bashrc && source ~/.bashrc" + echo "" + ;; +esac diff --git a/pkg/cmd/upgrade/runner.go b/pkg/cmd/upgrade/runner.go index e48172302..538a99d71 100644 --- a/pkg/cmd/upgrade/runner.go +++ b/pkg/cmd/upgrade/runner.go @@ -33,7 +33,7 @@ func (SystemUpgrader) UpgradeViaBrew(t *terminal.Terminal) error { return nil } -// UpgradeViaInstallScript runs the upstream install-latest.sh script via sudo. +// UpgradeViaInstallScript runs the upstream install-latest.sh script. func (SystemUpgrader) UpgradeViaInstallScript(t *terminal.Terminal) error { t.Vprintf("Running: bash -c \"$(curl -fsSL %s)\"\n", installScriptURL) t.Vprint("") diff --git a/pkg/cmd/upgrade/upgrade.go b/pkg/cmd/upgrade/upgrade.go index dc0bc81a6..92e48c55b 100644 --- a/pkg/cmd/upgrade/upgrade.go +++ b/pkg/cmd/upgrade/upgrade.go @@ -9,7 +9,6 @@ import ( "github.com/brevdev/brev-cli/pkg/cmd/register" "github.com/brevdev/brev-cli/pkg/cmd/version" "github.com/brevdev/brev-cli/pkg/store" - "github.com/brevdev/brev-cli/pkg/sudo" "github.com/brevdev/brev-cli/pkg/terminal" "github.com/spf13/cobra" @@ -43,7 +42,6 @@ type upgradeDeps struct { detector Detector upgrader Upgrader confirmer terminal.Confirmer - gater sudo.Gater skillInstaller SkillInstaller } @@ -52,7 +50,6 @@ func defaultUpgradeDeps() upgradeDeps { detector: SystemDetector{}, upgrader: SystemUpgrader{}, confirmer: register.TerminalPrompter{}, - gater: sudo.Default, skillInstaller: defaultSkillInstaller{}, } } @@ -146,11 +143,12 @@ func upgradeViaBrew(t *terminal.Terminal, deps upgradeDeps) (bool, error) { func upgradeViaDirect(t *terminal.Terminal, deps upgradeDeps) (bool, error) { t.Vprint("Detected install method: direct binary install") - t.Vprint("This will download the latest release and install it to /usr/local/bin/brev") + t.Vprint("This will download the latest release and install it to ~/.local/bin/brev") t.Vprint("") - if err := deps.gater.Gate(t, deps.confirmer, "Upgrade", false); err != nil { - return false, fmt.Errorf("sudo issue: %w", err) + if !deps.confirmer.ConfirmYesNo("Proceed with upgrade?") { + t.Vprint("Upgrade canceled.") + return false, nil } t.Vprint("") diff --git a/pkg/cmd/upgrade/upgrade_test.go b/pkg/cmd/upgrade/upgrade_test.go index 7afb59a69..607390fbd 100644 --- a/pkg/cmd/upgrade/upgrade_test.go +++ b/pkg/cmd/upgrade/upgrade_test.go @@ -6,7 +6,6 @@ import ( "github.com/brevdev/brev-cli/pkg/cmd/version" "github.com/brevdev/brev-cli/pkg/store" - "github.com/brevdev/brev-cli/pkg/sudo" "github.com/brevdev/brev-cli/pkg/terminal" ) @@ -65,7 +64,6 @@ func Test_runUpgrade_AlreadyUpToDate(t *testing.T) { detector: mockDetector{method: InstallMethodBrew}, upgrader: upgrader, confirmer: mockConfirmer{confirm: true}, - gater: sudo.CachedGater{}, skillInstaller: skill, } @@ -94,7 +92,6 @@ func Test_runUpgrade_BrewPath(t *testing.T) { detector: mockDetector{method: InstallMethodBrew}, upgrader: upgrader, confirmer: mockConfirmer{confirm: true}, - gater: sudo.CachedGater{}, skillInstaller: skill, } @@ -126,7 +123,6 @@ func Test_runUpgrade_DirectPath(t *testing.T) { detector: mockDetector{method: InstallMethodDirect}, upgrader: upgrader, confirmer: mockConfirmer{confirm: true}, - gater: sudo.CachedGater{}, skillInstaller: skill, } @@ -158,7 +154,6 @@ func Test_runUpgrade_UserCancels(t *testing.T) { detector: mockDetector{method: InstallMethodBrew}, upgrader: upgrader, confirmer: mockConfirmer{confirm: false}, - gater: sudo.CachedGater{}, skillInstaller: skill, } @@ -181,7 +176,6 @@ func Test_runUpgrade_VersionCheckFails(t *testing.T) { detector: mockDetector{method: InstallMethodBrew}, upgrader: &mockUpgrader{}, confirmer: mockConfirmer{confirm: true}, - gater: sudo.CachedGater{}, skillInstaller: &mockSkillInstaller{}, } @@ -204,7 +198,6 @@ func Test_runUpgrade_UpgraderFails(t *testing.T) { detector: mockDetector{method: InstallMethodBrew}, upgrader: upgrader, confirmer: mockConfirmer{confirm: true}, - gater: sudo.CachedGater{}, skillInstaller: skill, } @@ -230,7 +223,6 @@ func Test_runUpgrade_SkillInstallFailure_DoesNotFailUpgrade(t *testing.T) { detector: mockDetector{method: InstallMethodBrew}, upgrader: upgrader, confirmer: mockConfirmer{confirm: true}, - gater: sudo.CachedGater{}, skillInstaller: skill, }