From 8b7620b262dfb7cf8c246b63950c3ee421ac1824 Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 24 Jun 2025 15:31:50 +0800 Subject: [PATCH] feat: Enhance run-server.sh with uv-first Python environment setup (#129) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Enhance run-server.sh with uv-first Python environment setup - Implement uv-first approach for faster environment setup when available - Add automatic WSL environment detection using wslvar for better Windows integration - Improve system package installation by using bash -c for better command execution - Add comprehensive environment validation and fallback mechanisms - Optimize dependency installation with uv when environment supports it - Enhance configuration display workflow for better user experience - Add environment markers to track uv-created environments - Improve error handling and user messaging throughout setup process 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude * feat: Add .claude/settings.local.json to .gitignore Personal Claude Code settings should not be tracked in source control. Reference: https://docs.anthropic.com/en/docs/claude-code/settings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude * feat: Add shared Claude Code settings.json for team defaults - Add .claude/settings.json with default permissions for team use - Remove .claude/settings.local.json from git tracking (now personal config) Reference: https://docs.anthropic.com/en/docs/claude-code/settings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude * fix: Address Gemini Code Assist review issues in run-server.sh - Fix hardcoded Unix paths (lines 563 & 568) with cross-platform detection - Improve error handling by capturing uv stderr instead of /dev/null - Fix uv environment detection logic to check uv_created marker file Resolves three issues identified in PR review: 1. High Priority: Replace hardcoded $VENV_PATH/bin/python with detection 2. Medium Priority: Capture and display uv command errors for debugging 3. Medium Priority: Use marker file instead of path matching for uv detection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude * fix: Address additional Gemini Code Assist feedback - Enhance WSL warning message with more helpful guidance - Add security comment explaining bash -c usage over eval - Add get_venv_python_path helper function for cleaner cross-platform detection - Improve path resolution error handling with proper error checking Addresses 4 additional review points from gemini-code-assist to improve user experience, code clarity, and error handling robustness. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --------- Co-authored-by: Claude --- .claude/settings.json | 7 ++ .claude/settings.local.json | 18 ---- .gitignore | 3 + run-server.sh | 164 ++++++++++++++++++++++++++++-------- 4 files changed, 141 insertions(+), 51 deletions(-) create mode 100644 .claude/settings.json delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..8ee1dfe --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + ], + "deny": [] + } +} \ No newline at end of file diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index f374e06..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "permissions": { - "allow": [ - "mcp__gemini__review_code", - "mcp__gemini__chat", - "mcp__gemini__analyze", - "Bash(find:*)", - "mcp__gemini__review_changes", - "Bash(python test_resolve.py:*)", - "Bash(python3:*)", - "Bash(cat:*)", - "Bash(grep:*)", - "Bash(source:*)", - "Bash(rm:*)" - ], - "deny": [] - } -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index fd961db..759d3a3 100644 --- a/.gitignore +++ b/.gitignore @@ -175,6 +175,9 @@ FEATURE_*.md # Local user instructions CLAUDE.local.md +# Claude Code personal settings +.claude/settings.local.json + # Standalone mode files .zen_venv/ .docker_cleaned diff --git a/run-server.sh b/run-server.sh index 243f0e0..5fa9fe3 100755 --- a/run-server.sh +++ b/run-server.sh @@ -78,6 +78,20 @@ clear_python_cache() { # Platform Detection Functions # ---------------------------------------------------------------------------- +# Get cross-platform Python executable path from venv +get_venv_python_path() { + local venv_path="$1" + + # Check for both Unix and Windows Python executable paths + if [[ -f "$venv_path/bin/python" ]]; then + echo "$venv_path/bin/python" + elif [[ -f "$venv_path/Scripts/python.exe" ]]; then + echo "$venv_path/Scripts/python.exe" + else + return 1 # No Python executable found + fi +} + # Detect the operating system detect_os() { case "$OSTYPE" in @@ -106,7 +120,17 @@ get_claude_config_path() { echo "$HOME/.config/Claude/claude_desktop_config.json" ;; wsl) - echo "/mnt/c/Users/$USER/AppData/Roaming/Claude/claude_desktop_config.json" + local win_appdata + if command -v wslvar &> /dev/null; then + win_appdata=$(wslvar APPDATA 2>/dev/null) + fi + + if [[ -n "$win_appdata" ]]; then + echo "$(wslpath "$win_appdata")/Claude/claude_desktop_config.json" + else + print_warning "Could not determine Windows user path automatically. Please ensure APPDATA is set correctly or provide the full path manually." + echo "/mnt/c/Users/$USER/AppData/Roaming/Claude/claude_desktop_config.json" + fi ;; windows) echo "$APPDATA/Claude/claude_desktop_config.json" @@ -469,7 +493,7 @@ try_install_system_packages() { # Check if we can use sudo if can_use_sudo; then print_info "Installing system packages (this may ask for your password)..." - if eval "$install_cmd" >/dev/null 2>&1; then + if bash -c "$install_cmd" >/dev/null 2>&1; then # Replaced eval to prevent command injection print_success "System packages installed successfully" return 0 else @@ -534,6 +558,78 @@ bootstrap_pip() { return 1 } +# Setup environment using uv-first approach +setup_environment() { + local venv_python="" + + # Try uv-first approach + if command -v uv &> /dev/null; then + print_info "Setting up environment with uv..." + + # Only remove existing venv if it wasn't created by uv (to ensure clean uv setup) + if [[ -d "$VENV_PATH" ]] && [[ ! -f "$VENV_PATH/uv_created" ]]; then + print_info "Removing existing environment for clean uv setup..." + rm -rf "$VENV_PATH" + fi + + # Try Python 3.12 first (preferred) + local uv_output + if uv_output=$(uv venv --python 3.12 "$VENV_PATH" 2>&1); then + # Use helper function for cross-platform path detection + if venv_python=$(get_venv_python_path "$VENV_PATH"); then + touch "$VENV_PATH/uv_created" # Mark as uv-created + print_success "Created environment with uv using Python 3.12" + else + print_warning "uv succeeded but Python executable not found in venv" + fi + # Fall back to any available Python through uv + elif uv_output=$(uv venv "$VENV_PATH" 2>&1); then + # Use helper function for cross-platform path detection + if venv_python=$(get_venv_python_path "$VENV_PATH"); then + touch "$VENV_PATH/uv_created" # Mark as uv-created + local python_version=$($venv_python --version 2>&1) + print_success "Created environment with uv using $python_version" + else + print_warning "uv succeeded but Python executable not found in venv" + fi + else + print_warning "uv environment creation failed, falling back to system Python detection" + print_warning "uv output: $uv_output" + fi + else + print_info "uv not found, using system Python detection" + fi + + # If uv failed or not available, fallback to system Python detection + if [[ -z "$venv_python" ]]; then + print_info "Setting up environment with system Python..." + local python_cmd + python_cmd=$(find_python) || return 1 + + # Use existing venv creation logic + venv_python=$(setup_venv "$python_cmd") + if [[ $? -ne 0 ]]; then + return 1 + fi + else + # venv_python was already set by uv creation above, just convert to absolute path + if [[ -n "$venv_python" ]]; then + # Convert to absolute path for MCP registration + local abs_venv_python + if cd "$(dirname "$venv_python")" 2>/dev/null; then + abs_venv_python=$(pwd)/$(basename "$venv_python") + venv_python="$abs_venv_python" + else + print_error "Failed to resolve absolute path for venv_python" + return 1 + fi + fi + fi + + echo "$venv_python" + return 0 +} + # Setup virtual environment setup_venv() { local python_cmd="$1" @@ -659,8 +755,8 @@ setup_venv() { exit 1 fi - # Check if pip exists in the virtual environment - if [[ ! -f "$venv_pip" ]] && ! $venv_python -m pip --version &>/dev/null 2>&1; then + # Check if pip exists in the virtual environment (skip check if using uv-created environment) + if [[ ! -f "$VENV_PATH/uv_created" ]] && [[ ! -f "$venv_pip" ]] && ! $venv_python -m pip --version &>/dev/null 2>&1; then # On Linux, try to install system packages if pip is missing local os_type=$(detect_os) if [[ "$os_type" == "linux" || "$os_type" == "wsl" ]]; then @@ -742,8 +838,8 @@ install_dependencies() { local python_cmd="$1" local deps_needed=false - # First verify pip is available - if ! $python_cmd -m pip --version &>/dev/null 2>&1; then + # First verify pip is available (skip check if using uv) + if [[ ! -f "$VENV_PATH/uv_created" ]] && ! $python_cmd -m pip --version &>/dev/null 2>&1; then print_error "pip is not available in the Python environment" echo "" echo "This indicates an incomplete Python installation." @@ -775,9 +871,16 @@ install_dependencies() { echo " • Environment configuration" echo "" - # Determine if we're in a venv + # Determine installation method - prefer uv if available and we're in a uv-created environment local install_cmd - if [[ -n "${VIRTUAL_ENV:-}" ]] || [[ "$python_cmd" == *"$VENV_PATH"* ]]; then + local use_uv=false + + if command -v uv &> /dev/null && [[ -f "$VENV_PATH/uv_created" ]]; then + # Use uv for faster installation if environment was created by uv + install_cmd="uv pip install -q -r requirements.txt --python $python_cmd" + use_uv=true + print_info "Using uv for faster package installation..." + elif [[ -n "${VIRTUAL_ENV:-}" ]] || [[ "$python_cmd" == *"$VENV_PATH"* ]]; then install_cmd="$python_cmd -m pip install -q -r requirements.txt" else install_cmd="$python_cmd -m pip install -q --user -r requirements.txt" @@ -826,6 +929,10 @@ install_dependencies() { echo " $python_cmd -m pip install --user -r requirements.txt" else echo "Try running manually:" + if [[ "$use_uv" == true ]]; then + echo " uv pip install -r requirements.txt --python $python_cmd" + echo "Or fallback to pip:" + fi echo " $python_cmd -m pip install -r requirements.txt" echo "" echo "Or install individual packages:" @@ -1319,11 +1426,10 @@ main() { ;; -c|--config) # Setup minimal environment to get paths for config display + echo "Setting up environment for configuration display..." + echo "" local python_cmd - python_cmd=$(find_python) || exit 1 - local new_python_cmd - new_python_cmd=$(setup_venv "$python_cmd") - python_cmd="$new_python_cmd" + python_cmd=$(setup_environment) || exit 1 local script_dir=$(get_script_dir) local server_path="$script_dir/server.py" display_config_instructions "$python_cmd" "$server_path" @@ -1372,51 +1478,43 @@ main() { # Step 1.5: Clear Python cache to prevent import issues clear_python_cache - # Step 2: Find Python - local python_cmd - if ! python_cmd=$(find_python); then - # find_python already printed error messages, just exit - exit 1 - fi - - # Step 3: Setup environment file + # Step 2: Setup environment file setup_env_file || exit 1 - # Step 4: Source .env file + # Step 3: Source .env file if [[ -f .env ]]; then set -a source .env set +a fi - # Step 5: Validate API keys + # Step 4: Validate API keys validate_api_keys || exit 1 - # Step 6: Setup virtual environment - local new_python_cmd - new_python_cmd=$(setup_venv "$python_cmd") - python_cmd="$new_python_cmd" + # Step 5: Setup Python environment (uv-first approach) + local python_cmd + python_cmd=$(setup_environment) || exit 1 - # Step 7: Install dependencies + # Step 6: Install dependencies install_dependencies "$python_cmd" || exit 1 - # Step 8: Get absolute server path + # Step 7: Get absolute server path local script_dir=$(get_script_dir) local server_path="$script_dir/server.py" - # Step 9: Display setup instructions + # Step 8: Display setup instructions display_setup_instructions "$python_cmd" "$server_path" - # Step 10: Check Claude integrations + # Step 9: Check Claude integrations check_claude_cli_integration "$python_cmd" "$server_path" check_claude_desktop_integration "$python_cmd" "$server_path" - # Step 11: Display log information + # Step 10: Display log information echo "" echo "Logs will be written to: $script_dir/$LOG_DIR/$LOG_FILE" echo "" - # Step 12: Handle command line arguments + # Step 11: Handle command line arguments if [[ "$arg" == "-f" ]] || [[ "$arg" == "--follow" ]]; then follow_logs else @@ -1433,4 +1531,4 @@ main() { # ---------------------------------------------------------------------------- # Run main function with all arguments -main "$@" \ No newline at end of file +main "$@"