goose/download_cli.sh
aegntic 13314515bb
fix: use Windows-compatible default path for CLI installation (#5221)
Signed-off-by: aegntic <research@aegntic.ai>
Co-authored-by: aegntic <research@aegntic.ai>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-21 15:39:34 -04:00

355 lines
12 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -eu
##############################################################################
# goose CLI Install Script
#
# This script downloads the latest stable 'goose' CLI binary from GitHub releases
# and installs it to your system.
#
# Supported OS: macOS (darwin), Linux, Windows (MSYS2/Git Bash/WSL)
# Supported Architectures: x86_64, arm64
#
# Usage:
# curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | bash
#
# Environment variables:
# GOOSE_BIN_DIR - Directory to which goose will be installed (default: $HOME/.local/bin)
# GOOSE_VERSION - Optional: specific version to install (e.g., "v1.0.25"). Overrides CANARY. Can be in the format vX.Y.Z, vX.Y.Z-suffix, or X.Y.Z
# GOOSE_PROVIDER - Optional: provider for goose
# GOOSE_MODEL - Optional: model for goose
# CANARY - Optional: if set to "true", downloads from canary release instead of stable
# CONFIGURE - Optional: if set to "false", disables running goose configure interactively
# ** other provider specific environment variables (eg. DATABRICKS_HOST)
##############################################################################
# --- 1) Check for dependencies ---
# Check for curl
if ! command -v curl >/dev/null 2>&1; then
echo "Error: 'curl' is required to download goose. Please install curl and try again."
exit 1
fi
# Check for tar or unzip (depending on OS)
if ! command -v tar >/dev/null 2>&1 && ! command -v unzip >/dev/null 2>&1; then
echo "Error: Either 'tar' or 'unzip' is required to extract goose. Please install one and try again."
exit 1
fi
# Check for required extraction tools based on detected OS
if [ "${OS:-}" = "windows" ]; then
# Windows uses PowerShell's built-in Expand-Archive - check if PowerShell is available
if ! command -v powershell.exe >/dev/null 2>&1 && ! command -v pwsh >/dev/null 2>&1; then
echo "Warning: PowerShell is recommended to extract Windows packages but was not found."
echo "Falling back to unzip if available."
fi
else
if ! command -v tar >/dev/null 2>&1; then
echo "Error: 'tar' is required to extract packages for ${OS:-unknown}. Please install tar and try again."
exit 1
fi
fi
# --- 2) Variables ---
REPO="block/goose"
OUT_FILE="goose"
# Set default bin directory based on detected OS environment
if [[ "${WINDIR:-}" ]] || [[ "${windir:-}" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
# Native Windows environments - use Windows user profile path
DEFAULT_BIN_DIR="$USERPROFILE/goose"
elif [[ -f "/proc/version" ]] && grep -q "Microsoft\|WSL" /proc/version 2>/dev/null; then
# WSL - use Linux-style path but make sure it exists
DEFAULT_BIN_DIR="$HOME/.local/bin"
elif [[ "$PWD" =~ ^/mnt/[a-zA-Z]/ ]]; then
# WSL mount point detection
DEFAULT_BIN_DIR="$HOME/.local/bin"
else
# Default for Linux/macOS
DEFAULT_BIN_DIR="$HOME/.local/bin"
fi
GOOSE_BIN_DIR="${GOOSE_BIN_DIR:-$DEFAULT_BIN_DIR}"
RELEASE="${CANARY:-false}"
CONFIGURE="${CONFIGURE:-true}"
if [ -n "${GOOSE_VERSION:-}" ]; then
# Validate the version format
if [[ ! "$GOOSE_VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-.*)?$ ]]; then
echo "[error]: invalid version '$GOOSE_VERSION'."
echo " expected: semver format vX.Y.Z, vX.Y.Z-suffix, or X.Y.Z"
exit 1
fi
GOOSE_VERSION=$(echo "$GOOSE_VERSION" | sed 's/^v\{0,1\}/v/') # Ensure the version string is prefixed with 'v' if not already present
RELEASE_TAG="$GOOSE_VERSION"
else
# If GOOSE_VERSION is not set, fall back to existing behavior for backwards compatibility
RELEASE_TAG="$([[ "$RELEASE" == "true" ]] && echo "canary" || echo "stable")"
fi
# --- 3) Detect OS/Architecture ---
# Better OS detection for Windows environments
if [[ "${WINDIR:-}" ]] || [[ "${windir:-}" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
OS="windows"
elif [[ -f "/proc/version" ]] && grep -q "Microsoft\|WSL" /proc/version 2>/dev/null; then
# WSL detection
OS="windows"
elif [[ "$PWD" =~ ^/mnt/[a-zA-Z]/ ]]; then
# WSL mount point detection (like /mnt/c/)
OS="windows"
elif [[ "$OSTYPE" == "darwin"* ]]; then
OS="darwin"
elif command -v powershell.exe >/dev/null 2>&1 || command -v cmd.exe >/dev/null 2>&1; then
# Check if Windows executables are available (another Windows indicator)
OS="windows"
elif [[ "$PWD" =~ ^/[a-zA-Z]/ ]] && [[ -d "/c" || -d "/d" || -d "/e" ]]; then
# Check for Windows-style mount points (like in Git Bash)
OS="windows"
else
# Fallback to uname for other systems
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
fi
ARCH=$(uname -m)
# Handle Windows environments (MSYS2, Git Bash, Cygwin, WSL)
case "$OS" in
linux|darwin|windows) ;;
mingw*|msys*|cygwin*)
OS="windows"
;;
*)
echo "Error: Unsupported OS '$OS'. goose currently supports Linux, macOS, and Windows."
exit 1
;;
esac
case "$ARCH" in
x86_64)
ARCH="x86_64"
;;
arm64|aarch64)
# Some systems use 'arm64' and some 'aarch64' standardize to 'aarch64'
ARCH="aarch64"
;;
*)
echo "Error: Unsupported architecture '$ARCH'."
exit 1
;;
esac
# Debug output (safely handle undefined variables)
echo "WINDIR: ${WINDIR:-<not set>}"
echo "OSTYPE: $OSTYPE"
echo "uname -s: $(uname -s)"
echo "uname -m: $(uname -m)"
echo "PWD: $PWD"
# Output the detected OS
echo "Detected OS: $OS with ARCH $ARCH"
# Build the filename and URL for the stable release
if [ "$OS" = "darwin" ]; then
FILE="goose-$ARCH-apple-darwin.tar.bz2"
EXTRACT_CMD="tar"
elif [ "$OS" = "windows" ]; then
# Windows only supports x86_64 currently
if [ "$ARCH" != "x86_64" ]; then
echo "Error: Windows currently only supports x86_64 architecture."
exit 1
fi
FILE="goose-$ARCH-pc-windows-gnu.zip"
EXTRACT_CMD="unzip"
OUT_FILE="goose.exe"
else
FILE="goose-$ARCH-unknown-linux-gnu.tar.bz2"
EXTRACT_CMD="tar"
fi
DOWNLOAD_URL="https://github.com/$REPO/releases/download/$RELEASE_TAG/$FILE"
# --- 4) Download & extract 'goose' binary ---
echo "Downloading $RELEASE_TAG release: $FILE..."
if ! curl -sLf "$DOWNLOAD_URL" --output "$FILE"; then
echo "Error: Failed to download $DOWNLOAD_URL"
exit 1
fi
# Create a temporary directory for extraction
TMP_DIR="/tmp/goose_install_$RANDOM"
if ! mkdir -p "$TMP_DIR"; then
echo "Error: Could not create temporary extraction directory"
exit 1
fi
# Clean up temporary directory
trap 'rm -rf "$TMP_DIR"' EXIT
echo "Extracting $FILE to temporary directory..."
set +e # Disable immediate exit on error
if [ "$EXTRACT_CMD" = "tar" ]; then
tar -xjf "$FILE" -C "$TMP_DIR" 2> tar_error.log
extract_exit_code=$?
# Check for tar errors
if [ $extract_exit_code -ne 0 ]; then
if grep -iEq "missing.*bzip2|bzip2.*missing|bzip2.*No such file|No such file.*bzip2" tar_error.log; then
echo "Error: Failed to extract $FILE. 'bzip2' is required but not installed. See details below:"
else
echo "Error: Failed to extract $FILE. See details below:"
fi
cat tar_error.log
rm tar_error.log
exit 1
fi
rm tar_error.log
else
# Use unzip for Windows
unzip -q "$FILE" -d "$TMP_DIR" 2> unzip_error.log
extract_exit_code=$?
# Check for unzip errors
if [ $extract_exit_code -ne 0 ]; then
echo "Error: Failed to extract $FILE. See details below:"
cat unzip_error.log
rm unzip_error.log
exit 1
fi
rm unzip_error.log
fi
set -e # Re-enable immediate exit on error
rm "$FILE" # clean up the downloaded archive
# Determine the extraction directory (handle subdirectory in Windows packages)
# Windows releases may contain files in a 'goose-package' subdirectory
EXTRACT_DIR="$TMP_DIR"
if [ "$OS" = "windows" ] && [ -d "$TMP_DIR/goose-package" ]; then
echo "Found goose-package subdirectory, using that as extraction directory"
EXTRACT_DIR="$TMP_DIR/goose-package"
fi
# Make binary executable
if [ "$OS" = "windows" ]; then
chmod +x "$EXTRACT_DIR/goose.exe"
else
chmod +x "$EXTRACT_DIR/goose"
fi
# --- 5) Install to $GOOSE_BIN_DIR ---
if [ ! -d "$GOOSE_BIN_DIR" ]; then
echo "Creating directory: $GOOSE_BIN_DIR"
mkdir -p "$GOOSE_BIN_DIR"
fi
echo "Moving goose to $GOOSE_BIN_DIR/$OUT_FILE"
if [ "$OS" = "windows" ]; then
mv "$EXTRACT_DIR/goose.exe" "$GOOSE_BIN_DIR/$OUT_FILE"
else
mv "$EXTRACT_DIR/goose" "$GOOSE_BIN_DIR/$OUT_FILE"
fi
# Also move temporal-service and temporal CLI if they exist
if [ "$OS" = "windows" ]; then
if [ -f "$EXTRACT_DIR/temporal-service.exe" ]; then
echo "Moving temporal-service to $GOOSE_BIN_DIR/temporal-service.exe"
mv "$EXTRACT_DIR/temporal-service.exe" "$GOOSE_BIN_DIR/temporal-service.exe"
chmod +x "$GOOSE_BIN_DIR/temporal-service.exe"
fi
# Move temporal CLI if it exists
if [ -f "$EXTRACT_DIR/temporal.exe" ]; then
echo "Moving temporal CLI to $GOOSE_BIN_DIR/temporal.exe"
mv "$EXTRACT_DIR/temporal.exe" "$GOOSE_BIN_DIR/temporal.exe"
chmod +x "$GOOSE_BIN_DIR/temporal.exe"
fi
# Copy Windows runtime DLLs if they exist
for dll in "$EXTRACT_DIR"/*.dll; do
if [ -f "$dll" ]; then
echo "Moving Windows runtime DLL: $(basename "$dll")"
mv "$dll" "$GOOSE_BIN_DIR/"
fi
done
else
if [ -f "$EXTRACT_DIR/temporal-service" ]; then
echo "Moving temporal-service to $GOOSE_BIN_DIR/temporal-service"
mv "$EXTRACT_DIR/temporal-service" "$GOOSE_BIN_DIR/temporal-service"
chmod +x "$GOOSE_BIN_DIR/temporal-service"
fi
# Move temporal CLI if it exists
if [ -f "$EXTRACT_DIR/temporal" ]; then
echo "Moving temporal CLI to $GOOSE_BIN_DIR/temporal"
mv "$EXTRACT_DIR/temporal" "$GOOSE_BIN_DIR/temporal"
chmod +x "$GOOSE_BIN_DIR/temporal"
fi
fi
# skip configuration for non-interactive installs e.g. automation, docker
if [ "$CONFIGURE" = true ]; then
# --- 6) Configure goose (Optional) ---
echo ""
echo "Configuring goose"
echo ""
"$GOOSE_BIN_DIR/$OUT_FILE" configure
else
echo "Skipping 'goose configure', you may need to run this manually later"
fi
# --- 7) Check PATH and give instructions if needed ---
if [[ ":$PATH:" != *":$GOOSE_BIN_DIR:"* ]]; then
echo ""
echo "Warning: goose installed, but $GOOSE_BIN_DIR is not in your PATH."
if [ "$OS" = "windows" ]; then
echo "To add goose to your PATH in PowerShell:"
echo ""
echo "# Add to your PowerShell profile"
echo '$profilePath = $PROFILE'
echo 'if (!(Test-Path $profilePath)) { New-Item -Path $profilePath -ItemType File -Force }'
echo 'Add-Content -Path $profilePath -Value ''$env:PATH = "$env:USERPROFILE\.local\bin;$env:PATH"'''
echo "# Reload profile or restart PowerShell"
echo '. $PROFILE'
echo ""
echo "Alternatively, you can run:"
echo " goose configure"
echo "or rerun this install script after updating your PATH."
else
SHELL_NAME=$(basename "$SHELL")
echo ""
echo "The \$GOOSE_BIN_DIR is not in your PATH."
echo "What would you like to do?"
echo "1) Add it for me"
echo "2) I'll add it myself, show instructions"
read -p "Enter choice [1/2]: " choice
case "$choice" in
1)
RC_FILE="$HOME/.${SHELL_NAME}rc"
echo "Adding \$GOOSE_BIN_DIR to $RC_FILE..."
echo "export PATH=\"$GOOSE_BIN_DIR:\$PATH\"" >> "$RC_FILE"
echo "Done! Reload your shell or run 'source $RC_FILE' to apply changes."
;;
2)
echo ""
echo "Add it to your PATH by editing ~/.${SHELL_NAME}rc or similar:"
echo " export PATH=\"$GOOSE_BIN_DIR:\$PATH\""
echo "Then reload your shell (e.g. 'source ~/.${SHELL_NAME}rc') to apply changes."
;;
*)
echo "Invalid choice. Please add \$GOOSE_BIN_DIR to your PATH manually."
;;
esac
fi
echo ""
fi