fix: harden powershell environment scripts
This commit is contained in:
@@ -1,7 +1,55 @@
|
||||
param(
|
||||
[switch]$NoPause
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = Split-Path -Parent $PSScriptRoot
|
||||
$WheelDir = Join-Path $Root "vendor\wheels"
|
||||
$Req = Join-Path $Root "requirements.txt"
|
||||
New-Item -ItemType Directory -Force -Path $WheelDir | Out-Null
|
||||
python -m pip download -r $Req -d $WheelDir
|
||||
Write-Host "依赖包已下载到 $WheelDir"
|
||||
|
||||
function Wait-BeforeExit {
|
||||
if (-not $NoPause) {
|
||||
Write-Host ""
|
||||
Read-Host "Press Enter to exit"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-PythonCommand {
|
||||
foreach ($name in @("py", "python", "python3")) {
|
||||
if (Get-Command $name -ErrorAction SilentlyContinue) {
|
||||
return $name
|
||||
}
|
||||
}
|
||||
throw "Python was not found. Install Python 3.10+ before downloading wheels."
|
||||
}
|
||||
|
||||
try {
|
||||
Set-Location $Root
|
||||
if (-not (Test-Path $Req)) {
|
||||
throw "requirements.txt was not found: $Req"
|
||||
}
|
||||
|
||||
$python = Get-PythonCommand
|
||||
New-Item -ItemType Directory -Force -Path $WheelDir | Out-Null
|
||||
|
||||
Write-Host "Downloading offline wheels to: $WheelDir" -ForegroundColor Cyan
|
||||
if ($python -eq "py") {
|
||||
& py -3 -m pip download -r $Req -d $WheelDir
|
||||
} else {
|
||||
& $python -m pip download -r $Req -d $WheelDir
|
||||
}
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Wheel download failed. Check network access and pip."
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Offline wheels downloaded. Copy the whole project folder to the server and run scripts\install_offline.ps1." -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Host "Download failed:" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
} finally {
|
||||
Wait-BeforeExit
|
||||
}
|
||||
|
||||
+156
-10
@@ -1,19 +1,165 @@
|
||||
param(
|
||||
[switch]$NoPause
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = Split-Path -Parent $PSScriptRoot
|
||||
$Venv = Join-Path $Root ".venv"
|
||||
$Python = "python"
|
||||
|
||||
if (-not (Test-Path $Venv)) {
|
||||
& $Python -m venv $Venv
|
||||
}
|
||||
|
||||
$VenvPython = Join-Path $Venv "Scripts\python.exe"
|
||||
$WheelDir = Join-Path $Root "vendor\wheels"
|
||||
$Req = Join-Path $Root "requirements.txt"
|
||||
|
||||
if (-not (Test-Path $WheelDir)) {
|
||||
throw "未找到离线依赖目录:$WheelDir。请先在联网环境运行 scripts\download_wheels.ps1。"
|
||||
function Write-Step {
|
||||
param([string]$Message)
|
||||
Write-Host ""
|
||||
Write-Host "==> $Message" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
& $VenvPython -m pip install --no-index --find-links $WheelDir -r $Req
|
||||
Write-Host "安装完成。运行 scripts\run.ps1 启动服务。"
|
||||
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 ' ')"
|
||||
}
|
||||
}
|
||||
|
||||
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."
|
||||
& $VenvPython -m pip install -r $Req
|
||||
}
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Dependency installation failed. If the server is offline, run scripts\download_wheels.ps1 on an online machine first and copy vendor\wheels to this server."
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+63
-4
@@ -1,8 +1,67 @@
|
||||
param(
|
||||
[int]$Port = 7001,
|
||||
[string]$HostAddress = "0.0.0.0",
|
||||
[switch]$NoPause
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = Split-Path -Parent $PSScriptRoot
|
||||
$VenvPython = Join-Path $Root ".venv\Scripts\python.exe"
|
||||
if (-not (Test-Path $VenvPython)) {
|
||||
throw "未找到虚拟环境。请先运行 scripts\install_offline.ps1。"
|
||||
$InstallScript = Join-Path $PSScriptRoot "install_offline.ps1"
|
||||
|
||||
function Wait-BeforeExit {
|
||||
if (-not $NoPause) {
|
||||
Write-Host ""
|
||||
Read-Host "Press Enter to exit"
|
||||
}
|
||||
}
|
||||
|
||||
function Test-PortAvailable {
|
||||
param([int]$CheckPort)
|
||||
$listener = Get-NetTCPConnection -LocalPort $CheckPort -State Listen -ErrorAction SilentlyContinue
|
||||
if ($listener) {
|
||||
$pids = ($listener | Select-Object -ExpandProperty OwningProcess -Unique) -join ", "
|
||||
throw "Port $CheckPort is already in use. Owning process PID: $pids"
|
||||
}
|
||||
}
|
||||
|
||||
function Test-EnvironmentReady {
|
||||
if (-not (Test-Path $VenvPython)) {
|
||||
return $false
|
||||
}
|
||||
& $VenvPython -c "import fastapi, uvicorn, xlrd, xlwt, xlutils, httpx, yaml" 2>$null
|
||||
return ($LASTEXITCODE -eq 0)
|
||||
}
|
||||
|
||||
try {
|
||||
Set-Location $Root
|
||||
|
||||
if (-not (Test-EnvironmentReady)) {
|
||||
Write-Host "Runtime environment is missing or incomplete. Installing now..." -ForegroundColor Yellow
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File $InstallScript -NoPause
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Automatic environment installation failed. See the error above."
|
||||
}
|
||||
}
|
||||
|
||||
Test-PortAvailable -CheckPort $Port
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Starting PCFAnalysis service" -ForegroundColor Cyan
|
||||
Write-Host "Bind URL: http://$HostAddress`:$Port"
|
||||
Write-Host "Local URL: http://127.0.0.1`:$Port"
|
||||
Write-Host "Press Ctrl+C in this window to stop the service."
|
||||
Write-Host ""
|
||||
|
||||
& $VenvPython -m uvicorn app.main:app --host $HostAddress --port $Port
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Service exited with code: $LASTEXITCODE"
|
||||
}
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Host "Startup failed:" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
} finally {
|
||||
Wait-BeforeExit
|
||||
}
|
||||
Set-Location $Root
|
||||
& $VenvPython -m uvicorn app.main:app --host 127.0.0.1 --port 7001
|
||||
|
||||
Reference in New Issue
Block a user