diff --git a/packaging/check_update.ps1 b/packaging/check_update.ps1 index 87536b2..7f90413 100644 --- a/packaging/check_update.ps1 +++ b/packaging/check_update.ps1 @@ -315,24 +315,16 @@ try { } $zipName = "NetX-$latest-win64.zip" $zipPath = Join-Path $dlDir $zipName - - $needDownload = $true - if ((Test-Path -LiteralPath $zipPath) -and ((Get-Item -LiteralPath $zipPath).Length -gt 1MB)) { - Write-Host "==> Using existing download: $zipPath ($([math]::Round((Get-Item $zipPath).Length/1MB,1)) MB)" - $needDownload = $false - } - if ($needDownload) { - Write-Host "==> Downloading $zipName from $($result.source) ..." - # Only attach token for non-GitHub hosts (Forgejo/private). Public GitHub assets need no auth; - # a Forgejo token would break anonymous GitHub downloads. - $dlHeaders = @{ "User-Agent" = "NetX-UpdateCheck" } - $dlUri = [uri]$result.download_url - $isGithub = ($dlUri.Host -match '(^|\.)github\.com$' -or $dlUri.Host -match '(^|\.)githubusercontent\.com$') - if ($UpdateToken -and -not $isGithub) { - $dlHeaders["Authorization"] = "token $UpdateToken" - } - Invoke-WebRequest -Uri $result.download_url -OutFile $zipPath -Headers $dlHeaders -UseBasicParsing + Write-Host "==> Downloading $zipName from $($result.source) ..." + # Only attach token for non-GitHub hosts (Forgejo/private). Public GitHub assets need no auth; + # a Forgejo token would break anonymous GitHub downloads. + $dlHeaders = @{ "User-Agent" = "NetX-UpdateCheck" } + $dlUri = [uri]$result.download_url + $isGithub = ($dlUri.Host -match '(^|\.)github\.com$' -or $dlUri.Host -match '(^|\.)githubusercontent\.com$') + if ($UpdateToken -and -not $isGithub) { + $dlHeaders["Authorization"] = "token $UpdateToken" } + Invoke-WebRequest -Uri $result.download_url -OutFile $zipPath -Headers $dlHeaders -UseBasicParsing if ($result.sha256 -and $result.sha256 -notmatch 'REPLACE' -and $result.sha256.Trim()) { $hash = (Get-FileHash -Path $zipPath -Algorithm SHA256).Hash.ToLowerInvariant() $expect = $result.sha256.ToLowerInvariant() @@ -342,27 +334,7 @@ try { Write-Host "==> SHA256 OK" } - # Program Files installs need admin to replace files; elevate once (avoid loop via NETX_UPDATE_ELEVATED). - $progWritable = $false - try { - $probe = Join-Path $prog (".netx_w_" + [guid]::NewGuid().ToString("n")) - [IO.File]::WriteAllText($probe, "x") - Remove-Item -LiteralPath $probe -Force -ErrorAction SilentlyContinue - $progWritable = $true - } catch { - $progWritable = $false - } - if (-not $progWritable -and -not $env:NETX_UPDATE_ELEVATED) { - Write-Host "==> Program directory is not writable; relaunching update as Administrator (UAC)..." -ForegroundColor Yellow - $arg = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" -ProgramRoot `"$prog`" -DataRoot `"$data`" -Apply" - $ep = Start-Process -FilePath "powershell.exe" -ArgumentList $arg -WorkingDirectory $prog ` - -Verb RunAs -Wait -PassThru - if ($null -eq $ep.ExitCode) { exit 1 } - exit $ep.ExitCode - } - Write-Host "==> Applying update via update_netx.ps1" - $env:NETX_UPDATE_ELEVATED = "1" & powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "update_netx.ps1") ` -PackagePath $zipPath -ProgramRoot $prog -DataRoot $data Write-Host "==> Update applied to $latest" -ForegroundColor Green diff --git a/packaging/installer/netx.iss b/packaging/installer/netx.iss index d1943c3..c9acd89 100644 --- a/packaging/installer/netx.iss +++ b/packaging/installer/netx.iss @@ -5,7 +5,7 @@ #define MyAppName "NetX" #ifndef MyAppVersion - #define MyAppVersion "0.4.10" + #define MyAppVersion "0.4.7" #endif #define MyAppPublisher "NetX" #define MyAppURL "https://github.com/hansjone/netx" diff --git a/packaging/manifest.example.json b/packaging/manifest.example.json index 0e978b8..2dccc21 100644 --- a/packaging/manifest.example.json +++ b/packaging/manifest.example.json @@ -1,11 +1,11 @@ { "channel": "stable", - "latest": "0.4.10", + "latest": "0.4.7", "min_compatible": "0.3.0", - "notes_url": "https://github.com/hansjone/netx/releases/tag/v0.4.10", + "notes_url": "https://github.com/hansjone/netx/releases/tag/v0.4.7", "windows": { - "url": "https://github.com/hansjone/netx/releases/download/v0.4.10/NetX-0.4.10-win64.zip", - "setup_url": "https://github.com/hansjone/netx/releases/download/v0.4.10/NetX-Setup-0.4.10.exe", + "url": "https://github.com/hansjone/netx/releases/download/v0.4.7/NetX-0.4.7-win64.zip", + "setup_url": "https://github.com/hansjone/netx/releases/download/v0.4.7/NetX-Setup-0.4.7.exe", "sha256": "", "size": 0 } diff --git a/packaging/netx_tray.ps1 b/packaging/netx_tray.ps1 index 889e65d..e1d31c6 100644 --- a/packaging/netx_tray.ps1 +++ b/packaging/netx_tray.ps1 @@ -101,8 +101,7 @@ if ($autoVal -match '^(1|true|yes|on)$') { $CheckUpdateOnLaunch = $true } $uiUrl = "http://${hostBind}:${port}/" -$script:ver = Get-NetxVersion -ProgramRoot $prog -$ver = $script:ver +$ver = Get-NetxVersion -ProgramRoot $prog $dbMode = Get-DbMode -EnvMap $map $dbModeLabel = if ($dbMode -eq "bundled") { if (([cultureinfo]::CurrentUICulture.Name -match '^(zh|zh-)')) { "内置库" } else { "built-in DB" } @@ -161,25 +160,16 @@ function Invoke-NetxScript { } function Update-NetxTrayTip { - # Refresh version after in-place updates (tray process may outlive version.json). - try { - $nowVer = Get-NetxVersion -ProgramRoot $prog - if ($nowVer -and $nowVer -ne $script:ver) { - $script:ver = $nowVer - if ($null -ne $script:miHeader) { $script:miHeader.Text = "NetX $script:ver" } - } - } catch {} $running = Test-NetxApiHealthy -HostName $hostBind -Port $port -TimeoutSec 1 $state = if ($running) { (T "Running" "运行中") } else { (T "Stopped" "已停止") } - $v = $script:ver - $tip = "NetX $v · $state · $dbModeLabel`n$uiUrl" + $tip = "NetX $ver · $state · $dbModeLabel`n$uiUrl" if ($tip.Length -gt 63) { # NotifyIcon.Text max ~63 chars on older Windows. - $tip = "NetX $v · $state · $dbModeLabel" + $tip = "NetX $ver · $state · $dbModeLabel" } $notify.Text = $tip } @@ -208,63 +198,6 @@ function Start-NetxTrayDeferred { $timer.Start() } -function Start-NetxTrayWatchProcess { - # Never Start-Process -Wait on the WinForms UI thread — it freezes the tray menu. - param( - [Parameter(Mandatory = $true)]$Process, - [scriptblock]$OnExit - ) - if ($null -eq $Process) { - if ($OnExit) { & $OnExit $null } - return - } - $script:netxWatchProc = $Process - $script:netxWatchOnExit = $OnExit - if ($script:netxWatchTimer) { - try { $script:netxWatchTimer.Stop(); $script:netxWatchTimer.Dispose() } catch {} - } - $timer = New-Object System.Windows.Forms.Timer - $timer.Interval = 400 - $timer.add_Tick({ - $p = $script:netxWatchProc - if ($null -eq $p) { - try { $script:netxWatchTimer.Stop(); $script:netxWatchTimer.Dispose() } catch {} - $script:netxWatchTimer = $null - return - } - $done = $false - try { $done = [bool]$p.HasExited } catch { $done = $true } - if (-not $done) { return } - try { $script:netxWatchTimer.Stop(); $script:netxWatchTimer.Dispose() } catch {} - $script:netxWatchTimer = $null - $cb = $script:netxWatchOnExit - $script:netxWatchOnExit = $null - $script:netxWatchProc = $null - if ($cb) { & $cb $p } - }) - $script:netxWatchTimer = $timer - $timer.Start() -} - -function Restart-NetxTraySelf { - # Fresh process picks up new version.json / scripts after in-place update. - try { - $notify.ShowBalloonTip( - 2500, "NetX", - (T "Restarting tray…" "正在重启托盘…"), - [System.Windows.Forms.ToolTipIcon]::Info - ) - } catch {} - $trayFile = Join-Path $PSScriptRoot "netx_tray.ps1" - $arg = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$trayFile`" -ProgramRoot `"$prog`" -DataRoot `"$data`"" - try { - Start-Process -FilePath "powershell.exe" -ArgumentList $arg -WindowStyle Hidden | Out-Null - } catch {} - try { $notify.Visible = $false } catch {} - try { $form.Close() } catch {} - [System.Windows.Forms.Application]::Exit() -} - function Test-NetxSetupCancelled { param([AllowNull()][Nullable[int]]$ExitCode) if ($null -eq $ExitCode) { return $false } @@ -272,43 +205,6 @@ function Test-NetxSetupCancelled { return ([int]$ExitCode -eq -1073741510) } -function Complete-NetxTrayReconfig { - # Reload .env after reconfigure (host/port/mode may change). - if (Test-Path $envPath) { - $map = Read-DotEnv -Path $envPath - if ($map["NETX_HOST"]) { $script:hostBind = $map["NETX_HOST"]; $hostBind = $script:hostBind } - if ($map["NETX_PORT"]) { - try { - $script:port = [int]$map["NETX_PORT"] - $port = $script:port - } catch {} - } - $script:uiUrl = "http://${hostBind}:${port}/" - $uiUrl = $script:uiUrl - $dbMode = Get-DbMode -EnvMap $map - $script:dbModeLabel = if ($dbMode -eq "bundled") { - (T "built-in DB" "内置库") - } else { - (T "external DB" "外置库") - } - $dbModeLabel = $script:dbModeLabel - if ($miSub) { $miSub.Text = "$dbModeLabel · ${hostBind}:$port" } - } - - $restart = [System.Windows.Forms.MessageBox]::Show( - (T "Database configuration saved.`nStart NetX now?" "数据库配置已保存。`n是否立即启动 NetX?"), - "NetX", - [System.Windows.Forms.MessageBoxButtons]::YesNo, - [System.Windows.Forms.MessageBoxIcon]::Information - ) - if ($restart -eq [System.Windows.Forms.DialogResult]::Yes) { - Invoke-NetxScript -File $startPs1 -ExtraArgs @("-SkipBrowser") | Out-Null - Open-NetxUiWhenReady - } else { - Update-NetxTrayTip - } -} - function Open-NetxUiWhenReady { param([int]$TimeoutSec = 180) $notify.ShowBalloonTip( @@ -317,37 +213,18 @@ function Open-NetxUiWhenReady { (T "Starting… first run may take a minute." "正在启动…首次迁移可能需要一分钟。"), [System.Windows.Forms.ToolTipIcon]::Info ) - # Poll on a timer — do not block the tray UI thread with Wait-NetxApiHealthy. - if ($script:netxUiReadyTimer) { - try { $script:netxUiReadyTimer.Stop(); $script:netxUiReadyTimer.Dispose() } catch {} + if (Wait-NetxApiHealthy -HostName $hostBind -Port $port -TimeoutSec $TimeoutSec) { + try { Start-Process $uiUrl } catch {} + Update-NetxTrayTip + return } - $script:netxUiReadyTicks = 0 - $script:netxUiReadyMax = [Math]::Max(1, [int]($TimeoutSec * 2)) - $timer = New-Object System.Windows.Forms.Timer - $timer.Interval = 500 - $timer.add_Tick({ - $script:netxUiReadyTicks++ - if (Test-NetxApiHealthy -HostName $hostBind -Port $port -TimeoutSec 2) { - try { $script:netxUiReadyTimer.Stop(); $script:netxUiReadyTimer.Dispose() } catch {} - $script:netxUiReadyTimer = $null - try { Start-Process $uiUrl } catch {} - Update-NetxTrayTip - return - } - if ($script:netxUiReadyTicks -ge $script:netxUiReadyMax) { - try { $script:netxUiReadyTimer.Stop(); $script:netxUiReadyTimer.Dispose() } catch {} - $script:netxUiReadyTimer = $null - $notify.ShowBalloonTip( - 8000, - "NetX", - (T "UI not ready yet. Use tray → Open UI when ready, or check data\runtime\netx.err.log." "界面尚未就绪。就绪后请用托盘「打开界面」,或查看 data\runtime\netx.err.log。"), - [System.Windows.Forms.ToolTipIcon]::Warning - ) - Update-NetxTrayTip - } - }) - $script:netxUiReadyTimer = $timer - $timer.Start() + $notify.ShowBalloonTip( + 8000, + "NetX", + (T "UI not ready yet. Use tray → Open UI when ready, or check data\runtime\netx.err.log." "界面尚未就绪。就绪后请用托盘「打开界面」,或查看 data\runtime\netx.err.log。"), + [System.Windows.Forms.ToolTipIcon]::Warning + ) + Update-NetxTrayTip } $form = New-Object System.Windows.Forms.Form @@ -420,8 +297,7 @@ $menu.Padding = New-Object System.Windows.Forms.Padding(4, 4, 4, 4) $menu.Renderer = New-Object NetxMenuRenderer # --- header (info only) --- -$script:miHeader = New-NetxMenuItem -Text "NetX $script:ver" -Header -$miHeader = $script:miHeader +$miHeader = New-NetxMenuItem -Text "NetX $ver" -Header [void]$menu.Items.Add($miHeader) $miSub = New-NetxMenuItem -Text "$dbModeLabel · ${hostBind}:$port" -Muted @@ -483,7 +359,7 @@ $miReconfig.add_Click({ if ($ans -ne [System.Windows.Forms.DialogResult]::Yes) { return } $notify.ShowBalloonTip(3000, "NetX", (T "Stopping…" "正在停止…"), [System.Windows.Forms.ToolTipIcon]::Info) - Invoke-NetxScript -File $stopPs1 | Out-Null + Invoke-NetxScript -File $stopPs1 -Wait | Out-Null # ProgramData\.env is often admin-owned after Setup; repair ACL before reconfigure. $null = Ensure-NetxDataAcl -DataRoot $data -Quiet @@ -491,61 +367,48 @@ $miReconfig.add_Click({ try { $p = Start-NetxPowerShell -File $setupPs1 ` -Arguments @("-ProgramRoot", $prog, "-DataRoot", $data) ` - -WorkingDirectory $prog -WindowStyle Normal -PassThru - Start-NetxTrayWatchProcess -Process $p -OnExit { - param($sp) - if ($null -eq $sp) { Update-NetxTrayTip; return } - if (Test-NetxSetupCancelled -ExitCode $sp.ExitCode) { - $notify.ShowBalloonTip( - 4000, "NetX", - (T "Database setup cancelled." "已取消数据库配置。"), - [System.Windows.Forms.ToolTipIcon]::Info - ) + -WorkingDirectory $prog -WindowStyle Normal -Wait -PassThru + if (Test-NetxSetupCancelled -ExitCode $p.ExitCode) { + $notify.ShowBalloonTip( + 4000, "NetX", + (T "Database setup cancelled." "已取消数据库配置。"), + [System.Windows.Forms.ToolTipIcon]::Info + ) + Update-NetxTrayTip + return + } + if ($null -ne $p.ExitCode -and $p.ExitCode -ne 0) { + # Access denied → offer elevated re-run (UAC). + $elev = [System.Windows.Forms.MessageBox]::Show( + (T ` + "Database setup failed (exit $($p.ExitCode)).`n`nIf this was a permission error on %ProgramData%\NetX\.env, click Yes to retry as Administrator." ` + "数据库配置失败(退出码 $($p.ExitCode))。`n`n若是 %ProgramData%\NetX\.env 权限问题,点「是」将以管理员身份重试。"), + "NetX", + [System.Windows.Forms.MessageBoxButtons]::YesNo, + [System.Windows.Forms.MessageBoxIcon]::Warning + ) + if ($elev -eq [System.Windows.Forms.DialogResult]::Yes) { + $arg = "-NoProfile -ExecutionPolicy Bypass -File `"$setupPs1`" -ProgramRoot `"$prog`" -DataRoot `"$data`"" + $ep = Start-Process -FilePath "powershell.exe" -ArgumentList $arg ` + -WorkingDirectory $prog -Verb RunAs -Wait -PassThru + if (Test-NetxSetupCancelled -ExitCode $ep.ExitCode) { + Update-NetxTrayTip + return + } + if ($null -ne $ep.ExitCode -and $ep.ExitCode -ne 0) { + [System.Windows.Forms.MessageBox]::Show( + (T "Elevated database setup still failed (exit $($ep.ExitCode))." "管理员配置仍失败(退出码 $($ep.ExitCode))。"), + "NetX", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Error + ) + Update-NetxTrayTip + return + } + } else { Update-NetxTrayTip return } - if ($null -ne $sp.ExitCode -and $sp.ExitCode -ne 0) { - $elev = [System.Windows.Forms.MessageBox]::Show( - (T ` - "Database setup failed (exit $($sp.ExitCode)).`n`nIf this was a permission error on %ProgramData%\NetX\.env, click Yes to retry as Administrator." ` - "数据库配置失败(退出码 $($sp.ExitCode))。`n`n若是 %ProgramData%\NetX\.env 权限问题,点「是」将以管理员身份重试。"), - "NetX", - [System.Windows.Forms.MessageBoxButtons]::YesNo, - [System.Windows.Forms.MessageBoxIcon]::Warning - ) - if ($elev -ne [System.Windows.Forms.DialogResult]::Yes) { - Update-NetxTrayTip - return - } - $arg = "-NoProfile -ExecutionPolicy Bypass -File `"$setupPs1`" -ProgramRoot `"$prog`" -DataRoot `"$data`"" - try { - $ep = Start-Process -FilePath "powershell.exe" -ArgumentList $arg ` - -WorkingDirectory $prog -Verb RunAs -PassThru - } catch { - Update-NetxTrayTip - return - } - Start-NetxTrayWatchProcess -Process $ep -OnExit { - param($ep2) - if ($null -eq $ep2 -or (Test-NetxSetupCancelled -ExitCode $ep2.ExitCode)) { - Update-NetxTrayTip - return - } - if ($null -ne $ep2.ExitCode -and $ep2.ExitCode -ne 0) { - [System.Windows.Forms.MessageBox]::Show( - (T "Elevated database setup still failed (exit $($ep2.ExitCode))." "管理员配置仍失败(退出码 $($ep2.ExitCode))。"), - "NetX", - [System.Windows.Forms.MessageBoxButtons]::OK, - [System.Windows.Forms.MessageBoxIcon]::Error - ) - Update-NetxTrayTip - return - } - Complete-NetxTrayReconfig - } - return - } - Complete-NetxTrayReconfig } } catch { [System.Windows.Forms.MessageBox]::Show( @@ -555,6 +418,35 @@ $miReconfig.add_Click({ [System.Windows.Forms.MessageBoxIcon]::Error ) Update-NetxTrayTip + return + } + + # Reload .env after reconfigure (host/port/mode may change). + if (Test-Path $envPath) { + $map = Read-DotEnv -Path $envPath + if ($map["NETX_HOST"]) { $hostBind = $map["NETX_HOST"] } + if ($map["NETX_PORT"]) { try { $port = [int]$map["NETX_PORT"] } catch {} } + $uiUrl = "http://${hostBind}:${port}/" + $dbMode = Get-DbMode -EnvMap $map + $dbModeLabel = if ($dbMode -eq "bundled") { + (T "built-in DB" "内置库") + } else { + (T "external DB" "外置库") + } + $miSub.Text = "$dbModeLabel · ${hostBind}:$port" + } + + $restart = [System.Windows.Forms.MessageBox]::Show( + (T "Database configuration saved.`nStart NetX now?" "数据库配置已保存。`n是否立即启动 NetX?"), + "NetX", + [System.Windows.Forms.MessageBoxButtons]::YesNo, + [System.Windows.Forms.MessageBoxIcon]::Information + ) + if ($restart -eq [System.Windows.Forms.DialogResult]::Yes) { + Invoke-NetxScript -File $startPs1 -ExtraArgs @("-SkipBrowser") | Out-Null + Open-NetxUiWhenReady + } else { + Update-NetxTrayTip } } }) @@ -564,78 +456,25 @@ $miUpdate = New-NetxMenuItem -Text (T "Check for updates…" "检查更新…") $miUpdate.add_Click({ Start-NetxTrayDeferred { try { - $notify.ShowBalloonTip( - 3000, "NetX", - (T "Checking for updates…" "正在检查更新…"), - [System.Windows.Forms.ToolTipIcon]::Info - ) - # No -Wait on UI thread (freezes ContextMenuStrip / tray). $p = Start-NetxPowerShell -File $checkPs1 -Arguments @("-ProgramRoot", $prog, "-DataRoot", $data) ` - -WorkingDirectory $prog -WindowStyle Normal -PassThru - Start-NetxTrayWatchProcess -Process $p -OnExit { - param($cp) - try { - if ($null -eq $cp) { return } - if (Test-NetxSetupCancelled -ExitCode $cp.ExitCode) { return } - if ($cp.ExitCode -eq 10) { - $ans = [System.Windows.Forms.MessageBox]::Show( - (T "A newer NetX is available. Download and apply now?" "发现新版本,是否立即下载并更新?"), - (T "NetX update" "NetX 更新"), - [System.Windows.Forms.MessageBoxButtons]::YesNo, - [System.Windows.Forms.MessageBoxIcon]::Question - ) - if ($ans -ne [System.Windows.Forms.DialogResult]::Yes) { return } - $arg = "-NoProfile -ExecutionPolicy Bypass -File `"$checkPs1`" -ProgramRoot `"$prog`" -DataRoot `"$data`" -Apply" - $notify.ShowBalloonTip( - 5000, "NetX", - (T "Downloading / applying update… keep the console open." "正在下载/应用更新…请勿关闭控制台窗口。"), - [System.Windows.Forms.ToolTipIcon]::Info - ) - try { - $ap = Start-Process -FilePath "powershell.exe" -ArgumentList $arg ` - -WorkingDirectory $prog -WindowStyle Normal -Verb RunAs -PassThru - } catch { - $notify.ShowBalloonTip(4000, "NetX", (T "Update cancelled (UAC)." "已取消更新(UAC)。"), [System.Windows.Forms.ToolTipIcon]::Info) - return - } - Start-NetxTrayWatchProcess -Process $ap -OnExit { - param($ap2) - if ($null -eq $ap2) { return } - if (Test-NetxSetupCancelled -ExitCode $ap2.ExitCode) { - $notify.ShowBalloonTip(4000, "NetX", (T "Update cancelled." "已取消更新。"), [System.Windows.Forms.ToolTipIcon]::Info) - return - } - if ($null -ne $ap2.ExitCode -and $ap2.ExitCode -eq 0) { - $newVer = Get-NetxVersion -ProgramRoot $prog - [System.Windows.Forms.MessageBox]::Show( - (T "Updated to $newVer.`nTray will restart." "已更新到 $newVer。`n即将重启托盘。"), - (T "NetX update" "NetX 更新"), - [System.Windows.Forms.MessageBoxButtons]::OK, - [System.Windows.Forms.MessageBoxIcon]::Information - ) - Restart-NetxTraySelf - return - } - [System.Windows.Forms.MessageBox]::Show( - (T "Update failed (exit $($ap2.ExitCode)). Re-run Check for updates, or install NetX-Setup manually." "更新失败(退出码 $($ap2.ExitCode))。请重试「检查更新」,或手动运行 Setup 安装包。"), - (T "NetX update" "NetX 更新"), - [System.Windows.Forms.MessageBoxButtons]::OK, - [System.Windows.Forms.MessageBoxIcon]::Warning - ) - Update-NetxTrayTip - } - } elseif ($cp.ExitCode -eq 0) { - [System.Windows.Forms.MessageBox]::Show( - (T "NetX is up to date ($script:ver)." "已是最新版本 ($script:ver)。"), - (T "NetX update" "NetX 更新") - ) - } - } catch { - [System.Windows.Forms.MessageBox]::Show( - (T "Update check failed: $($_.Exception.Message)" "检查更新失败:$($_.Exception.Message)"), - "NetX" - ) + -WorkingDirectory $prog -WindowStyle Normal -Wait -PassThru + if (Test-NetxSetupCancelled -ExitCode $p.ExitCode) { return } + if ($p.ExitCode -eq 10) { + $ans = [System.Windows.Forms.MessageBox]::Show( + (T "A newer NetX is available. Download and apply now?" "发现新版本,是否立即下载并更新?"), + (T "NetX update" "NetX 更新"), + [System.Windows.Forms.MessageBoxButtons]::YesNo, + [System.Windows.Forms.MessageBoxIcon]::Question + ) + if ($ans -eq [System.Windows.Forms.DialogResult]::Yes) { + Start-NetxPowerShell -File $checkPs1 -Arguments @("-ProgramRoot", $prog, "-DataRoot", $data, "-Apply") ` + -WorkingDirectory $prog -WindowStyle Normal | Out-Null } + } elseif ($p.ExitCode -eq 0) { + [System.Windows.Forms.MessageBox]::Show( + (T "NetX is up to date ($ver)." "已是最新版本 ($ver)。"), + (T "NetX update" "NetX 更新") + ) } } catch { [System.Windows.Forms.MessageBox]::Show( diff --git a/packaging/start_netx_app.ps1 b/packaging/start_netx_app.ps1 index a795e44..e56c792 100644 --- a/packaging/start_netx_app.ps1 +++ b/packaging/start_netx_app.ps1 @@ -1,4 +1,4 @@ -param( +param( [string]$ProgramRoot = "", [string]$DataRoot = "", [switch]$SkipBrowser = $false, @@ -118,25 +118,21 @@ if (-not (Test-Path $startScript)) { throw "start_netx.ps1 not found at $startScript" } +$startExtra = @( + "-SkipInstall", "-Background", + "-BindHost", $hostBind, "-Port", "$port" +) +if ($InlineSchedulers) { $startExtra += "-InlineSchedulers" } + Write-Host "==> Starting NetX (API + workers; UI via API on :$port)" -# Run in-process with -Background so this console exits after /health (nested -# Start-Process -Wait on a Hidden child often never returns under UAC update). -$startArgs = @{ - SkipInstall = $true - Background = $true - BindHost = $hostBind - Port = $port -} -if ($InlineSchedulers) { $startArgs["InlineSchedulers"] = $true } -& $startScript @startArgs -if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +$startProc = Start-NetxPowerShell -File $startScript -Arguments $startExtra ` + -WorkingDirectory $prog -WindowStyle Hidden -Wait -PassThru +if ($startProc.ExitCode -ne 0) { exit $startProc.ExitCode } $url = "http://${hostBind}:${port}/" -if (-not (Test-NetxApiHealthy -HostName $hostBind -Port $port -TimeoutSec 2)) { - if (-not (Wait-NetxApiHealthy -HostName $hostBind -Port $port -TimeoutSec 30)) { - Write-Host "[ERR] NetX started but /health not ready: $url" -ForegroundColor Red - exit 1 - } +if (-not (Wait-NetxApiHealthy -HostName $hostBind -Port $port -TimeoutSec 30)) { + Write-Host "[ERR] NetX process exited 0 but /health not ready: $url" -ForegroundColor Red + exit 1 } Write-Host "==> Open: $url" -ForegroundColor Green if (-not $SkipBrowser) { diff --git a/packaging/update_netx.ps1 b/packaging/update_netx.ps1 index 616ebf6..0f12cc5 100644 --- a/packaging/update_netx.ps1 +++ b/packaging/update_netx.ps1 @@ -90,8 +90,7 @@ try { -ProgramRoot $prog -DataRoot $data # Replace program layers only — never wipe data root. - # Include python/ (portable runtime) — required with shipped .venv since 0.4.6. - $replaceDirs = @("netx_api", "alembic", "web", "packaging", "scripts", "postgres", "packages", "python") + $replaceDirs = @("netx_api", "alembic", "web", "packaging", "scripts", "postgres", "packages") foreach ($name in $replaceDirs) { $from = Join-Path $src $name $to = Join-Path $prog $name @@ -108,7 +107,7 @@ try { Copy-Item -Path $from -Destination (Join-Path $prog $f) -Force } } - # Legacy top-level runtime/ (older packages) + shipped .venv + # 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 } @@ -121,21 +120,11 @@ try { Copy-Item -Path (Join-Path $src ".venv") -Destination $venvTo -Recurse -Force } - # Builder-path pyvenv.cfg → local python/runtime (same as first install). - if (Get-Command Repair-NetxShippedVenv -ErrorAction SilentlyContinue) { - if (Repair-NetxShippedVenv -ProgramRoot $prog) { - Write-Host "==> Relinked .venv to bundled python/runtime" -ForegroundColor Green - } - } - Write-Host "==> Update files applied" -ForegroundColor Green if (-not $NoStart) { - # Same process (no nested powershell) so the update console can exit cleanly. - & (Join-Path $PSScriptRoot "start_netx_app.ps1") ` + & powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "start_netx_app.ps1") ` -ProgramRoot $prog -DataRoot $data -SkipBrowser - if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } - Write-Host "==> Update complete. You can close this window." -ForegroundColor Green } finally { if (Test-Path $work) { Remove-Item -Recurse -Force $work -ErrorAction SilentlyContinue diff --git a/pyproject.toml b/pyproject.toml index 4c6493f..d16a36f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "netx-ops" -version = "0.4.10" +version = "0.4.7" description = "netx operations tool: alarm-centric workflows with REST API and stdio MCP" readme = "README.md" requires-python = ">=3.11"