2026-07-02 15:30:18 +08:00
param (
2026-07-02 15:46:33 +08:00
[ switch ] $NoPause ,
[ string ] $IndexUrl = " https://pypi.org/simple "
2026-07-02 15:30:18 +08:00
)
2026-06-30 14:29:02 +08:00
$ErrorActionPreference = " Stop "
$Root = Split-Path -Parent $PSScriptRoot
$Venv = Join-Path $Root " .venv "
$VenvPython = Join-Path $Venv " Scripts\python.exe "
$WheelDir = Join-Path $Root " vendor\wheels "
$Req = Join-Path $Root " requirements.txt "
2026-07-02 15:30:18 +08:00
function Write-Step {
param ( [ string ] $Message )
Write-Host " "
Write-Host " ==> $Message " -ForegroundColor Cyan
}
function Wait-BeforeExit {
if ( -not $NoPause ) {
Write-Host " "
Read-Host " Press Enter to exit "
}
}
function Get-PythonCandidate {
$candidates = @ (
@ { Exe = " py " ; Args = @ ( " -3 " ) } ,
@ { Exe = " python " ; Args = @ ( ) } ,
@ { Exe = " python3 " ; Args = @ ( ) }
)
foreach ( $candidate in $candidates ) {
$command = Get-Command $candidate . Exe -ErrorAction SilentlyContinue
if ( -not $command ) {
continue
}
try {
$args = @ ( $candidate . Args ) + @ ( " -c " , " import sys; print(sys.executable); print(sys.version_info.major); print(sys.version_info.minor) " )
$output = & $candidate . Exe @args 2 > $null
if ( $LASTEXITCODE -ne 0 -or $output . Count -lt 3 ) {
continue
}
$major = [ int ] $output [ 1 ]
$minor = [ int ] $output [ 2 ]
if ( $major -gt 3 -or ( $major -eq 3 -and $minor -ge 10 ) ) {
return [ pscustomobject ] @ {
Exe = $candidate . Exe
Args = $candidate . Args
Path = $output [ 0 ]
Version = " $major . $minor "
}
}
} catch {
continue
}
}
return $null
}
function Install-PythonWithWinget {
$winget = Get-Command winget -ErrorAction SilentlyContinue
if ( -not $winget ) {
throw " Python 3.10+ was not found, and winget is not available. Install Python 3.10+ manually and run this script again. "
}
Write-Step " Python was not found. Installing Python 3.12 with winget "
& winget install - -id Python . Python . 3 . 12 -e - -source winget - -accept -package -agreements - -accept -source -agreements
if ( $LASTEXITCODE -ne 0 ) {
throw " winget failed to install Python. Check network access or install Python 3.10+ manually. "
}
}
function Ensure-Python {
$python = Get-PythonCandidate
if ( $python ) {
Write-Host " Python found: $( $python . Path ) , version $( $python . Version ) "
return $python
}
Install-PythonWithWinget
$python = Get-PythonCandidate
if ( -not $python ) {
throw " Python is still unavailable after installation. Reopen PowerShell or check PATH. "
}
Write-Host " Python ready: $( $python . Path ) , version $( $python . Version ) "
return $python
}
function Invoke-Python {
param (
[ object ] $Python ,
[ string[] ] $Arguments
)
$allArgs = @ ( $Python . Args ) + $Arguments
& $Python . Exe @allArgs
if ( $LASTEXITCODE -ne 0 ) {
throw " Python command failed: $( $Python . Exe ) $( $allArgs -join ' ' ) "
}
2026-06-30 14:29:02 +08:00
}
2026-07-02 15:30:18 +08:00
function Ensure-Venv {
param ( [ object ] $Python )
if ( -not ( Test-Path $VenvPython ) ) {
Write-Step " Creating virtual environment "
Invoke-Python -Python $Python -Arguments @ ( " -m " , " venv " , $Venv )
} else {
Write-Host " Virtual environment already exists: $Venv "
}
if ( -not ( Test-Path $VenvPython ) ) {
throw " Failed to create virtual environment: $VenvPython does not exist. "
}
}
function Install-Requirements {
if ( -not ( Test-Path $Req ) ) {
throw " requirements.txt was not found: $Req "
}
Write-Step " Installing dependencies "
$wheels = @ ( )
if ( Test-Path $WheelDir ) {
$wheels = Get-ChildItem -Path $WheelDir -Filter * . whl -File -ErrorAction SilentlyContinue
}
if ( $wheels . Count -gt 0 ) {
Write-Host " Offline wheel directory found: $WheelDir "
& $VenvPython -m pip install - -no -index - -find -links $WheelDir -r $Req
} else {
Write-Host " No offline wheels found. Installing dependencies from the network. "
2026-07-02 15:46:33 +08:00
& $VenvPython -m pip install `
- -index -url $IndexUrl `
- -trusted -host pypi . org `
- -trusted -host files . pythonhosted . org `
- -disable -pip -version -check `
- -retries 3 `
- -timeout 60 `
-r $Req
2026-07-02 15:30:18 +08:00
}
if ( $LASTEXITCODE -ne 0 ) {
2026-07-02 15:46:33 +08:00
throw " Dependency installation failed. Check network access to $IndexUrl . If the server is offline, run scripts\download_wheels.ps1 on an online machine first and copy vendor\wheels to this server. "
2026-07-02 15:30:18 +08:00
}
}
function Test-ApplicationImport {
Write-Step " Verifying environment "
& $VenvPython -c " import fastapi, uvicorn, xlrd, xlwt, xlutils, httpx, yaml; print('Dependency verification passed') "
if ( $LASTEXITCODE -ne 0 ) {
throw " Dependency verification failed. "
}
}
try {
Set-Location $Root
Write-Step " Preparing PCFAnalysis environment "
$python = Ensure-Python
Ensure-Venv -Python $python
Install-Requirements
Test-ApplicationImport
Write-Host " "
Write-Host " Environment installation completed. Run scripts\run.ps1 to start the service. " -ForegroundColor Green
} catch {
Write-Host " "
Write-Host " Installation failed: " -ForegroundColor Red
Write-Host $_ . Exception . Message -ForegroundColor Red
exit 1
} finally {
Wait-BeforeExit
}