Files
QVPN/scripts/verify-xray-upgrade.ps1
Курнат Андрей aae6dd28dd Release QVPN 1.0.5 with Xray and resilient tunnel recovery
Integrate Xray 26.9.9 with the sing-box system tunnel and verify Windows routes during health checks.

Improve endpoint selection, network recovery, subscription refresh deadlines, diagnostics, packaging, and regression coverage.
2026-10-30 06:15:08 +03:00

73 lines
5.2 KiB
PowerShell

[CmdletBinding()]
param()
$ErrorActionPreference = 'Stop'
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
$dll = Join-Path $repo 'tests/QVPN.Tests/bin/Release/net10.0-windows/QVPN.Tests.dll'
$report = Join-Path $repo 'research/xray-upgrade-verification.json'
$phase = 'preflight'; $happWasRunning = $false
function Save-Phase([string]$name) {
$script:phase = $name
[pscustomobject]@{Completed=$false;Phase=$name;At=[DateTimeOffset]::Now.ToString('o')} | ConvertTo-Json | Set-Content -LiteralPath $report -Encoding utf8
}
function Send([string]$command,[string]$value='') {
$arguments=@($dll,'--client',$command); if($value){$arguments+=@('--value',$value)}
$response=& dotnet @arguments | ConvertFrom-Json
if($LASTEXITCODE -ne 0 -or !$response.Ok){throw "Service command failed: $command"}
return $response.Status
}
try {
$identity=[Security.Principal.WindowsIdentity]::GetCurrent()
if(!([Security.Principal.WindowsPrincipal]::new($identity)).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'Administrator required'}
$version=([xml](Get-Content (Join-Path $repo 'Directory.Build.props') -Raw)).Project.PropertyGroup.Version
$setup=Join-Path $repo "dist/QVPN-Setup-$version.exe"
$expected=(Get-Content (Join-Path $repo "dist/QVPN-Setup-$version.sha256") -Raw).Split(' ')[0]
if((Get-FileHash -LiteralPath $setup).Hash -ne $expected){throw 'Installer checksum mismatch'}
$before=Send 'status'; if(!$before.Nodes.Count){throw 'Cached subscription missing'}
$zurich=$before.Nodes | Where-Object {$_.Name -like '*Цюрих*'} | Select-Object -First 1
if(!$zurich){throw 'Zurich is missing from subscription'}
Save-Phase 'refresh current provider parameters'
$data=Join-Path $env:ProgramData 'QVPN'
Copy-Item -LiteralPath (Join-Path $data 'settings.dpapi') -Destination (Join-Path $data ("settings.dpapi.before-$version-"+[DateTime]::UtcNow.ToString('yyyyMMddHHmmss')))
$refreshed=Send 'refresh'
$zurich=$refreshed.Nodes | Where-Object {$_.Name -eq $zurich.Name} | Select-Object -First 1
if(!$zurich){throw 'Zurich is missing after refreshing provider parameters'}
Save-Phase 'release competing HAPP tunnel'
$happ=Get-Service HappService -ErrorAction SilentlyContinue
$happWasRunning=$happ -and $happ.Status -eq 'Running'
Get-Process Happ -ErrorAction SilentlyContinue | Where-Object {$_.Path -eq 'C:\Program Files\FlyFrogLLC\Happ\Happ.exe'} | Stop-Process -Force
if($happWasRunning){Stop-Service HappService; (Get-Service HappService).WaitForStatus('Stopped',[TimeSpan]::FromSeconds(30))}
Start-Sleep -Seconds 2
if(Get-NetAdapter -ErrorAction SilentlyContinue | Where-Object {$_.Name -like '*happ*' -and $_.Status -eq 'Up'}){throw 'HAPP adapter is still active'}
$null=Send 'select' $zurich.Id
Save-Phase 'install Xray update preserving encrypted settings'
$timer=[Diagnostics.Stopwatch]::StartNew()
for($attempt=0;$attempt -lt 2;$attempt++) {
$installer=Start-Process -FilePath $setup -ArgumentList '--silent' -WindowStyle Hidden -PassThru -Wait
if($installer.ExitCode -eq 0){break}
if($attempt -eq 1){throw 'Installer failed twice'}
Start-Sleep -Seconds 5
}
Save-Phase 'connect installed Xray to Zurich'
$null=Send 'connect'; $timer.Restart()
do {
Start-Sleep -Milliseconds 250
$status=Send 'status'
if($status.State -eq 'connected' -and $status.VpnCore -eq 'Xray' -and $status.XrayPid){break}
}while($timer.Elapsed.TotalSeconds -lt 60)
if($status.State -ne 'connected' -or $status.VpnCore -ne 'Xray'){throw 'Installed Xray connection did not complete'}
if($status.ActiveName -ne $zurich.Name){throw 'Selected Zurich was not retained'}
$connectSeconds=[Math]::Round($timer.Elapsed.TotalSeconds,2)
Save-Phase 'verify installed Xray recovery and continuous TUN traffic'
$verification=Start-Process -FilePath (Join-Path $PSHOME 'pwsh.exe') -WindowStyle Hidden -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',(Join-Path $PSScriptRoot 'verify-stability-installed.ps1'),'-ObserveSeconds','300') -PassThru -Wait
if($verification.ExitCode -ne 0){throw 'Installed recovery/stability verification failed'}
# Return the competing client to the state observed before this diagnostic run.
# The updated QVPN service and selected location remain installed for normal use.
if($happWasRunning -and !$before.DesiredConnected){$null=Send 'disconnect'; Start-Service HappService}
[pscustomobject]@{Completed=$true;At=[DateTimeOffset]::Now.ToString('o');Version=$version;ConnectionSeconds=$connectSeconds;VpnCore='Xray';Location='Zurich';KeyRetained=$true;HappStopped=$happWasRunning;HappRestored=($happWasRunning -and !$before.DesiredConnected);RecoveryReport='installed-stability-verification.json'} | ConvertTo-Json | Set-Content -LiteralPath $report -Encoding utf8
} catch {
try{$null=Send 'disconnect'}catch{}
if($happWasRunning){Start-Service HappService -ErrorAction SilentlyContinue}
[pscustomobject]@{Completed=$false;At=[DateTimeOffset]::Now.ToString('o');Phase=$phase;Failure=$_.Exception.Message;HappRestored=$happWasRunning} | ConvertTo-Json | Set-Content -LiteralPath $report -Encoding utf8
exit 1
}