From e0d592c6c0f5911fd81ef2d7975b6fe7790faeaf Mon Sep 17 00:00:00 2001 From: Pratik Patel Date: Wed, 19 Aug 2026 10:00:42 -0700 Subject: [PATCH 1/3] feat: improve latest release installer --- README.md | 6 ++ bin/install-latest.sh | 152 ++++++++++++++++++++++++++---------------- 2 files changed, 100 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index f625c153..a4834723 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ Installs to `~/.local/bin`. If it's not on your `PATH`, add this to your shell p export PATH="$HOME/.local/bin:$PATH" ``` +To install somewhere else, set `BREV_INSTALL_DIR` for the installer: + +```bash +curl -fsSL https://raw.githubusercontent.com/brevdev/brev-cli/main/bin/install-latest.sh | BREV_INSTALL_DIR="$HOME/bin" bash +``` + ### Windows **Using Brev With Windows Subsystem for Linux (WSL)** diff --git a/bin/install-latest.sh b/bin/install-latest.sh index 47a3ed19..8cc8325f 100755 --- a/bin/install-latest.sh +++ b/bin/install-latest.sh @@ -1,61 +1,97 @@ #!/usr/bin/env bash -set -eo pipefail - -# Detect OS and architecture -OS="$(uname -s | tr '[:upper:]' '[:lower:]')" -ARCH="$(uname -m)" -case "${ARCH}" in - x86_64) ARCH="amd64" ;; - aarch64) ARCH="arm64" ;; -esac - -# GitHub API token: GITHUB_TOKEN env, else gh auth token. -GITHUB_TOKEN="${GITHUB_TOKEN:-}" -if [ -z "${GITHUB_TOKEN}" ] && command -v gh >/dev/null 2>&1; then - GITHUB_TOKEN="$(gh auth token 2>/dev/null || true)" -fi - -# Fetch release metadata from GitHub API -API_RESPONSE="$(curl -sf ${GITHUB_TOKEN:+-H "Authorization: token ${GITHUB_TOKEN}"} https://api.github.com/repos/brevdev/brev-cli/releases/latest)" || { - echo "Error: Failed to fetch release info from GitHub API." >&2 - echo "This is often caused by rate limiting when many requests come from the same IP." >&2 - echo "If you are using a VPN, try turning it off and running this script again." >&2 - echo "You can also set GITHUB_TOKEN to avoid rate limits." >&2 - echo "For more details, see: https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting" >&2 - exit 1 +set -euo pipefail + +# Keep all installation work inside main so a truncated `curl | bash` download +# cannot execute a partial installer. + +curl_brev_cli() { + curl \ + --fail \ + --silent \ + --show-error \ + --location \ + --proto '=https' \ + --proto-redir '=https' \ + --connect-timeout 10 \ + "$@" +} + +get_release_arch() { + case "$(uname -m)" in + x86_64|amd64) printf '%s\n' "amd64" ;; + aarch64|arm64) printf '%s\n' "arm64" ;; + *) return 1 ;; + esac +} + +get_latest_release_tag() { + # Use GitHub's web redirect instead of its REST API so installations from + # shared egress IPs are not subject to the unauthenticated API quota. + local release_url release_tag + release_url="$(curl_brev_cli --max-time 30 --output /dev/null --write-out '%{url_effective}' \ + "https://github.com/brevdev/brev-cli/releases/latest")" || return 1 + release_tag="${release_url##*/}" + [[ "$release_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1 + printf '%s\n' "$release_tag" +} + +main() { + local os release_arch release_tag release_version archive_name archive_path install_dir + + os="$(uname -s | tr '[:upper:]' '[:lower:]')" + case "$os" in + darwin|linux) ;; + *) + printf 'Error: Unsupported operating system: %s\n' "$os" >&2 + return 1 + ;; + esac + + if ! release_arch="$(get_release_arch)"; then + printf 'Error: Unsupported architecture: %s\n' "$(uname -m)" >&2 + return 1 + fi + + TMP_DIR="$(mktemp -d)" + trap 'rm -rf -- "$TMP_DIR"' EXIT + + if ! release_tag="$(get_latest_release_tag)"; then + printf 'Error: Could not determine the latest Brev CLI release.\n' >&2 + return 1 + fi + + release_version="${release_tag#v}" + archive_name="brev-cli_${release_version}_${os}_${release_arch}.tar.gz" + archive_path="${TMP_DIR}/${archive_name}" + + # The archive has no total time limit; slow but progressing downloads may + # take as long as needed. + curl_brev_cli \ + --output "$archive_path" \ + "https://github.com/brevdev/brev-cli/releases/download/${release_tag}/${archive_name}" + tar -xzf "$archive_path" -C "$TMP_DIR" -- brev + + if [[ ! -f "${TMP_DIR}/brev" || -L "${TMP_DIR}/brev" ]]; then + printf 'Error: Release archive did not contain a regular brev executable.\n' >&2 + return 1 + fi + + install_dir="${BREV_INSTALL_DIR:-${HOME}/.local/bin}" + mkdir -p "$install_dir" + mv "${TMP_DIR}/brev" "${install_dir}/brev" + chmod 0755 "${install_dir}/brev" + + printf 'Successfully installed Brev CLI to %s/brev\n' "$install_dir" + + case ":${PATH}:" in + *":${install_dir}:"*) ;; + *) + printf '\nWarning: %s is not in your PATH.\n' "$install_dir" >&2 + printf 'Add it to your shell profile, then restart your shell:\n' >&2 + printf ' export PATH="%s:%s"\n' "$install_dir" "\$PATH" >&2 + ;; + esac } -# Extract the download URL for this platform -DOWNLOAD_URL="$(echo "${API_RESPONSE}" | grep "browser_download_url.*${OS}.*${ARCH}" | cut -d '"' -f 4 || true)" -if [ -z "${DOWNLOAD_URL}" ]; then - echo "Error: Could not find release for ${OS} ${ARCH}" >&2 - echo "GitHub API response (truncated): ${API_RESPONSE:0:200}" >&2 - exit 1 -fi - -# Create temporary directory and ensure cleanup -TMP_DIR="$(mktemp -d)" -trap 'rm -rf "${TMP_DIR}"' EXIT - -# Download and extract the release -curl -sL "${DOWNLOAD_URL}" -o "${TMP_DIR}/brev.tar.gz" -tar -xzf "${TMP_DIR}/brev.tar.gz" -C "${TMP_DIR}" - -# Install the binary to the user's local bin -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" - -case ":${PATH}:" in - *":${INSTALL_DIR}:"*) ;; - *) - echo - echo "Warning: ${INSTALL_DIR} is not in your PATH." >&2 - echo "Add it by appending the following line to your shell profile (e.g. ~/.bashrc, ~/.zshrc):" >&2 - echo " export PATH=\"\${HOME}/.local/bin:\${PATH}\"" >&2 - echo "Then restart your shell or run 'source' on the profile to pick up the change." >&2 - ;; -esac +TMP_DIR="" +main "$@" From 5a4b4684857f10995715b3ea2fcf1b3bd9913329 Mon Sep 17 00:00:00 2001 From: Pratik Patel Date: Wed, 19 Aug 2026 12:34:29 -0700 Subject: [PATCH 2/3] refactor: inline installer curl options --- bin/install-latest.sh | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/bin/install-latest.sh b/bin/install-latest.sh index 8cc8325f..f880cd3f 100755 --- a/bin/install-latest.sh +++ b/bin/install-latest.sh @@ -4,18 +4,6 @@ set -euo pipefail # Keep all installation work inside main so a truncated `curl | bash` download # cannot execute a partial installer. -curl_brev_cli() { - curl \ - --fail \ - --silent \ - --show-error \ - --location \ - --proto '=https' \ - --proto-redir '=https' \ - --connect-timeout 10 \ - "$@" -} - get_release_arch() { case "$(uname -m)" in x86_64|amd64) printf '%s\n' "amd64" ;; @@ -28,7 +16,17 @@ get_latest_release_tag() { # Use GitHub's web redirect instead of its REST API so installations from # shared egress IPs are not subject to the unauthenticated API quota. local release_url release_tag - release_url="$(curl_brev_cli --max-time 30 --output /dev/null --write-out '%{url_effective}' \ + release_url="$(curl \ + --fail \ + --silent \ + --show-error \ + --location \ + --proto '=https' \ + --proto-redir '=https' \ + --connect-timeout 10 \ + --max-time 30 \ + --output /dev/null \ + --write-out '%{url_effective}' \ "https://github.com/brevdev/brev-cli/releases/latest")" || return 1 release_tag="${release_url##*/}" [[ "$release_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1 @@ -66,7 +64,14 @@ main() { # The archive has no total time limit; slow but progressing downloads may # take as long as needed. - curl_brev_cli \ + curl \ + --fail \ + --silent \ + --show-error \ + --location \ + --proto '=https' \ + --proto-redir '=https' \ + --connect-timeout 10 \ --output "$archive_path" \ "https://github.com/brevdev/brev-cli/releases/download/${release_tag}/${archive_name}" tar -xzf "$archive_path" -C "$TMP_DIR" -- brev From a112020e152ed6ab51be3f0e9a5702168dccad06 Mon Sep 17 00:00:00 2001 From: Pratik Patel Date: Wed, 19 Aug 2026 13:08:39 -0700 Subject: [PATCH 3/3] feat: prefer Homebrew for CLI installation --- bin/install-latest.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bin/install-latest.sh b/bin/install-latest.sh index f880cd3f..c380e5b7 100755 --- a/bin/install-latest.sh +++ b/bin/install-latest.sh @@ -45,6 +45,13 @@ main() { ;; esac + if [[ -z "${BREV_INSTALL_DIR:-}" ]] && command -v brew >/dev/null 2>&1; then + if brew install brevdev/homebrew-brev/brev; then + return 0 + fi + printf 'Warning: Homebrew installation failed; falling back to the release archive.\n' >&2 + fi + if ! release_arch="$(get_release_arch)"; then printf 'Error: Unsupported architecture: %s\n' "$(uname -m)" >&2 return 1