#!/bin/bash # Tea CLI Installer # Downloads and installs the latest Tea CLI binary # Supports multiple platforms and installation methods set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration GITEA_REPO="gitea.com/gitea/tea" USER_BIN_DIR="$HOME/bin" SYSTEM_INSTALL_DIR="/usr/local/bin" TEMP_DIR="/tmp/tea-install" # Function to print colored output print_status() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } # Function to check if command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Function to detect OS and package manager detect_os() { if [[ -f /etc/os-release ]]; then . /etc/os-release OS="$ID" OS_VERSION="$VERSION_ID" else print_error "Cannot detect operating system" exit 1 fi # Detect package manager if command_exists apt; then PKG_MANAGER="apt" elif command_exists yum; then PKG_MANAGER="yum" elif command_exists dnf; then PKG_MANAGER="dnf" elif command_exists pacman; then PKG_MANAGER="pacman" elif command_exists zypper; then PKG_MANAGER="zypper" else PKG_MANAGER="unknown" fi print_status "Detected OS: $OS $OS_VERSION" print_status "Package manager: $PKG_MANAGER" } # Function to get system architecture get_arch() { ARCH=$(uname -m) case $ARCH in x86_64) echo "amd64" ;; aarch64|arm64) echo "arm64" ;; armv7l) echo "arm" ;; *) print_error "Unsupported architecture: $ARCH" exit 1 ;; esac } # Function to get current installed version and location get_current_version() { if command_exists tea; then CURRENT_VERSION=$(tea --version 2>/dev/null | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' | head -n 1) if [[ -z "$CURRENT_VERSION" ]]; then CURRENT_VERSION="unknown" fi # Determine installation location TEA_PATH=$(which tea) if [[ "$TEA_PATH" == "$USER_BIN_DIR"* ]]; then INSTALL_TYPE="user" elif [[ "$TEA_PATH" == "/usr/local/bin"* ]] || [[ "$TEA_PATH" == "/usr/bin"* ]]; then INSTALL_TYPE="system" else INSTALL_TYPE="other" fi else CURRENT_VERSION="not_installed" INSTALL_TYPE="none" fi echo "$CURRENT_VERSION:$INSTALL_TYPE" } # Function to check if user bin directory exists and is in PATH check_user_bin_setup() { if [[ ! -d "$USER_BIN_DIR" ]]; then print_status "User bin directory doesn't exist" return 1 fi # Check if user bin is in PATH if [[ ":$PATH:" != *":$USER_BIN_DIR:"* ]]; then print_warning "User bin directory ($USER_BIN_DIR) is not in PATH" print_status "Add to PATH: export PATH=\"$USER_BIN_DIR:\$PATH\"" return 1 fi return 0 } # Function to check for version-specific symlink check_version_symlink() { local tea_path=$(which tea 2>/dev/null) if [[ -z "$tea_path" ]]; then return 1 fi # Check if it's a symlink if [[ -L "$tea_path" ]]; then SYMLINK_TARGET=$(readlink "$tea_path") # Extract version from symlink target if [[ "$SYMLINK_TARGET" =~ tea-(v?[0-9]+\.[0-9]+\.[0-9]+) ]]; then CURRENT_SYMLINK_VERSION="${BASH_REMATCH[1]}" return 0 fi fi return 1 } # Function to determine installation strategy determine_install_strategy() { local current_info=$(get_current_version) local current_version=$(echo "$current_info" | cut -d: -f1) local install_type=$(echo "$current_info" | cut -d: -f2) # Strategy 1: Check user bin directory first if check_user_bin_setup; then print_status "User bin directory is available and in PATH" # Check if tea exists in user bin if [[ -f "$USER_BIN_DIR/tea" ]]; then print_status "Tea CLI found in user bin directory" # Check for version symlink if check_version_symlink; then print_status "Version symlink found: $CURRENT_SYMLINK_VERSION" INSTALL_STRATEGY="user_update" TARGET_DIR="$USER_BIN_DIR" else print_status "No version symlink found, will create one" INSTALL_STRATEGY="user_upgrade" TARGET_DIR="$USER_BIN_DIR" fi else print_status "Tea CLI not found in user bin, will install there" INSTALL_STRATEGY="user_install" TARGET_DIR="$USER_BIN_DIR" fi else print_status "User bin directory not available, using system installation" INSTALL_STRATEGY="system_install" TARGET_DIR="$SYSTEM_INSTALL_DIR" fi print_status "Installation strategy: $INSTALL_STRATEGY" print_status "Target directory: $TARGET_DIR" } # Function to compare versions compare_versions() { local version1="$1" local version2="$2" # Remove 'v' prefix if present version1=${version1#v} version2=${version2#v} # Split versions into arrays IFS='.' read -ra V1 <<< "$version1" IFS='.' read -ra V2 <<< "$version2" # Compare major, minor, patch for i in {0..2}; do local v1=${V1[$i]:-0} local v2=${V2[$i]:-0} if (( v1 > v2 )); then return 0 # version1 > version2 elif (( v1 < v2 )); then return 1 # version1 < version2 fi done return 0 # versions are equal } # Function to get latest release info get_latest_release() { print_status "Fetching latest Tea CLI release information..." if command_exists curl; then RELEASE_INFO=$(curl -s "https://gitea.com/api/v1/repos/gitea/tea/releases/latest") elif command_exists wget; then RELEASE_INFO=$(wget -qO- "https://gitea.com/api/v1/repos/gitea/tea/releases/latest") else print_error "curl or wget is required" exit 1 fi if [[ -z "$RELEASE_INFO" ]]; then print_error "Failed to fetch release information" exit 1 fi # Extract version and download URLs LATEST_VERSION=$(echo "$RELEASE_INFO" | grep -o '"tag_name": *"[^"]*"' | sed -E 's/.*"([^"]+)".*/\1/') if [[ -z "$LATEST_VERSION" ]]; then print_error "Failed to parse release information" exit 1 fi print_success "Latest version: $LATEST_VERSION" } # Function to check if update is needed check_update_needed() { local latest_version="$1" local current_info=$(get_current_version) local current_version=$(echo "$current_info" | cut -d: -f1) local install_type=$(echo "$current_info" | cut -d: -f2) print_status "Current version: $current_version ($install_type)" print_status "Latest version: $latest_version" if [[ "$current_version" == "not_installed" ]]; then print_status "Tea CLI is not installed" return 0 # Need to install fi if [[ "$current_version" == "unknown" ]]; then print_warning "Cannot determine current version, proceeding with update" return 0 # Assume update needed fi # Clean version strings for comparison local clean_current="${current_version#v}" local clean_latest="${latest_version#v}" if compare_versions "$clean_latest" "$clean_current"; then if [[ "$clean_latest" == "$clean_current" ]]; then print_success "You already have the latest version ($current_version)" if [[ "$force_reinstall" == "true" ]]; then print_status "Force reinstall requested, proceeding..." return 0 else return 1 # No update needed fi else print_status "Newer version available: $latest_version > $current_version" return 0 # Update needed fi else print_success "Your version ($current_version) is newer than latest release ($latest_version)" return 1 # No update needed fi } # Function to download file download_file() { local url="$1" local filename="$2" print_status "Downloading $filename..." if command_exists curl; then curl -L -o "$TEMP_DIR/$filename" "$url" elif command_exists wget; then wget -O "$TEMP_DIR/$filename" "$url" else print_error "curl or wget is required" exit 1 fi if [[ ! -f "$TEMP_DIR/$filename" ]]; then print_error "Failed to download $filename" exit 1 fi print_success "Downloaded $filename" } # Function to install Tea CLI from package manager install_tea_from_package_manager() { # Package manager installation only works for system-wide installation if [[ "$INSTALL_STRATEGY" == user* ]]; then print_status "Skipping package manager for user installation" return 1 fi print_status "Attempting to install Tea CLI from package manager..." case $PKG_MANAGER in apt) # Add Gitea repository if not present if ! grep -q "https://dl.gitea.com/gitea/gpg.key" /etc/apt/sources.list /etc/apt/sources.list.d/*.list 2>/dev/null; then print_status "Adding Gitea repository..." curl -fsSL https://dl.gitea.com/gitea/gpg.key | sudo apt-key add - echo "deb https://dl.gitea.com/gitea/ gitea main" | sudo tee /etc/apt/sources.list.d/gitea.list sudo apt update fi sudo apt install -y tea return 0 ;; yum) # Add Gitea repository print_status "Adding Gitea repository..." sudo yum-config-manager --add-repo https://dl.gitea.com/gitea/RPM/GPG.KEY sudo yum-config-manager --add-repo https://dl.gitea.com/gitea/yum/ gitea sudo yum install -y tea return 0 ;; dnf) # Add Gitea repository print_status "Adding Gitea repository..." sudo dnf config-manager --add-repo https://dl.gitea.com/gitea/RPM/GPG.KEY sudo dnf config-manager --add-repo https://dl.gitea.com/gitea/yum/ gitea sudo dnf install -y tea return 0 ;; pacman) # Tea CLI is in AUR print_warning "Tea CLI is available in AUR. Consider using 'yay -S tea' or 'pacaur -S tea'" return 1 ;; zypper) print_status "Adding Gitea repository..." sudo zypper addrepo -G https://dl.gitea.com/gitea/RPM/GPG.KEY sudo zypper addrepo https://dl.gitea.com/gitea/yum/ gitea sudo zypper install -y tea return 0 ;; *) print_warning "Package manager $PKG_MANAGER not supported for automatic installation" return 1 ;; esac } # Function to install Tea CLI from binary install_tea_from_binary() { local arch="$1" local version="$2" local binary_name="tea-${version#v}-linux-${arch}" local download_url="https://gitea.com/gitea/tea/releases/download/$version/$binary_name" download_file "$download_url" "$binary_name" # Determine installation command based on target directory local install_cmd="install -m 755" local symlink_cmd="ln -sf" if [[ "$TARGET_DIR" == "$SYSTEM_INSTALL_DIR" ]]; then install_cmd="sudo $install_cmd" symlink_cmd="sudo $symlink_cmd" fi print_status "Installing Tea CLI binary to $TARGET_DIR..." # Install the binary with version suffix $install_cmd "$TEMP_DIR/$binary_name" "$TARGET_DIR/tea-$version" # Create/update symlink to point to this version $symlink_cmd "$TARGET_DIR/tea-$version" "$TARGET_DIR/tea" print_success "Tea CLI binary installed successfully" print_status "Version symlink: tea -> tea-$version" } # Function to verify Tea CLI installation verify_tea_installation() { print_status "Verifying Tea CLI installation..." if command_exists tea; then local version=$(tea --version 2>/dev/null | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' | head -n 1) print_success "Tea CLI installed: $version" return 0 else print_error "Tea CLI installation verification failed" return 1 fi } # Function to cleanup cleanup() { if [[ -d "$TEMP_DIR" ]]; then rm -rf "$TEMP_DIR" print_status "Cleaned up temporary files" fi } # Function to show usage show_usage() { echo "Tea CLI Installer" echo echo "Usage: $0 [OPTIONS]" echo echo "Options:" echo " -h, --help Show this help message" echo " -v, --version Install specific version (default: latest)" echo " -f, --force Force reinstall even if up-to-date" echo " -p, --package Force installation from package manager" echo " -b, --binary Force installation from binary" echo echo "Examples:" echo " $0 # Install latest version using best method" echo " $0 -v v0.10.0 # Install specific version" echo " $0 --package # Force installation from package manager" echo " $0 --binary # Force installation from binary" echo } # Main installation function main() { local target_version="" local force_reinstall=false local install_method="auto" # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_usage exit 0 ;; -v|--version) target_version="$2" shift 2 ;; -f|--force) force_reinstall=true shift ;; -p|--package) install_method="package" shift ;; -b|--binary) install_method="binary" shift ;; *) print_error "Unknown option: $1" show_usage exit 1 ;; esac done echo "========================================" echo "Tea CLI Installer" echo "========================================" echo # Check if running as root for system installation if [[ $EUID -ne 0 ]]; then print_warning "Not running as root. Some operations may require sudo." fi # Detect system detect_os ARCH=$(get_arch) # Get latest version if not specified if [[ -z "$target_version" ]]; then get_latest_release target_version="$LATEST_VERSION" else print_status "Using specified version: $target_version" fi # Determine installation strategy determine_install_strategy # Check if update is needed if ! check_update_needed "$target_version"; then print_success "No installation needed. Exiting." exit 0 fi # Create temporary directory mkdir -p "$TEMP_DIR" # Trap cleanup on exit trap cleanup EXIT # Installation logic based on method and strategy case $install_method in auto) # For user installations, prefer binary over package manager if [[ "$INSTALL_STRATEGY" == user* ]]; then print_status "Using binary installation for user directory" install_tea_from_binary "$ARCH" "$target_version" verify_tea_installation else # Try package manager first for system installations if install_tea_from_package_manager; then verify_tea_installation else install_tea_from_binary "$ARCH" "$target_version" verify_tea_installation fi fi ;; package) if ! install_tea_from_package_manager; then print_error "Package manager installation failed" exit 1 fi verify_tea_installation ;; binary) install_tea_from_binary "$ARCH" "$target_version" verify_tea_installation ;; esac # Installation completed successfully echo print_success "Installation completed successfully!" echo echo "Installation details:" echo "- Strategy: $INSTALL_STRATEGY" echo "- Location: $TARGET_DIR" if [[ "$INSTALL_STRATEGY" == user* ]]; then echo "- Binary: tea (symlink to tea-$target_version)" echo "- Version: $target_version" fi echo echo "Next steps:" echo "1. Test installation: tea --version" echo "2. Configure login: tea login add" echo "3. Use Tea CLI: tea --help" echo if [[ "$INSTALL_STRATEGY" == user* ]]; then echo "User-specific installation notes:" echo "- Binary installed in: $USER_BIN_DIR" echo "- Ensure $USER_BIN_DIR is in your PATH" echo "- You can have multiple versions side-by-side" fi echo echo "For more information, visit: https://gitea.com/gitea/tea" } # Run main function main "$@"