Release v1.11.1 #60
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| pull_request: | |
| branches: [main] | |
| types: [closed] | |
| permissions: | |
| contents: write | |
| id-token: write | |
| actions: read | |
| jobs: | |
| release: | |
| if: github.event.pull_request.merged == true && github.event.pull_request.head.ref == 'dev' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Get version | |
| id: version | |
| shell: pwsh | |
| run: | | |
| $version = ([xml](Get-Content src/Directory.Build.props)).Project.PropertyGroup.Version | Where-Object { $_ } | |
| echo "VERSION=$version" >> $env:GITHUB_OUTPUT | |
| - name: Check if release already exists | |
| id: check | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| if gh release view "v${{ steps.version.outputs.VERSION }}" > /dev/null 2>&1; then | |
| echo "EXISTS=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "EXISTS=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create release | |
| if: steps.check.outputs.EXISTS == 'false' | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release create "v${{ steps.version.outputs.VERSION }}" --title "v${{ steps.version.outputs.VERSION }}" --generate-notes --target main | |
| - name: Setup .NET 10.0 | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Build and test | |
| run: | | |
| dotnet restore | |
| dotnet build -c Release | |
| dotnet test tests/PlanViewer.Core.Tests/PlanViewer.Core.Tests.csproj -c Release --no-build --verbosity normal | |
| - name: Publish App (all platforms) | |
| run: | | |
| dotnet publish src/PlanViewer.App/PlanViewer.App.csproj -c Release -r win-x64 --self-contained -o publish/win-x64 | |
| dotnet publish src/PlanViewer.App/PlanViewer.App.csproj -c Release -r linux-x64 --self-contained -o publish/linux-x64 | |
| dotnet publish src/PlanViewer.App/PlanViewer.App.csproj -c Release -r osx-x64 --self-contained -o publish/osx-x64 | |
| dotnet publish src/PlanViewer.App/PlanViewer.App.csproj -c Release -r osx-arm64 --self-contained -o publish/osx-arm64 | |
| # ── SSMS extension VSIX (issue #343 — get it into automated builds) ── | |
| # PlanViewer.Ssms is a legacy non-SDK project and is not in PlanViewer.sln, | |
| # so the `dotnet build` above never touches it. It needs full MSBuild plus | |
| # the VSSDK build targets. continue-on-error keeps a VSIX build failure | |
| # from blocking the (critical-path) cross-platform app release. | |
| - name: Add MSBuild to PATH | |
| uses: microsoft/setup-msbuild@v2 | |
| continue-on-error: true | |
| - name: Build SSMS extension | |
| id: ssms | |
| continue-on-error: true | |
| shell: pwsh | |
| env: | |
| VERSION: ${{ steps.version.outputs.VERSION }} | |
| run: | | |
| $manifest = 'src/PlanViewer.Ssms/source.extension.vsixmanifest' | |
| $manifestVersion = ([xml](Get-Content $manifest)).PackageManifest.Metadata.Identity.Version | |
| if ($manifestVersion -ne $env:VERSION) { | |
| Write-Host "::warning::VSIX manifest version ($manifestVersion) != release version ($env:VERSION) — bump source.extension.vsixmanifest and Properties/AssemblyInfo.cs" | |
| } | |
| msbuild src/PlanViewer.Ssms/PlanViewer.Ssms.csproj -t:Restore,Build -p:Configuration=Release -p:DeployExtension=false | |
| dotnet build src/PlanViewer.Ssms.Installer/PlanViewer.Ssms.Installer.csproj -c Release | |
| $vsix = 'src/PlanViewer.Ssms/bin/Release/PlanViewer.Ssms.vsix' | |
| $exe = 'src/PlanViewer.Ssms.Installer/bin/Release/net472/InstallSsmsExtension.exe' | |
| if (-not (Test-Path $vsix)) { throw "VSIX not produced at $vsix" } | |
| if (-not (Test-Path $exe)) { throw "Installer not produced at $exe" } | |
| New-Item -ItemType Directory -Force -Path releases | Out-Null | |
| Copy-Item $vsix 'releases/PlanViewer.Ssms.vsix' | |
| Copy-Item $exe 'releases/InstallSsmsExtension.exe' | |
| - name: Upload SSMS extension to release | |
| if: steps.ssms.outcome == 'success' | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.version.outputs.VERSION }} | |
| run: gh release upload "v$env:VERSION" releases/PlanViewer.Ssms.vsix releases/InstallSsmsExtension.exe --clobber | |
| - name: Warn if SSMS extension build failed | |
| if: steps.ssms.outcome != 'success' | |
| run: echo "::warning::SSMS extension VSIX build failed — release published without it (issue #343)" | |
| # ── SignPath code signing (Windows only, skipped if secret not configured) ── | |
| - name: Check if signing is configured | |
| id: signing | |
| shell: bash | |
| run: | | |
| if [ -n "${{ secrets.SIGNPATH_API_TOKEN }}" ]; then | |
| echo "ENABLED=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "ENABLED=false" >> $GITHUB_OUTPUT | |
| echo "::warning::SIGNPATH_API_TOKEN not configured — releasing unsigned binaries" | |
| fi | |
| - name: Upload Windows build for signing | |
| if: steps.signing.outputs.ENABLED == 'true' | |
| id: upload-unsigned | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: App-unsigned | |
| path: publish/win-x64/ | |
| - name: Sign Windows build | |
| if: steps.signing.outputs.ENABLED == 'true' | |
| uses: signpath/github-action-submit-signing-request@v2 | |
| with: | |
| api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' | |
| organization-id: '7969f8b6-d946-4a74-9bac-a55856d8b8e0' | |
| project-slug: 'PerformanceStudio' | |
| signing-policy-slug: 'release-signing' | |
| artifact-configuration-slug: 'App' | |
| github-artifact-id: '${{ steps.upload-unsigned.outputs.artifact-id }}' | |
| wait-for-completion: true | |
| output-artifact-directory: 'signed/win-x64' | |
| - name: Replace unsigned Windows build with signed | |
| if: steps.signing.outputs.ENABLED == 'true' | |
| shell: pwsh | |
| run: | | |
| Remove-Item -Recurse -Force publish/win-x64 | |
| Copy-Item -Recurse signed/win-x64 publish/win-x64 | |
| # ── Velopack (uses signed Windows binaries) ─────────────────────── | |
| - name: Create Velopack release (Windows) | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.version.outputs.VERSION }} | |
| run: | | |
| dotnet tool install -g vpk | |
| New-Item -ItemType Directory -Force -Path releases/velopack | |
| # Download previous release for delta generation | |
| vpk download github --repoUrl https://github.com/${{ github.repository }} --channel win -o releases/velopack --token $env:GH_TOKEN | |
| # Pack Windows release (now signed) | |
| vpk pack -u PerformanceStudio -v $env:VERSION -p publish/win-x64 -e PlanViewer.App.exe -o releases/velopack --channel win | |
| # ── Package and upload ──────────────────────────────────────────── | |
| - name: Package and upload | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.version.outputs.VERSION }} | |
| run: | | |
| New-Item -ItemType Directory -Force -Path releases | |
| # Package Windows (signed) and Linux as flat zips | |
| foreach ($rid in @('win-x64', 'linux-x64')) { | |
| if (Test-Path 'README.md') { Copy-Item 'README.md' "publish/$rid/" } | |
| if (Test-Path 'LICENSE') { Copy-Item 'LICENSE' "publish/$rid/" } | |
| Compress-Archive -Path "publish/$rid/*" -DestinationPath "releases/PerformanceStudio-$rid.zip" -Force | |
| } | |
| # Package macOS as proper .app bundles | |
| foreach ($rid in @('osx-x64', 'osx-arm64')) { | |
| $appName = "PerformanceStudio.app" | |
| $bundleDir = "publish/$rid-bundle/$appName" | |
| # Create .app bundle structure | |
| New-Item -ItemType Directory -Force -Path "$bundleDir/Contents/MacOS" | |
| New-Item -ItemType Directory -Force -Path "$bundleDir/Contents/Resources" | |
| # Copy all published files into Contents/MacOS | |
| Copy-Item -Path "publish/$rid/*" -Destination "$bundleDir/Contents/MacOS/" -Recurse | |
| # Move Info.plist to Contents/ (it was copied to MacOS/ with the publish output) | |
| if (Test-Path "$bundleDir/Contents/MacOS/Info.plist") { | |
| Move-Item -Path "$bundleDir/Contents/MacOS/Info.plist" -Destination "$bundleDir/Contents/Info.plist" -Force | |
| } | |
| # Update version in Info.plist to match csproj | |
| $plist = Get-Content "$bundleDir/Contents/Info.plist" -Raw | |
| $plist = $plist -replace '(<key>CFBundleVersion</key>\s*<string>)[^<]*(</string>)', "`${1}$env:VERSION`${2}" | |
| $plist = $plist -replace '(<key>CFBundleShortVersionString</key>\s*<string>)[^<]*(</string>)', "`${1}$env:VERSION`${2}" | |
| Set-Content -Path "$bundleDir/Contents/Info.plist" -Value $plist -NoNewline | |
| # Move icon to Contents/Resources | |
| if (Test-Path "$bundleDir/Contents/MacOS/EDD.icns") { | |
| Move-Item -Path "$bundleDir/Contents/MacOS/EDD.icns" -Destination "$bundleDir/Contents/Resources/EDD.icns" -Force | |
| } | |
| # Add README and LICENSE alongside the .app bundle | |
| $wrapperDir = "publish/$rid-bundle" | |
| if (Test-Path 'README.md') { Copy-Item 'README.md' "$wrapperDir/" } | |
| if (Test-Path 'LICENSE') { Copy-Item 'LICENSE' "$wrapperDir/" } | |
| Compress-Archive -Path "$wrapperDir/*" -DestinationPath "releases/PerformanceStudio-$rid.zip" -Force | |
| } | |
| # Checksums (zips only, Velopack has its own checksums) | |
| $checksums = Get-ChildItem releases/*.zip | ForEach-Object { | |
| $hash = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLower() | |
| "$hash $($_.Name)" | |
| } | |
| $checksums | Out-File -FilePath releases/SHA256SUMS.txt -Encoding utf8 | |
| Write-Host "Checksums:" | |
| $checksums | ForEach-Object { Write-Host $_ } | |
| # Upload zips + checksums | |
| gh release upload "v$env:VERSION" releases/*.zip releases/SHA256SUMS.txt --clobber | |
| # Upload Velopack artifacts | |
| vpk upload github --repoUrl https://github.com/${{ github.repository }} --channel win -o releases/velopack --releaseName "v$env:VERSION" --tag "v$env:VERSION" --merge --token $env:GH_TOKEN |