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.
106 lines
8.0 KiB
PowerShell
106 lines
8.0 KiB
PowerShell
[CmdletBinding()]
|
|
param([string]$Configuration = 'Release', [switch]$SkipTests)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$projectRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
$distributionRoot = Join-Path $projectRoot 'dist'
|
|
$payloadRoot = Join-Path $distributionRoot 'payload'
|
|
$setupPublishRoot = Join-Path $distributionRoot 'setup-publish'
|
|
$version = ([xml](Get-Content -LiteralPath (Join-Path $projectRoot 'Directory.Build.props') -Raw)).Project.PropertyGroup.Version
|
|
|
|
function Reset-BuildDirectory([string]$Path) {
|
|
$absoluteTarget = [IO.Path]::GetFullPath($Path)
|
|
$allowedRoot = [IO.Path]::GetFullPath($distributionRoot) + [IO.Path]::DirectorySeparatorChar
|
|
if (-not $absoluteTarget.StartsWith($allowedRoot, [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw "Refusing to remove outside dist: $absoluteTarget"
|
|
}
|
|
if (Test-Path -LiteralPath $absoluteTarget) {
|
|
if ((Get-Item -LiteralPath $absoluteTarget -Force).Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
|
throw "Build directory is a reparse point: $absoluteTarget"
|
|
}
|
|
$linkedEntry = Get-ChildItem -LiteralPath $absoluteTarget -Recurse -Force | Where-Object { $_.Attributes -band [IO.FileAttributes]::ReparsePoint } | Select-Object -First 1
|
|
if ($linkedEntry) { throw "Build directory contains a reparse point: $($linkedEntry.FullName)" }
|
|
Remove-Item -LiteralPath $absoluteTarget -Recurse -Force
|
|
}
|
|
New-Item -ItemType Directory -Path $absoluteTarget -Force | Out-Null
|
|
}
|
|
|
|
function Invoke-DotNet([string[]]$Arguments) {
|
|
& dotnet @Arguments
|
|
if ($LASTEXITCODE -ne 0) { throw "dotnet failed ($LASTEXITCODE): $($Arguments -join ' ')" }
|
|
}
|
|
|
|
$coreRoot = Join-Path $projectRoot 'tools/core-unpacked/sing-box-1.14.2-windows-amd64'
|
|
$rulesRoot = Join-Path $projectRoot 'tools/rules'
|
|
$xrayRoot = Join-Path $projectRoot 'tools/xray-unpacked'
|
|
foreach ($coreFile in @('sing-box.exe', 'libcronet.dll', 'LICENSE')) {
|
|
if (-not (Test-Path -LiteralPath (Join-Path $coreRoot $coreFile) -PathType Leaf)) { throw "Missing packaged VPN core: $coreFile" }
|
|
}
|
|
foreach ($xrayFile in @('xray.exe', 'wintun.dll', 'LICENSE', 'LICENSE-Wintun')) {
|
|
if (-not (Test-Path -LiteralPath (Join-Path $xrayRoot $xrayFile) -PathType Leaf)) { throw "Missing packaged Xray core: $xrayFile. Run scripts/prepare-xray.ps1." }
|
|
}
|
|
if (-not (Test-Path -LiteralPath $rulesRoot) -or -not (Get-ChildItem -LiteralPath $rulesRoot -Filter '*.srs' -File)) {
|
|
throw 'Missing routing rules in tools/rules. The installer never downloads dependencies.'
|
|
}
|
|
foreach ($project in @('src/QVPN.App/QVPN.App.csproj', 'src/QVPN.Service/QVPN.Service.csproj', 'src/QVPN.Setup/QVPN.Setup.csproj')) {
|
|
if (-not (Test-Path -LiteralPath (Join-Path $projectRoot $project))) { throw "Missing project: $project" }
|
|
}
|
|
|
|
if (-not $SkipTests) {
|
|
$testProjects = @(Get-ChildItem -LiteralPath (Join-Path $projectRoot 'tests') -Filter '*.csproj' -File -Recurse -ErrorAction SilentlyContinue)
|
|
foreach ($testProject in $testProjects) { Invoke-DotNet -Arguments @('run', '--project', $testProject.FullName, '-c', $Configuration) }
|
|
}
|
|
|
|
New-Item -ItemType Directory -Path $distributionRoot -Force | Out-Null
|
|
Reset-BuildDirectory $payloadRoot
|
|
Reset-BuildDirectory $setupPublishRoot
|
|
Write-Host 'Publishing Windows UI and service with the bundled .NET runtime...'
|
|
Invoke-DotNet -Arguments @('publish', (Join-Path $projectRoot 'src/QVPN.App/QVPN.App.csproj'), '-c', $Configuration, '-r', 'win-x64', '--self-contained', 'true', '-p:PublishSingleFile=true', '-p:IncludeNativeLibrariesForSelfExtract=true', '-o', (Join-Path $payloadRoot 'app'))
|
|
Invoke-DotNet -Arguments @('publish', (Join-Path $projectRoot 'src/QVPN.Service/QVPN.Service.csproj'), '-c', $Configuration, '-r', 'win-x64', '--self-contained', 'true', '-p:PublishSingleFile=false', '-o', (Join-Path $payloadRoot 'service'))
|
|
New-Item -ItemType Directory -Path (Join-Path $payloadRoot 'core'), (Join-Path $payloadRoot 'rules') -Force | Out-Null
|
|
Copy-Item -LiteralPath (Join-Path $coreRoot 'sing-box.exe'), (Join-Path $coreRoot 'libcronet.dll'), (Join-Path $coreRoot 'LICENSE') -Destination (Join-Path $payloadRoot 'core')
|
|
Copy-Item -LiteralPath (Join-Path $xrayRoot 'xray.exe'), (Join-Path $xrayRoot 'wintun.dll'), (Join-Path $xrayRoot 'LICENSE-Wintun') -Destination (Join-Path $payloadRoot 'core')
|
|
Copy-Item -LiteralPath (Join-Path $xrayRoot 'LICENSE') -Destination (Join-Path $payloadRoot 'core/LICENSE-Xray')
|
|
Copy-Item -LiteralPath (Join-Path $projectRoot 'tools/Xray-core-v26.9.9-source.zip') -Destination (Join-Path $payloadRoot 'core/Xray-core-v26.9.9-source.zip')
|
|
Get-ChildItem -LiteralPath $rulesRoot -Filter '*.srs' -File | Copy-Item -Destination (Join-Path $payloadRoot 'rules')
|
|
if (Test-Path -LiteralPath (Join-Path $projectRoot 'README.md')) { Copy-Item -LiteralPath (Join-Path $projectRoot 'README.md') -Destination (Join-Path $payloadRoot 'README.md') }
|
|
if (Test-Path -LiteralPath (Join-Path $PSScriptRoot '../src/QVPN.Setup/COPYING')) { Copy-Item -LiteralPath (Join-Path $PSScriptRoot '../src/QVPN.Setup/COPYING') -Destination (Join-Path $payloadRoot 'core/COPYING') }
|
|
if (Test-Path -LiteralPath (Join-Path $projectRoot 'tools/sing-box-1.14.2-source.zip')) { Copy-Item -LiteralPath (Join-Path $projectRoot 'tools/sing-box-1.14.2-source.zip') -Destination (Join-Path $payloadRoot 'core/sing-box-1.14.2-source.zip') }
|
|
@'
|
|
QVPN uses unmodified Xray-core v26.9.9 (Windows amd64), copyright Project X contributors, for VLESS/Reality.
|
|
Xray is distributed under Mozilla Public License 2.0 in core/LICENSE-Xray.
|
|
The original upstream source is included in core/Xray-core-v26.9.9-source.zip.
|
|
Source: https://github.com/XTLS/Xray-core/tree/v26.9.9
|
|
Release: https://github.com/XTLS/Xray-core/releases/tag/v26.9.9
|
|
Wintun license is included in core/LICENSE-Wintun.
|
|
QVPN includes sing-box v1.14.2 (Windows amd64), copyright nekohasekai / SagerNet.
|
|
sing-box provides the Windows TUN, DNS handling and routing to the local Xray proxy.
|
|
sing-box is distributed under GNU GPL v3 or later with the notice in core/LICENSE.
|
|
Upstream source archive for the unmodified binary: core/sing-box-1.14.2-source.zip
|
|
Source tree and module/build instructions: https://github.com/SagerNet/sing-box/tree/v1.14.2
|
|
Release and original archive: https://github.com/SagerNet/sing-box/releases/tag/v1.14.2
|
|
The bundled rule sets are from https://github.com/SagerNet/sing-geosite and https://github.com/SagerNet/sing-geoip.
|
|
See the respective repositories for source data and notices.
|
|
'@ | Set-Content -LiteralPath (Join-Path $payloadRoot 'THIRD-PARTY-NOTICES.txt') -Encoding utf8
|
|
|
|
Get-ChildItem -LiteralPath $payloadRoot -Filter '*.pdb' -Recurse -File | Remove-Item -Force
|
|
$manifest = [ordered]@{}
|
|
foreach ($file in (Get-ChildItem -LiteralPath $payloadRoot -Recurse -File | Sort-Object FullName)) {
|
|
$relativePath = [IO.Path]::GetRelativePath($payloadRoot, $file.FullName)
|
|
$manifest[$relativePath] = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256).Hash
|
|
}
|
|
$manifest | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (Join-Path $payloadRoot 'payload-manifest.json') -Encoding utf8
|
|
$payloadZip = Join-Path $distributionRoot 'payload.zip'
|
|
if (Test-Path -LiteralPath $payloadZip) { Remove-Item -LiteralPath $payloadZip -Force }
|
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
[IO.Compression.ZipFile]::CreateFromDirectory($payloadRoot, $payloadZip, [IO.Compression.CompressionLevel]::Optimal, $false)
|
|
Write-Host 'Embedding the complete payload in the offline Windows installer...'
|
|
Invoke-DotNet -Arguments @('publish', (Join-Path $projectRoot 'src/QVPN.Setup/QVPN.Setup.csproj'), '-c', $Configuration, '-r', 'win-x64', '--self-contained', 'true', '-o', $setupPublishRoot)
|
|
$installerName = "QVPN-Setup-$version.exe"
|
|
$installer = Join-Path $distributionRoot $installerName
|
|
Copy-Item -LiteralPath (Join-Path $setupPublishRoot 'QVPN-Setup.exe') -Destination $installer -Force
|
|
$installerHash = (Get-FileHash -LiteralPath $installer -Algorithm SHA256).Hash
|
|
"$installerHash $installerName" | Set-Content -LiteralPath (Join-Path $distributionRoot "QVPN-Setup-$version.sha256") -Encoding ascii
|
|
Write-Host "Installer: $installer"
|
|
Write-Host "SHA256: $installerHash"
|