diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 00000000..4bb3d125 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,623 @@ +#!/bin/bash + +set -e + +# Define color output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Define log functions +log_info() { + echo -e "${BLUE}ℹ️ $1${NC}" +} + +log_success() { + echo -e "${GREEN}✅ $1${NC}" +} + +log_warning() { + echo -e "${YELLOW}⚠️ $1${NC}" +} + +log_error() { + echo -e "${RED}❌ $1${NC}" +} + +# Check if command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Check if it's a development machine environment +is_dev_machine() { + # Check if development machine specific directories or files exist + if [ -d "/apsara" ] || [ -d "/home/admin" ] || [ -f "/etc/redhat-release" ]; then + return 0 + fi + return 1 +} + +# Check if running in WSL +is_wsl() { + if [ -f /proc/version ] && grep -qi microsoft /proc/version; then + return 0 + fi + if [ -n "$WSL_DISTRO_NAME" ] || [ -n "$WSL_INTEROP" ]; then + return 0 + fi + return 1 +} + +# Get Windows npm prefix (for Git Bash/Cygwin/MSYS) +get_windows_npm_prefix() { + # Try to get npm prefix + local npm_prefix=$(npm config get prefix 2>/dev/null || echo "") + + if [ -n "$npm_prefix" ]; then + echo "$npm_prefix" + return 0 + fi + + # Try common Windows npm locations + if [ -n "$APPDATA" ]; then + echo "$APPDATA/npm" + return 0 + fi + + # Try Program Files + if [ -d "/c/Program Files/nodejs" ]; then + echo "/c/Program Files/nodejs" + return 0 + fi + + # Fallback + echo "$HOME/.npm-global" +} + +# Get shell configuration file +get_shell_profile() { + local current_shell=$(basename "$SHELL") + case "$current_shell" in + bash) + echo "$HOME/.bashrc" + ;; + zsh) + echo "$HOME/.zshrc" + ;; + fish) + echo "$HOME/.config/fish/config.fish" + ;; + *) + echo "$HOME/.profile" + ;; + esac +} + +# Clean npm configuration conflicts +clean_npmrc_conflict() { + local npmrc="$HOME/.npmrc" + if [[ -f "$npmrc" ]]; then + log_info "Cleaning npmrc conflicts..." + grep -Ev '^(prefix|globalconfig) *= *' "$npmrc" > "${npmrc}.tmp" && mv -f "${npmrc}.tmp" "$npmrc" || true + fi +} + +# Install nvm +install_nvm() { + local NVM_DIR="${NVM_DIR:-$HOME/.nvm}" + local NVM_VERSION="${NVM_VERSION:-v0.40.3}" + + if [ -s "$NVM_DIR/nvm.sh" ]; then + log_info "nvm is already installed at $NVM_DIR" + return 0 + fi + + log_info "Installing nvm ${NVM_VERSION}..." + + # Install nvm using official installer + if curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh | bash; then + log_success "nvm installed successfully" + + # Load nvm for current session + export NVM_DIR="${NVM_DIR}" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + + return 0 + else + log_error "Failed to install nvm" + return 1 + fi +} + +# Install Node.js +install_nodejs_with_nvm() { + local NODE_VERSION="${NODE_VERSION:-22}" + local NVM_DIR="${NVM_DIR:-$HOME/.nvm}" + + # Ensure nvm is loaded + export NVM_DIR="${NVM_DIR}" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + + if ! command_exists nvm; then + log_error "nvm not loaded properly" + return 1 + fi + + # Check if xz needs to be installed + if ! command_exists xz; then + log_warning "xz not found, trying to install xz-utils..." + if command_exists yum; then + sudo yum install -y xz || log_warning "Failed to install xz, continuing anyway..." + elif command_exists apt-get; then + sudo apt-get update && sudo apt-get install -y xz-utils || log_warning "Failed to install xz, continuing anyway..." + fi + fi + + # Set Node.js mirror source (for domestic network) + export NVM_NODEJS_ORG_MIRROR="https://npmmirror.com/mirrors/node" + + # Clear cache + log_info "Clearing nvm cache..." + nvm cache clear || true + + # Install Node.js + log_info "Installing Node.js v${NODE_VERSION}..." + if nvm install ${NODE_VERSION}; then + nvm alias default ${NODE_VERSION} + nvm use default + log_success "Node.js v${NODE_VERSION} installed successfully" + + # Verify installation + log_info "Node.js version: $(node -v)" + log_info "npm version: $(npm -v)" + + # Clean npm configuration conflicts + clean_npmrc_conflict + + # Configure npm mirror source + npm config set registry https://registry.npmmirror.com + log_info "npm registry set to npmmirror" + + return 0 + else + log_error "Failed to install Node.js" + return 1 + fi +} + +# Check Node.js version +check_node_version() { + if ! command_exists node; then + return 1 + fi + + local current_version=$(node -v | sed 's/v//') + local major_version=$(echo $current_version | cut -d. -f1) + + if [ "$major_version" -ge 20 ]; then + log_success "Node.js v$current_version is already installed (>= 20)" + return 0 + else + log_warning "Node.js v$current_version is installed but version < 20" + return 1 + fi +} + +# Install Node.js +install_nodejs() { + local platform=$(uname -s) + + # Check if running in WSL (treat as Linux) + if is_wsl; then + log_info "WSL environment detected, treating as Linux..." + platform="Linux" + fi + + case "$platform" in + Linux|Darwin) + log_info "Installing Node.js on $platform..." + + # Install nvm + if ! install_nvm; then + log_error "Failed to install nvm" + return 1 + fi + + # Load nvm + export NVM_DIR="${HOME}/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + + # Install Node.js + if ! install_nodejs_with_nvm; then + log_error "Failed to install Node.js" + return 1 + fi + + ;; + MINGW*|CYGWIN*|MSYS*) + log_warning "Windows platform detected (Git Bash/Cygwin/MSYS)" + log_info "For Windows, we recommend:" + log_info " 1. Install Node.js from https://nodejs.org/en/download/ (recommended)" + log_info " 2. Or use WSL (Windows Subsystem for Linux)" + log_info "" + + # Check if Node.js is already installed + if command_exists node; then + local node_version=$(node -v) + log_info "Node.js $node_version is already installed" + if check_node_version; then + log_success "Node.js version is compatible (>= 20)" + return 0 + else + log_warning "Node.js version is too old. Please upgrade from https://nodejs.org/en/download/" + return 1 + fi + else + log_error "Node.js is not installed. Please install it from https://nodejs.org/en/download/" + log_info "After installing Node.js, you can run this script again to install Qwen Code." + exit 1 + fi + ;; + *) + log_error "Unsupported platform: $platform" + exit 1 + ;; + esac +} + +# Check and update Node.js +check_and_install_nodejs() { + if check_node_version; then + log_info "Using existing Node.js installation" + clean_npmrc_conflict + else + log_warning "Installing or upgrading Node.js..." + install_nodejs + fi +} + +# Uninstall existing Qwen Code +uninstall_existing_qwen_code() { + local platform=$(uname -s) + + # Check if running in WSL + if is_wsl; then + platform="Linux" + fi + + # Check if package is installed (even if command doesn't exist) + local npm_prefix=$(npm config get prefix 2>/dev/null || echo "") + local node_modules_dir="" + + if [ -n "$npm_prefix" ]; then + # Handle Windows paths + case "$platform" in + MINGW*|CYGWIN*|MSYS*) + # Windows: npm prefix might be like C:\Users\... or /c/Users/... + node_modules_dir="$npm_prefix/node_modules/@qwen-code/qwen-code" + ;; + *) + # Unix-like: standard location + node_modules_dir="$npm_prefix/lib/node_modules/@qwen-code/qwen-code" + ;; + esac + else + # Try common locations + case "$platform" in + MINGW*|CYGWIN*|MSYS*) + # Windows locations + local win_prefix=$(get_windows_npm_prefix) + node_modules_dir="$win_prefix/node_modules/@qwen-code/qwen-code" + ;; + *) + # Unix-like locations + if [ -d "$HOME/.nvm" ]; then + # Find node version directory + local node_version=$(node -v 2>/dev/null | sed 's/v//' || echo "") + if [ -n "$node_version" ]; then + node_modules_dir="$HOME/.nvm/versions/node/v${node_version}/lib/node_modules/@qwen-code/qwen-code" + fi + fi + if [ -z "$node_modules_dir" ] || [ ! -d "$node_modules_dir" ]; then + node_modules_dir="/usr/local/lib/node_modules/@qwen-code/qwen-code" + fi + ;; + esac + fi + + if command_exists qwen || [ -d "$node_modules_dir" ]; then + log_warning "Existing Qwen Code installation detected" + + # Try to get current version + local current_version=$(qwen --version 2>/dev/null || echo "unknown") + if [ "$current_version" != "unknown" ]; then + log_info "Current version: $current_version" + fi + + log_info "Uninstalling existing Qwen Code..." + + # Try npm uninstall first + if npm uninstall -g @qwen-code/qwen-code 2>/dev/null; then + log_success "Successfully uninstalled existing Qwen Code via npm" + else + log_warning "npm uninstall failed or returned non-zero, trying manual removal..." + fi + + # Always try to manually remove the module directory and binaries + case "$platform" in + MINGW*|CYGWIN*|MSYS*) + # Windows platform (Git Bash/Cygwin/MSYS) + local win_npm_prefix=$(get_windows_npm_prefix) + + # Windows binary locations + local common_paths=() + + # Try npm prefix locations + if [ -n "$win_npm_prefix" ]; then + common_paths+=( + "$win_npm_prefix/qwen" + "$win_npm_prefix/qwen.cmd" + "$win_npm_prefix/qwen.ps1" + ) + fi + + # Try APPDATA if available + if [ -n "$APPDATA" ]; then + common_paths+=( + "$APPDATA/npm/qwen" + "$APPDATA/npm/qwen.cmd" + "$APPDATA/npm/qwen.ps1" + ) + fi + + # Try Program Files + if [ -d "/c/Program Files/nodejs" ]; then + common_paths+=( + "/c/Program Files/nodejs/qwen" + "/c/Program Files/nodejs/qwen.cmd" + ) + fi + + # Remove binaries + for bin_path in "${common_paths[@]}"; do + if [ -f "$bin_path" ] || [ -L "$bin_path" ]; then + rm -f "$bin_path" 2>/dev/null && log_info "Removed $bin_path" || log_warning "Could not remove $bin_path" + fi + done + ;; + *) + # Unix-like platforms (Linux/macOS/WSL) + local unix_npm_prefix=$(npm config get prefix 2>/dev/null || echo "$HOME/.npm-global") + local bin_path="$unix_npm_prefix/bin/qwen" + + # Remove qwen binary if exists + if [ -f "$bin_path" ] || [ -L "$bin_path" ]; then + rm -f "$bin_path" && log_info "Removed $bin_path" + fi + + # Remove from common Unix locations + local common_paths=( + "/usr/local/bin/qwen" + "$HOME/.npm-global/bin/qwen" + "$HOME/.local/bin/qwen" + ) + + for path in "${common_paths[@]}"; do + if [ -f "$path" ] || [ -L "$path" ]; then + rm -f "$path" && log_info "Removed $path" + fi + done + ;; + esac + + for path in "${common_paths[@]}"; do + if [ -f "$path" ]; then + rm -f "$path" && log_info "Removed $path" + fi + done + + # Remove the npm module directory if it exists + if [ -n "$node_modules_dir" ] && [ -d "$node_modules_dir" ]; then + log_info "Removing module directory: $node_modules_dir" + rm -rf "$node_modules_dir" 2>/dev/null && log_info "Removed module directory" || log_warning "Could not remove module directory (may require sudo)" + fi + + # Also try to find and remove from nvm directories + if [ -d "$HOME/.nvm" ]; then + for nvm_node_dir in "$HOME/.nvm/versions/node"/*/lib/node_modules/@qwen-code/qwen-code; do + if [ -d "$nvm_node_dir" ]; then + log_info "Removing nvm module directory: $nvm_node_dir" + rm -rf "$nvm_node_dir" 2>/dev/null && log_info "Removed nvm module directory" || log_warning "Could not remove nvm module directory" + fi + done + fi + + # Verify uninstallation + if command_exists qwen; then + log_warning "Qwen Code command still exists after uninstall attempt. Attempting to locate and remove it..." + + # Find the qwen executable + local qwen_path=$(which qwen 2>/dev/null) + if [ -n "$qwen_path" ] && [ -f "$qwen_path" ]; then + log_info "Found qwen executable at: $qwen_path" + if rm -f "$qwen_path"; then + log_success "Successfully removed qwen executable: $qwen_path" + else + log_error "Failed to remove qwen executable: $qwen_path (may require sudo)" + fi + else + log_warning "Could not locate qwen executable path" + fi + fi + + # Final check + if command_exists qwen; then + log_warning "Qwen Code command still exists. You may need to reload your shell." + else + log_success "Successfully removed existing Qwen Code" + fi + fi +} + +# Install Qwen Code +install_qwen_code() { + # Uninstall existing installation first + uninstall_existing_qwen_code + + # Additional cleanup: ensure module directory is completely removed + local platform=$(uname -s) + if is_wsl; then + platform="Linux" + fi + + local npm_prefix=$(npm config get prefix 2>/dev/null || echo "") + local module_dir="" + + if [ -n "$npm_prefix" ]; then + case "$platform" in + MINGW*|CYGWIN*|MSYS*) + module_dir="$npm_prefix/node_modules/@qwen-code/qwen-code" + ;; + *) + module_dir="$npm_prefix/lib/node_modules/@qwen-code/qwen-code" + ;; + esac + else + # Try to find node version directory + case "$platform" in + MINGW*|CYGWIN*|MSYS*) + local win_prefix=$(get_windows_npm_prefix) + module_dir="$win_prefix/node_modules/@qwen-code/qwen-code" + ;; + *) + if [ -d "$HOME/.nvm" ]; then + local node_version=$(node -v 2>/dev/null | sed 's/v//' || echo "") + if [ -n "$node_version" ]; then + module_dir="$HOME/.nvm/versions/node/v${node_version}/lib/node_modules/@qwen-code/qwen-code" + fi + fi + ;; + esac + fi + + # Force remove module directory if it still exists + if [ -n "$module_dir" ] && [ -d "$module_dir" ]; then + log_warning "Module directory still exists, forcing removal..." + rm -rf "$module_dir" 2>/dev/null || { + log_warning "Could not remove module directory. Trying with npm cache clean..." + npm cache clean --force 2>/dev/null || true + } + fi + + # Small delay to ensure filesystem operations complete + sleep 1 + + log_info "Installing Qwen Code..." + + # Install Qwen Code with force flag to handle any conflicts + if npm i -g @qwen-code/qwen-code@latest --force; then + log_success "Qwen Code installed successfully!" + + # Verify installation + if command_exists qwen; then + log_info "Qwen Code version: $(qwen --version 2>/dev/null || echo 'version info not available')" + else + log_warning "Qwen Code installed but command not found. You may need to reload your shell or add npm global bin to PATH." + log_info "Try running: export PATH=\"\$PATH:$(npm config get prefix)/bin\"" + fi + else + log_error "Failed to install Qwen Code!" + log_info "You may need to manually remove the old installation:" + if [ -n "$module_dir" ]; then + log_info " rm -rf $module_dir" + fi + exit 1 + fi +} + +# Main function +main() { + echo "==========================================" + echo " Qwen Code Installation Script" + echo " One-Click Installation for Everyone" + echo "==========================================" + echo "" + + # Check system + local platform=$(uname -s) + log_info "System: $platform $(uname -r)" + log_info "Shell: $(basename "$SHELL")" + + if is_wsl; then + log_info "WSL (Windows Subsystem for Linux) detected" + fi + + if is_dev_machine; then + log_info "Development machine environment detected" + fi + + # Windows-specific guidance + case "$platform" in + MINGW*|CYGWIN*|MSYS*) + log_info "" + log_info "Note: You're running this script in Git Bash/Cygwin/MSYS" + log_info "Make sure Node.js is installed and accessible from this environment" + log_info "" + ;; + esac + + # Check and install Node.js + check_and_install_nodejs + + # Ensure npm command is available + if ! command_exists npm; then + log_error "npm command not found after Node.js installation!" + log_info "Please run: source $(get_shell_profile)" + exit 1 + fi + + # Install Qwen Code + install_qwen_code + + echo "" + echo "==========================================" + log_success "Installation completed successfully!" + echo "==========================================" + echo "" + + log_info "To start using Qwen Code, run:" + local current_shell=$(basename "$SHELL") + case "$current_shell" in + bash) + echo " source ~/.bashrc" + ;; + zsh) + echo " source ~/.zshrc" + ;; + fish) + echo " source ~/.config/fish/config.fish" + ;; + *) + echo " source ~/.profile # or reload your shell" + ;; + esac + echo " qwen" + echo "" + + # Try to run Qwen Code + if command_exists qwen; then + log_info "Qwen Code is ready to use!" + log_info "Run 'qwen' to start, or visit https://github.com/QwenLM/qwen-code for more information." + else + log_info "Please reload your shell and run 'qwen' command." + fi +} + +# Error handling +trap 'log_error "An error occurred. Installation aborted."; exit 1' ERR + +# Run main function +main