jcbowyer's picture
Clean HuggingFace deployment without binary files
e59d91d
Raw
History Blame Contribute Delete
12.3 kB
<#
.SYNOPSIS
CommunityOne Open Navigator - Windows installation script (PowerShell).
.DESCRIPTION
Windows-native equivalent of scripts/deployment/install.sh. Creates the
Python virtual environment, installs the backend dependencies from
requirements.txt (NOT `uv sync` — see the note below), seeds .env, and
creates the logs directory.
Why pip + requirements.txt and not `uv sync`:
The root pyproject.toml is a *uv workspace* whose members are only
packages/*. `uv sync` installs those workspace libraries but does NOT
install the top-level requirements.txt — so the dev tooling
(pytest/black/ruff) and runtime deps like yt-dlp are left out. The
backend is installed from requirements.txt, which is the complete set.
Run from the repository root (paths are relative to the CWD):
powershell -ExecutionPolicy Bypass -File scripts\deployment\install.ps1
or via the thin root wrapper:
.\install.ps1
#>
$ErrorActionPreference = 'Stop'
Write-Host "CommunityOne Open Navigator - Installation Script (Windows)"
Write-Host "=================================================="
Write-Host ""
# --- Resolve a Python launcher ------------------------------------------------
# Prefer the `py` launcher (ships with python.org installers), fall back to
# `python` on PATH. We need a real CPython, not the Windows Store stub.
function Resolve-Python {
foreach ($candidate in @(
@{ Exe = 'py'; Args = @('-3') },
@{ Exe = 'python'; Args = @() }
)) {
$cmd = Get-Command $candidate.Exe -ErrorAction SilentlyContinue
if ($null -eq $cmd) { continue }
try {
$ver = & $candidate.Exe @($candidate.Args + '--version') 2>&1
if ($ver -match 'Python\s+3\.\d+') {
return [pscustomobject]@{ Exe = $candidate.Exe; Args = $candidate.Args; Version = "$ver".Trim() }
}
} catch { }
}
return $null
}
Write-Host "Checking Python version..."
$py = Resolve-Python
if ($null -eq $py) {
Write-Host "[X] Error: Python 3 is not installed (or only the Windows Store stub is present)." -ForegroundColor Red
Write-Host " Install Python 3.11-3.13 from https://www.python.org/downloads/windows/"
Write-Host " (tick 'Add python.exe to PATH' in the installer), then re-run this script."
exit 1
}
# Parse MAJOR.MINOR from e.g. "Python 3.12.3"
$pyVersion = ($py.Version -replace '^Python\s+', '')
$verParts = $pyVersion.Split('.')
$pyMajor = [int]$verParts[0]
$pyMinor = [int]$verParts[1]
Write-Host "[OK] Found Python $pyMajor.$pyMinor (via '$($py.Exe)')"
# Python 3.14+ compatibility: some transitive deps (e.g. `whenever`) build a
# Rust extension via PyO3, which currently supports up to Python 3.13. On 3.14+
# that build hard-fails. Tell those packages to skip the Rust extension and use
# their (slower) pure-Python implementation so the install still succeeds.
if ($pyMinor -ge 14) {
Write-Host "[!] Python $pyMajor.$pyMinor detected - newer than PyO3's max supported (3.13)." -ForegroundColor Yellow
Write-Host " Forcing pure-Python builds for Rust-backed deps (whenever) to avoid build failures."
$env:WHENEVER_NO_BUILD_RUST_EXT = "1"
$env:PYO3_USE_ABI3_FORWARD_COMPATIBILITY = "1"
}
# --- Optional system dependency: Tesseract OCR --------------------------------
# Unlike Linux, lxml ships Windows wheels, so no libxml2/libxslt headers are
# needed. The only optional native tool is Tesseract (OCR fallback). Try winget,
# then Chocolatey; otherwise tell the user how to get it. Never fatal.
Write-Host ""
Write-Host "Checking optional system dependency (Tesseract OCR)..."
if (Get-Command tesseract -ErrorAction SilentlyContinue) {
Write-Host "[OK] Tesseract available: $((tesseract --version 2>&1 | Select-Object -First 1))"
} elseif (Get-Command winget -ErrorAction SilentlyContinue) {
Write-Host "Installing Tesseract via winget..."
try {
winget install --id UB-Mannheim.TesseractOCR --accept-source-agreements --accept-package-agreements -e
} catch {
Write-Host "[!] winget could not install Tesseract; OCR fallback will stay disabled." -ForegroundColor Yellow
}
} elseif (Get-Command choco -ErrorAction SilentlyContinue) {
Write-Host "Installing Tesseract via Chocolatey..."
try { choco install -y tesseract } catch {
Write-Host "[!] choco could not install Tesseract; OCR fallback will stay disabled." -ForegroundColor Yellow
}
} else {
Write-Host "[!] Tesseract not found and no winget/choco available." -ForegroundColor Yellow
Write-Host " OCR is optional. To enable it, install the UB-Mannheim Tesseract build:"
Write-Host " https://github.com/UB-Mannheim/tesseract/wiki"
}
# --- Virtual environment ------------------------------------------------------
Write-Host ""
Write-Host "Creating virtual environment..."
if (Test-Path ".venv") {
Write-Host "[!] Virtual environment already exists. Removing old one..." -ForegroundColor Yellow
Remove-Item -Recurse -Force ".venv"
}
& $py.Exe @($py.Args + @('-m', 'venv', '.venv'))
Write-Host "[OK] Virtual environment created"
# Use the venv's interpreter directly (robust regardless of activation state /
# ExecutionPolicy). All subsequent pip calls go through this python.exe.
$venvPython = Join-Path (Get-Location) ".venv\Scripts\python.exe"
if (-not (Test-Path $venvPython)) {
Write-Host "[X] Expected venv interpreter not found at $venvPython" -ForegroundColor Red
exit 1
}
# --- pip + dependencies -------------------------------------------------------
Write-Host ""
Write-Host "Upgrading pip..."
& $venvPython -m pip install --upgrade pip
Write-Host "[OK] pip upgraded"
Write-Host ""
Write-Host "Installing dependencies (this may take a few minutes)..."
# Prefer CPU-only requirements (no GPU needed) when present, matching install.sh.
if (Test-Path "requirements-cpu.txt") {
Write-Host "Using CPU-only requirements (no GPU needed)..."
& $venvPython -m pip install -r requirements-cpu.txt
} else {
& $venvPython -m pip install -r requirements.txt
}
Write-Host "[OK] Dependencies installed"
# Install the local workspace libraries (packages/*) as editable, top-level
# importable modules. requirements.txt only pins third-party deps; the API
# entrypoint imports agents/ingestion/config/llm/etc. which now live under
# packages/* (there is no top-level agents/ tree anymore), so without this step
# `python main.py serve` fails with "ModuleNotFoundError: No module named
# 'agents'". --no-deps keeps the dependency closure exactly as requirements.txt
# pins it (these packages' own third-party deps are already installed above).
# This is the full runtime set the Dockerfile installs (eager + lazy imports).
Write-Host ""
Write-Host "Installing local workspace packages (editable)..."
& $venvPython -m pip install --no-deps `
-e packages/core -e packages/core-lib -e packages/datamodels `
-e packages/agents -e packages/scrapers -e packages/ingestion `
-e packages/llm -e packages/accessibility -e packages/hosting
Write-Host "[OK] Workspace packages installed"
# --- Optional: infrastructure CLIs (Azure CLI + Terraform) --------------------
# For the infra/azure subscriptions Terraform. Opt-in (most contributors don't
# need it) — enable with: $env:INSTALL_INFRA_TOOLS = "1"; .\install.ps1
# Prefers winget/choco; falls back to a rootless install (azure-cli via pip,
# terraform binary into .venv\Scripts) when neither is available.
$TerraformVersion = if ($env:TERRAFORM_VERSION) { $env:TERRAFORM_VERSION } else { "1.9.8" }
function Install-AzureCli {
if (Get-Command az -ErrorAction SilentlyContinue) { Write-Host "[OK] Azure CLI already installed"; return }
Write-Host "Installing Azure CLI..."
if (Get-Command winget -ErrorAction SilentlyContinue) {
try { winget install --id Microsoft.AzureCLI --accept-source-agreements --accept-package-agreements -e } catch {}
} elseif (Get-Command choco -ErrorAction SilentlyContinue) {
try { choco install -y azure-cli } catch {}
}
if (-not (Get-Command az -ErrorAction SilentlyContinue)) {
Write-Host "[!] No system install path; installing azure-cli into .venv via pip (rootless)..." -ForegroundColor Yellow
try { & $venvPython -m pip install --upgrade azure-cli } catch {
Write-Host "[!] Could not install Azure CLI. Manual: https://learn.microsoft.com/cli/azure/install-azure-cli" -ForegroundColor Yellow
}
}
}
function Install-Terraform {
if (Get-Command terraform -ErrorAction SilentlyContinue) { Write-Host "[OK] Terraform already installed"; return }
Write-Host "Installing Terraform $TerraformVersion..."
if (Get-Command winget -ErrorAction SilentlyContinue) {
try { winget install --id Hashicorp.Terraform --accept-source-agreements --accept-package-agreements -e } catch {}
} elseif (Get-Command choco -ErrorAction SilentlyContinue) {
try { choco install -y terraform } catch {}
}
if (-not (Get-Command terraform -ErrorAction SilentlyContinue)) {
# Rootless: drop the official binary into .venv\Scripts (on PATH once activated).
$arch = if ([Environment]::Is64BitOperatingSystem) { "amd64" } else { "386" }
$url = "https://releases.hashicorp.com/terraform/$TerraformVersion/terraform_${TerraformVersion}_windows_$arch.zip"
$zip = Join-Path $env:TEMP "terraform.zip"
try {
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
Expand-Archive -Path $zip -DestinationPath ".venv\Scripts" -Force
Remove-Item $zip -Force
Write-Host "[OK] Terraform installed to .venv\Scripts"
} catch {
Write-Host "[!] Could not download Terraform. Manual: https://developer.hashicorp.com/terraform/install" -ForegroundColor Yellow
}
}
}
if ($env:INSTALL_INFRA_TOOLS -eq "1") {
Write-Host ""
Write-Host "Installing infrastructure CLIs (Azure CLI + Terraform) [INSTALL_INFRA_TOOLS=1]..."
Install-AzureCli
Install-Terraform
# If a tool fell back to the rootless install it lives in .venv\Scripts, which is
# only on PATH once the venv is activated.
if (Test-Path ".venv\Scripts\terraform.exe") {
Write-Host "[!] Rootless tools are in .venv\Scripts — activate the venv (.\.venv\Scripts\Activate.ps1) to use az/terraform." -ForegroundColor Yellow
}
}
# --- .env ---------------------------------------------------------------------
Write-Host ""
if (-not (Test-Path ".env")) {
Write-Host "Creating .env file from template..."
Copy-Item ".env.example" ".env"
Write-Host "[OK] .env file created"
Write-Host ""
Write-Host "[!] IMPORTANT: edit .env and set at least NEON_DATABASE_URL_DEV" -ForegroundColor Yellow
Write-Host " (points the API at the local Postgres warehouse on localhost:5433)."
} else {
Write-Host "[OK] .env file already exists"
}
# --- logs ---------------------------------------------------------------------
Write-Host ""
Write-Host "Creating logs directory..."
New-Item -ItemType Directory -Force -Path "logs" | Out-Null
Write-Host "[OK] logs directory created"
# --- Done ---------------------------------------------------------------------
Write-Host ""
Write-Host "=================================================="
Write-Host "Installation Complete!"
Write-Host "=================================================="
Write-Host ""
Write-Host "To activate the virtual environment, run:"
Write-Host " .\.venv\Scripts\Activate.ps1"
Write-Host ""
Write-Host "Then you can use the CLI:"
Write-Host " python main.py --help"
Write-Host " python main.py serve"
Write-Host ""
Write-Host "Install the frontend, then launch all three services:"
Write-Host " cd web_app; npm install; cd .."
Write-Host " cd web_docs; npm install; cd .."
Write-Host " .\start-all.ps1"
Write-Host ""
Write-Host "Don't forget to configure your .env file with API keys!"
Write-Host ""
if ($env:INSTALL_INFRA_TOOLS -ne "1") {
Write-Host "Working on infra\azure (Azure subscriptions)? Re-run with the infra CLIs:"
Write-Host ' $env:INSTALL_INFRA_TOOLS = "1"; .\install.ps1 # installs Azure CLI + Terraform'
Write-Host ""
}