netx/packaging/update_netx.ps1
oliver 477ec4b120 Add Windows packaging with bundled UI and dual-mode PostgreSQL.
Ship installable releases via zip/Inno Setup with optional bundled Postgres, API-hosted SPA, manual update path, and CI workflow for future tag builds.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 22:52:03 +08:00

104 lines
4 KiB
PowerShell

param(
[Parameter(Mandatory = $true)]
[string]$PackagePath,
[string]$ProgramRoot = "",
[string]$DataRoot = "",
[switch]$SkipBackup = $false,
[switch]$NoStart = $false
)
$ErrorActionPreference = "Stop"
. "$PSScriptRoot\_common.ps1"
$prog = Get-NetxProgramRoot -Override $ProgramRoot
$data = Get-NetxDataRoot -ProgramRoot $prog -Override $DataRoot
if (-not (Test-Path $PackagePath)) {
throw "package_not_found: $PackagePath"
}
$work = Join-Path $env:TEMP ("netx-update-" + [guid]::NewGuid().ToString("n"))
New-Item -ItemType Directory -Path $work -Force | Out-Null
try {
Write-Host "==> Extracting update package"
if ($PackagePath.ToLowerInvariant().EndsWith(".zip")) {
Expand-Archive -Path $PackagePath -DestinationPath $work -Force
} else {
throw "unsupported_package: use a .zip built by build_release.ps1"
}
$src = $work
# If zip has a single top-level folder, use it.
$kids = @(Get-ChildItem -Path $work -Directory)
if ($kids.Count -eq 1 -and (Test-Path (Join-Path $kids[0].FullName "version.json"))) {
$src = $kids[0].FullName
} elseif (-not (Test-Path (Join-Path $work "version.json"))) {
$nested = Get-ChildItem -Path $work -Recurse -Filter "version.json" | Select-Object -First 1
if ($nested) { $src = Split-Path -Parent $nested.FullName }
}
$newVerFile = Join-Path $src "version.json"
if (-not (Test-Path $newVerFile)) {
throw "version.json missing in package"
}
$newVer = (Get-Content -Raw $newVerFile | ConvertFrom-Json).version
$oldVer = Get-NetxVersion -ProgramRoot $prog
Write-Host "==> Updating $oldVer -> $newVer"
if (-not $SkipBackup) {
$stamp = Get-Date -Format "yyyyMMdd_HHmmss"
$bak = Join-Path $data "backups\pre-update-$stamp"
New-Item -ItemType Directory -Path $bak -Force | Out-Null
$envFile = Join-Path $data ".env"
if (Test-Path $envFile) {
Copy-Item $envFile (Join-Path $bak ".env")
}
Write-Host "==> Backup marker: $bak (data/pgdata left in place; optional pg_dump not run)"
}
Write-Host "==> Stopping services"
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "stop_netx_app.ps1") `
-ProgramRoot $prog -DataRoot $data
# Replace program layers only — never wipe data root.
$replaceDirs = @("netx_api", "alembic", "web", "packaging", "scripts", "postgres", "packages")
foreach ($name in $replaceDirs) {
$from = Join-Path $src $name
$to = Join-Path $prog $name
if (-not (Test-Path $from)) { continue }
Write-Host "==> Replacing $name"
if (Test-Path $to) {
Remove-Item -Recurse -Force $to
}
Copy-Item -Path $from -Destination $to -Recurse -Force
}
foreach ($f in @("requirements.txt", "pyproject.toml", "alembic.ini", "version.json", "README.md", "LICENSE")) {
$from = Join-Path $src $f
if (Test-Path $from) {
Copy-Item -Path $from -Destination (Join-Path $prog $f) -Force
}
}
# Optional runtime / .venv shipped in package
if (Test-Path (Join-Path $src "runtime")) {
$rt = Join-Path $prog "runtime"
if (Test-Path $rt) { Remove-Item -Recurse -Force $rt }
Copy-Item -Path (Join-Path $src "runtime") -Destination $rt -Recurse -Force
}
if (Test-Path (Join-Path $src ".venv")) {
Write-Host "==> Replacing .venv from package"
$venvTo = Join-Path $prog ".venv"
if (Test-Path $venvTo) { Remove-Item -Recurse -Force $venvTo }
Copy-Item -Path (Join-Path $src ".venv") -Destination $venvTo -Recurse -Force
}
Write-Host "==> Update files applied" -ForegroundColor Green
if (-not $NoStart) {
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "start_netx_app.ps1") `
-ProgramRoot $prog -DataRoot $data -SkipBrowser
}
} finally {
if (Test-Path $work) {
Remove-Item -Recurse -Force $work -ErrorAction SilentlyContinue
}
}