mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
624 lines
20 KiB
Bash
624 lines
20 KiB
Bash
#!/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
|