From eb9ec687cb0653bcee589fd7a2e4b2a4ac631cd5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Mar 2026 22:56:13 -0700 Subject: [PATCH] Use uv for Python management in CI and build script - CI: install uv via astral-sh/setup-uv, use uv to install Python and run the build script (replaces setup-python which fails on self-hosted macOS runners) - build_sidecar.py: auto-detects uv and uses it for venv creation and package installation (much faster), falls back to standard venv + pip when uv is not available Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitea/workflows/build.yml | 19 ++++----------- python/build_sidecar.py | 49 ++++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 55c5cd5..90527e5 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -32,24 +32,15 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v4 + - name: Set up Python - if: matrix.platform != 'macos' - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION }} - - - name: Verify Python (macOS) - if: matrix.platform == 'macos' - run: | - python3 --version - python3 -m pip --version - - - name: Install Python build tools - run: python3 -m pip install --upgrade pip setuptools wheel + run: uv python install ${{ env.PYTHON_VERSION }} - name: Build sidecar working-directory: python - run: python3 build_sidecar.py --cpu-only + run: uv run --python ${{ env.PYTHON_VERSION }} python build_sidecar.py --cpu-only - name: Upload sidecar artifact uses: actions/upload-artifact@v3 diff --git a/python/build_sidecar.py b/python/build_sidecar.py index 1eb0b0e..d38699a 100644 --- a/python/build_sidecar.py +++ b/python/build_sidecar.py @@ -59,42 +59,67 @@ def get_target_triple() -> str: return f"{arch}-unknown-{system}" +def _has_uv() -> bool: + """Check if uv is available.""" + try: + subprocess.run(["uv", "--version"], capture_output=True, check=True) + return True + except (FileNotFoundError, subprocess.CalledProcessError): + return False + + def create_venv_and_install(cpu_only: bool) -> Path: - """Create a fresh venv and install dependencies.""" + """Create a fresh venv and install dependencies. + + Uses uv if available (much faster), falls back to standard venv + pip. + """ venv_dir = BUILD_DIR / "sidecar-venv" if venv_dir.exists(): shutil.rmtree(venv_dir) - print(f"[build] Creating venv at {venv_dir}") - subprocess.run([sys.executable, "-m", "venv", str(venv_dir)], check=True) + use_uv = _has_uv() - # Determine python path inside venv — use `python -m pip` instead of - # calling pip directly to avoid permission errors on Windows + if use_uv: + print(f"[build] Creating venv with uv at {venv_dir}") + subprocess.run( + ["uv", "venv", "--python", f"{sys.version_info.major}.{sys.version_info.minor}", + str(venv_dir)], + check=True, + ) + else: + print(f"[build] Creating venv at {venv_dir}") + subprocess.run([sys.executable, "-m", "venv", str(venv_dir)], check=True) + + # Determine python path inside venv if sys.platform == "win32": python = str(venv_dir / "Scripts" / "python") else: python = str(venv_dir / "bin" / "python") - def pip_install(*args: str) -> None: - subprocess.run([python, "-m", "pip", *args], check=True) + def pkg_install(*args: str) -> None: + if use_uv: + subprocess.run(["uv", "pip", "install", "--python", python, *args], check=True) + else: + subprocess.run([python, "-m", "pip", *args], check=True) - # Upgrade pip - pip_install("install", "--upgrade", "pip", "setuptools", "wheel") + if not use_uv: + # Upgrade pip (uv doesn't need this) + pkg_install("install", "--upgrade", "pip", "setuptools", "wheel") # Install torch (CPU-only to avoid bundling ~2GB of CUDA libs) if cpu_only: print("[build] Installing PyTorch (CPU-only)") - pip_install( + pkg_install( "install", "torch", "torchaudio", "--index-url", "https://download.pytorch.org/whl/cpu", ) else: print("[build] Installing PyTorch (default, may include CUDA)") - pip_install("install", "torch", "torchaudio") + pkg_install("install", "torch", "torchaudio") # Install project and dev deps (includes pyinstaller) print("[build] Installing project dependencies") - pip_install("install", "-e", f"{SCRIPT_DIR}[dev]") + pkg_install("install", "-e", f"{SCRIPT_DIR}[dev]") return Path(python)