From 60b795c258a8eee917ae6487acde93bcd47be3ea Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:16:21 +0100 Subject: [PATCH 1/3] Implement fallback installation for 'uv' package Added a fallback installation method for 'uv' using platform-specific scripts for Windows and Unix-like systems. Enhanced error handling and path checking for the 'uv' executable. --- builder/penv_setup.py | 156 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 10 deletions(-) diff --git a/builder/penv_setup.py b/builder/penv_setup.py index e4856269f..a81f53bfd 100644 --- a/builder/penv_setup.py +++ b/builder/penv_setup.py @@ -14,8 +14,10 @@ import json import os +import platform import re import semantic_version +import shutil import site import socket import subprocess @@ -113,6 +115,125 @@ def get_executable_path(penv_dir, executable_name): return str(Path(penv_dir) / scripts_dir / f"{executable_name}{exe_suffix}") +def install_uv_fallback(): + """ + Fallback method to install uv using official installation scripts. + Uses platform-specific installation methods: + - Linux/macOS: curl or wget to download and execute install.sh + - Windows: PowerShell script install.ps1 + + Returns: + str or None: Path to uv executable if successful, None otherwise + """ + system = platform.system() + + try: + if system == "Windows": + # Windows installation using PowerShell script + ps_script = 'irm https://astral.sh/uv/install.ps1 | iex' + cmd = [ + "powershell.exe", + "-ExecutionPolicy", "ByPass", + "-NoProfile", + "-Command", ps_script + ] + + print("Attempting to install uv using official Windows installer...") + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=300 + ) + + if result.returncode != 0: + print(f"Warning: uv installation failed: {result.stderr}") + return None + + # Check for uv in common Windows locations + possible_paths = [ + str(Path.home() / ".local" / "bin" / "uv.exe"), + str(Path.home() / ".cargo" / "bin" / "uv.exe"), + ] + + # Also check PATH + uv_in_path = shutil.which("uv") + if uv_in_path: + print(f"Successfully installed uv using official installer: {uv_in_path}") + return uv_in_path + + for path in possible_paths: + if os.path.isfile(path): + print(f"Successfully installed uv using official installer: {path}") + return path + + else: + # Unix-like systems (Linux, macOS) + install_url = "https://astral.sh/uv/install.sh" + + # Try curl first (most common) + if shutil.which("curl"): + print("Attempting to install uv using official installer (curl)...") + cmd = f"curl -LsSf {install_url} | sh" + result = subprocess.run( + cmd, + shell=True, + capture_output=True, + text=True, + timeout=300 + ) + # Fallback to wget if curl is not available + elif shutil.which("wget"): + print("Attempting to install uv using official installer (wget)...") + cmd = f"wget -qO- {install_url} | sh" + result = subprocess.run( + cmd, + shell=True, + capture_output=True, + text=True, + timeout=300 + ) + else: + print("Warning: Neither curl nor wget available for uv installation") + return None + + if result.returncode != 0: + print(f"Warning: uv installation failed: {result.stderr}") + return None + + # Check for uv in common Unix locations + possible_paths = [ + str(Path.home() / ".local" / "bin" / "uv"), + str(Path.home() / ".cargo" / "bin" / "uv"), + ] + + # Check XDG_BIN_HOME if set + xdg_bin = os.getenv("XDG_BIN_HOME") + if xdg_bin: + possible_paths.insert(0, str(Path(xdg_bin) / "uv")) + + # Also check PATH + uv_in_path = shutil.which("uv") + if uv_in_path: + print(f"Successfully installed uv using official installer: {uv_in_path}") + return uv_in_path + + for path in possible_paths: + if os.path.isfile(path): + print(f"Successfully installed uv using official installer: {path}") + return path + + print("Warning: uv installation script completed but uv executable not found") + return None + + except subprocess.TimeoutExpired: + print("Warning: uv installation timed out") + return None + except Exception as e: + print(f"Warning: uv installation failed with error: {e}") + return None + + def setup_pipenv_in_package(env, penv_dir): """ Checks if 'penv' folder exists in platformio dir and creates virtual environment if not. @@ -279,17 +400,32 @@ def install_python_deps(python_exe, external_uv_executable, uv_cache_dir=None): stderr=subprocess.STDOUT, timeout=300 ) - except subprocess.CalledProcessError as e: - print(f"Error: uv installation via pip failed with exit code {e.returncode}") - return False - except subprocess.TimeoutExpired: - print("Error: uv installation via pip timed out") - return False - except FileNotFoundError: - print("Error: Python executable not found") - return False + uv_in_penv_available = True except Exception as e: - print(f"Error installing uv package manager via pip: {e}") + print(f"Warning: uv installation via pip failed: {e}") + + # Ultimate fallback: Use official installation scripts + if not uv_in_penv_available: + print("Attempting ultimate fallback: official uv installation script...") + system_uv_path = install_uv_fallback() + + if system_uv_path: + # Try to use the system-installed uv to install uv into penv + try: + subprocess.check_call( + [system_uv_path, "pip", "install", "uv>=0.1.0", f"--python={python_exe}", "--quiet"], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT, + timeout=300, + env=uv_env + ) + uv_in_penv_available = True + print("Successfully installed uv into penv using system uv") + except Exception as e: + print(f"Error: Failed to install uv into penv using system uv: {e}") + + if not uv_in_penv_available: + print("Error: All uv installation methods failed") return False From 1630883fdf4a37da388f3a5baadfba9b2a3758dc Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:19:03 +0100 Subject: [PATCH 2/3] Use IS_WINDOWS constant for OS detection Refactor to use IS_WINDOWS constant instead of platform.system() for OS detection. --- builder/penv_setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/builder/penv_setup.py b/builder/penv_setup.py index a81f53bfd..3e209a9a5 100644 --- a/builder/penv_setup.py +++ b/builder/penv_setup.py @@ -14,7 +14,6 @@ import json import os -import platform import re import semantic_version import shutil @@ -125,10 +124,9 @@ def install_uv_fallback(): Returns: str or None: Path to uv executable if successful, None otherwise """ - system = platform.system() try: - if system == "Windows": + if IS_WINDOWS: # Windows installation using PowerShell script ps_script = 'irm https://astral.sh/uv/install.ps1 | iex' cmd = [ From 9af473458cd43de447d499ac9bf5b0bbc4f2565f Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:48:54 +0100 Subject: [PATCH 3/3] Refactor uv installation path checks --- builder/penv_setup.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/builder/penv_setup.py b/builder/penv_setup.py index 3e209a9a5..ace9c4a0c 100644 --- a/builder/penv_setup.py +++ b/builder/penv_setup.py @@ -154,17 +154,18 @@ def install_uv_fallback(): str(Path.home() / ".cargo" / "bin" / "uv.exe"), ] - # Also check PATH - uv_in_path = shutil.which("uv") - if uv_in_path: - print(f"Successfully installed uv using official installer: {uv_in_path}") - return uv_in_path - + # Check known install locations first for path in possible_paths: if os.path.isfile(path): print(f"Successfully installed uv using official installer: {path}") return path - + + # Fall back to PATH lookup + uv_in_path = shutil.which("uv") + if uv_in_path: + print(f"Successfully installed uv using official installer: {uv_in_path}") + return uv_in_path + else: # Unix-like systems (Linux, macOS) install_url = "https://astral.sh/uv/install.sh"