param([switch]$SkipTests) $ErrorActionPreference = 'Stop' $repoPath = Split-Path -Parent $PSScriptRoot $qaPath = Join-Path $repoPath 'artifacts/windows-qa' $releaseVersion = ([xml](Get-Content -Raw (Join-Path $PSScriptRoot 'Directory.Build.props'))).Project.PropertyGroup.Version $outputPath = Join-Path $repoPath "artifacts/QSafe-Windows-$releaseVersion" $buildPath = Join-Path $repoPath 'artifacts/windows-lan-build' New-Item -ItemType Directory -Force $qaPath | Out-Null Push-Location $repoPath try { if (-not $SkipTests) { if ((Get-Command javac -ErrorAction SilentlyContinue) -and (Get-Command java -ErrorAction SilentlyContinue)) { New-Item -ItemType Directory -Force (Join-Path $qaPath 'java') | Out-Null & javac -encoding UTF-8 -d "$qaPath/java" app/src/main/java/ru/kusoft/qsafe/VaultCrypto.java windows/QSafe.Tests/InteropFixture.java if ($LASTEXITCODE -ne 0) { throw 'Java fixture compilation failed' } & java -cp "$qaPath/java" InteropFixture write $qaPath if ($LASTEXITCODE -ne 0) { throw 'Java fixture failed' } } & dotnet restore windows/QSafe.Tests -p:UseArtifactsOutput=true "-p:ArtifactsPath=$buildPath" --source "$repoPath/.gradle" --nologo if ($LASTEXITCODE -ne 0) { throw 'Local dependency restore failed' } & dotnet run --project windows/QSafe.Tests -c Release --no-restore -p:UseArtifactsOutput=true "-p:ArtifactsPath=$buildPath" -- $qaPath | Tee-Object -FilePath "$qaPath/tests.log" if ($LASTEXITCODE -ne 0) { throw 'Windows tests failed' } if ((Get-Command java -ErrorAction SilentlyContinue) -and (Test-Path "$qaPath/java/InteropFixture.class")) { & java -cp "$qaPath/java" InteropFixture verify $qaPath | Tee-Object -FilePath "$qaPath/java-interop.log" if ($LASTEXITCODE -ne 0) { throw 'Android interop failed' } } } & dotnet restore windows/QSafe.Desktop -r win-x64 -p:UseArtifactsOutput=true "-p:ArtifactsPath=$buildPath" --source "$repoPath/.gradle" --nologo if ($LASTEXITCODE -ne 0) { throw 'Local runtime restore failed' } & dotnet publish windows/QSafe.Desktop/QSafe.Desktop.csproj -c Release -r win-x64 --self-contained true --no-restore -p:UseArtifactsOutput=true "-p:ArtifactsPath=$buildPath" -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -o $outputPath --nologo if ($LASTEXITCODE -ne 0) { throw 'Publish failed' } Copy-Item -LiteralPath windows/QUICKSTART.txt -Destination "$outputPath/ПРОЧИТАЙТЕ.txt" $smokeProcess = Start-Process -FilePath "$outputPath/QSafe.exe" -ArgumentList '--smoke-test' -WindowStyle Hidden -PassThru if (-not $smokeProcess.WaitForExit(15000)) { $smokeProcess.Kill(); throw 'Startup smoke test timed out' } if ($smokeProcess.ExitCode -ne 0) { throw "Startup smoke test failed: $($smokeProcess.ExitCode)" } 'PASS published self-contained executable starts and exits cleanly with synthetic demo' | Set-Content -LiteralPath "$qaPath/startup.log" $zipPath = Join-Path $repoPath "artifacts/QSafe-Windows-$releaseVersion.zip" Compress-Archive -Path "$outputPath/*" -DestinationPath $zipPath -Force Get-FileHash -LiteralPath "$outputPath/QSafe.exe", $zipPath -Algorithm SHA256 | ForEach-Object { "$($_.Hash.ToLowerInvariant()) $([System.IO.Path]::GetFileName($_.Path))" } | Set-Content -LiteralPath (Join-Path $repoPath "artifacts/QSafe-Windows-$releaseVersion.sha256") Write-Output "Ready: $outputPath/QSafe.exe" } finally { Pop-Location }