From edded77691e6f7c2ca30868b3845c58fc70cb2dd Mon Sep 17 00:00:00 2001 From: Angular <15901434509@qq.com> Date: Wed, 20 May 2026 15:14:18 +0800 Subject: [PATCH] fix(installer): fall back when brew install fails (#2881) --- .github/workflows/ci.yml | 3 ++ scripts/install.sh | 24 ++++++++--- scripts/install.test.sh | 92 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 scripts/install.test.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53f03c978..ca27ddd43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,6 +77,9 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Test shell installers + run: bash scripts/install.test.sh + - name: Setup Go uses: actions/setup-go@v5 with: diff --git a/scripts/install.sh b/scripts/install.sh index ebb0d82ca..59122b2ca 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -66,16 +66,27 @@ detect_os() { install_cli_brew() { info "Installing Multica CLI via Homebrew..." if ! brew tap multica-ai/tap 2>/dev/null; then - fail "Failed to add Homebrew tap. Check your network connection." + warn "Failed to add Homebrew tap. Falling back to GitHub Releases binary install." + return 1 fi # brew install exits non-zero if already installed on older Homebrew versions - if ! brew install "$BREW_PACKAGE" 2>/dev/null; then + local brew_log + brew_log=$(mktemp) + if ! brew install "$BREW_PACKAGE" >"$brew_log" 2>&1; then if brew list "$BREW_PACKAGE" >/dev/null 2>&1; then + rm -f "$brew_log" ok "Multica CLI already installed via Homebrew" else - fail "Failed to install multica via Homebrew." + warn "Failed to install multica via Homebrew. Falling back to GitHub Releases binary install." + if [ -s "$brew_log" ]; then + warn "Homebrew output (last 80 lines):" + tail -n 80 "$brew_log" | sed 's/^/ /' >&2 + fi + rm -f "$brew_log" + return 1 fi else + rm -f "$brew_log" ok "Multica CLI installed via Homebrew" fi } @@ -103,8 +114,9 @@ install_cli_binary() { tar -xzf "$tmp_dir/multica.tar.gz" -C "$tmp_dir" multica - # Try /usr/local/bin first, fall back to ~/.local/bin - local bin_dir="/usr/local/bin" + # Try /usr/local/bin first, fall back to ~/.local/bin. Tests and scripted + # installs can override the first choice with MULTICA_BIN_DIR. + local bin_dir="${MULTICA_BIN_DIR:-/usr/local/bin}" if [ -w "$bin_dir" ]; then mv "$tmp_dir/multica" "$bin_dir/multica" elif command_exists sudo; then @@ -232,7 +244,7 @@ install_cli() { fi if command_exists brew; then - install_cli_brew + install_cli_brew || install_cli_binary else install_cli_binary fi diff --git a/scripts/install.test.sh b/scripts/install.test.sh new file mode 100644 index 000000000..9f4632fd1 --- /dev/null +++ b/scripts/install.test.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +test_brew_failure_falls_back_to_release_binary() { + local tmp + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' RETURN + + local stub_bin="$tmp/stub-bin" + local install_bin="$tmp/install-bin" + local payload_dir="$tmp/payload" + mkdir -p "$stub_bin" "$install_bin" "$payload_dir" + + cat >"$stub_bin/brew" <<'STUB' +#!/usr/bin/env bash +case "${1:-}" in + tap) + exit 0 + ;; + install) + echo "simulated brew install failure" >&2 + exit 42 + ;; + list) + exit 1 + ;; + *) + exit 0 + ;; +esac +STUB + chmod +x "$stub_bin/brew" + + cat >"$payload_dir/multica" <<'STUB' +#!/usr/bin/env bash +echo "multica v0.3.2 (commit: test)" +STUB + chmod +x "$payload_dir/multica" + tar -czf "$tmp/multica.tar.gz" -C "$payload_dir" multica + + cat >"$stub_bin/curl" <<'STUB' +#!/usr/bin/env bash +if [[ "$*" == *"-sI"* ]]; then + printf 'HTTP/2 302\r\nlocation: https://github.com/multica-ai/multica/releases/tag/v0.3.2\r\n' + exit 0 +fi + +out="" +while [[ $# -gt 0 ]]; do + case "$1" in + -o) + out="$2" + shift 2 + ;; + *) + shift + ;; + esac +done + +if [[ -z "$out" ]]; then + echo "stub curl expected -o" >&2 + exit 2 +fi +cp "$MULTICA_TEST_ARCHIVE" "$out" +STUB + chmod +x "$stub_bin/curl" + + local out="$tmp/install.out" + local err="$tmp/install.err" + if ! PATH="$stub_bin:$install_bin:/usr/bin:/bin" \ + MULTICA_BIN_DIR="$install_bin" \ + MULTICA_TEST_ARCHIVE="$tmp/multica.tar.gz" \ + bash "$ROOT_DIR/scripts/install.sh" >"$out" 2>"$err"; then + echo "expected install.sh to fall back after brew install failure" >&2 + cat "$out" >&2 || true + cat "$err" >&2 || true + return 1 + fi + + if [[ ! -x "$install_bin/multica" ]]; then + echo "expected fallback binary at $install_bin/multica" >&2 + cat "$out" >&2 || true + cat "$err" >&2 || true + return 1 + fi +} + +test_brew_failure_falls_back_to_release_binary +echo "install.sh tests passed"