Files
OpenNest/.github/workflows/windows-release.yml
T
ajandClaude Opus 5.5 7fe4de4b6d feat(release): ship self-updating Velopack packages on GitHub Releases
Publish-Windows.ps1 now packs the smoke-tested folder with the pinned vpk
CLI (.config/dotnet-tools.json, 1.2.0 to match the Velopack package) into
velopack/: Setup.exe, Portable.zip, the full nupkg, releases.win.json and
RELEASES, checked for the full package and the portable layout and hashed
into OpenNest.vX.Y.Z.velopack.sha256. The release workflow uploads them
with the candidate.

With no feed URL set, installed copies now check GitHub Releases (full
releases only) instead of skipping update checks; a URL in Options still
selects a self-hosted feed. Deploy-OpenNest.ps1 uses the pinned vpk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:52:30 -04:00

115 lines
5.1 KiB
YAML

name: Windows release build
on:
push:
branches: ['release/**']
tags: ['v*']
workflow_dispatch:
inputs:
version:
description: 'Release version (for example 0.3.0)'
required: true
type: string
permissions:
contents: read
jobs:
windows:
runs-on: windows-2022
# The suites have roughly doubled since the v0.3.0 run (7.5 min); the hang
# guard below names a stuck test long before this cap.
timeout-minutes: 45
defaults:
run:
shell: pwsh
env:
DOTNET_CLI_TELEMETRY_OPTOUT: '1'
DOTNET_NOLOGO: '1'
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
dotnet-version: '8.0.x'
- name: Resolve version
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
$version = $env:INPUT_VERSION
if ($env:GITHUB_EVENT_NAME -eq 'push') {
$version = $env:GITHUB_REF_NAME -replace '^release/', '' -replace '^v', ''
}
if ($version -notmatch '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$') {
throw "Expected an X.Y.Z version, not '$version'."
}
"RELEASE_VERSION=$version" >> $env:GITHUB_ENV
- name: Build solution
run: |
dotnet build OpenNest.sln -c Release
if ($LASTEXITCODE -ne 0) { throw 'Solution build failed.' }
- name: Install pinned Poppler for PDF report tests
run: |
$zip = Join-Path $env:RUNNER_TEMP 'poppler-26.09.0.zip'
Invoke-WebRequest 'https://github.com/oschwartz10612/poppler-windows/releases/download/v26.09.0-0/Release-26.09.0-0.zip' -OutFile $zip
$actual = (Get-FileHash $zip -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actual -ne '7a6f256a0ddf7536182246a5733331bf4677cbcc34f4663774947ad34556c8d0') { throw 'Poppler archive hash mismatch.' }
$destination = Join-Path $env:RUNNER_TEMP 'poppler-release-tests'
Expand-Archive $zip -DestinationPath $destination
$bin = Join-Path $destination 'poppler-26.09.0/Library/bin'
$tool = Join-Path $bin 'pdftotext.exe'
if (-not (Test-Path $tool)) { throw 'Pinned pdftotext.exe is missing.' }
& $tool -v
if ($LASTEXITCODE -ne 0) { throw 'Pinned pdftotext.exe cannot start.' }
$bin >> $env:GITHUB_PATH
- name: Run all test projects on Windows
# A test silent for 5 minutes is killed and named in the log, with a
# mini dump in TestResults, instead of holding the runner until the
# job timeout.
run: |
foreach ($project in @('OpenNest.Tests', 'OpenNest.Desktop.Tests', 'OpenNest.Engine.Tests', 'OpenNest.IO.Tests', 'OpenNest.Server.Tests', 'OpenNest.WinForms.Tests', 'OpenNest.FrontEnd.Tests')) {
dotnet test "$project/$project.csproj" -c Release --no-build --blame-hang-timeout 5m --blame-hang-dump-type mini --logger "trx;LogFileName=$project.trx" --results-directory TestResults
if ($LASTEXITCODE -ne 0) { throw "$project failed." }
}
dotnet test OpenNest.Tests/OpenNest.Tests.csproj -c Debug --blame-hang-timeout 5m --blame-hang-dump-type mini --logger 'trx;LogFileName=OpenNest.Tests.Debug.trx' --results-directory TestResults
if ($LASTEXITCODE -ne 0) { throw 'Debug tests failed.' }
- name: Publish and verify Windows package
run: ./scripts/Publish-Windows.ps1 -Version $env:RELEASE_VERSION
- name: Verify overwrite protection
run: |
$zip = Get-ChildItem artifacts/*.zip
$before = (Get-FileHash $zip.FullName).Hash
$rejected = $false
try { ./scripts/Publish-Windows.ps1 -Version $env:RELEASE_VERSION }
catch {
if ($_.Exception.Message -notlike 'Refusing to overwrite existing output:*') { throw }
$rejected = $true
}
if (-not $rejected) { throw 'Existing output was not rejected.' }
if ((Get-FileHash $zip.FullName).Hash -ne $before) { throw 'Existing ZIP changed.' }
Write-Host 'PASS: existing release package preserved.'
- name: Upload verified release candidate
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: OpenNest-${{ env.RELEASE_VERSION }}-win-x64
path: |
artifacts/*.zip
artifacts/*.sha256
artifacts/velopack/OpenNest-win-Setup.exe
artifacts/velopack/OpenNest-win-Portable.zip
artifacts/velopack/*-full.nupkg
artifacts/velopack/releases.win.json
artifacts/velopack/RELEASES
if-no-files-found: error
retention-days: 14
compression-level: 0
- name: Upload test results
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: windows-test-results
path: TestResults/
if-no-files-found: warn
retention-days: 14