Skip to content

Feature/3d plotting v2 (#4) #11

Feature/3d plotting v2 (#4)

Feature/3d plotting v2 (#4) #11

name: Release Packaging
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
permissions:
contents: write
jobs:
package:
runs-on: windows-latest
env:
# Opt into Node.js 24 for all JavaScript actions ahead of the June 2026 forced migration.
# Remove once all actions used here ship native Node.js 24 support.
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
# Update these toolchain pins when the runner image changes or when a dependency upgrade
# is required. Keep WiX major version aligned with packaging/build-packages.ps1 checks.
DOTNET_VERSION: "9.0.x"
PYTHON_VERSION: "3.12"
WIX_VERSION: "6.0.2"
BUILD_CONFIGURATION: "Release"
BUILD_PLATFORM: "x64"
MSVC_PLATFORM_TOOLSET: "v143"
# Signing is optional and intentionally disabled by default so the pipeline keeps working
# before a code-signing certificate is configured.
ENABLE_CODESIGN: "false"
# GitHub Actions does not allow direct secrets.* references inside step `if:` expressions.
# Compute a boolean once in job env and reference that in the conditional step below.
HAS_CODESIGN_SECRETS: ${{ secrets.WINDOWS_CODESIGN_PFX_BASE64 != '' && secrets.WINDOWS_CODESIGN_PFX_PASSWORD != '' }}
# Override via GitHub variable WINDOWS_CODESIGN_TIMESTAMP_URL if your CA requires another URL.
CODE_SIGN_TIMESTAMP_URL: "http://timestamp.digicert.com"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
# Optional signing bootstrap. This step only runs when BOTH secrets exist:
# - WINDOWS_CODESIGN_PFX_BASE64
# - WINDOWS_CODESIGN_PFX_PASSWORD
# To keep unsigned behavior, do nothing (leave secrets unset).
#
# When you later enable signing, no pipeline logic changes are required; just add secrets
# (and optionally the WINDOWS_CODESIGN_TIMESTAMP_URL variable).
- name: Prepare Optional Code Signing
if: env.HAS_CODESIGN_SECRETS == 'true'
shell: pwsh
env:
TIMESTAMP_URL_OVERRIDE: ${{ vars.WINDOWS_CODESIGN_TIMESTAMP_URL }}
run: |
$ErrorActionPreference = "Stop"
$pfxPath = Join-Path $env:RUNNER_TEMP "binxray-codesign.pfx"
[System.IO.File]::WriteAllBytes(
$pfxPath,
[Convert]::FromBase64String("${{ secrets.WINDOWS_CODESIGN_PFX_BASE64 }}")
)
$signtoolCmd = Get-Command signtool.exe -ErrorAction SilentlyContinue
$signtoolPath = $null
if ($signtoolCmd) {
$signtoolPath = $signtoolCmd.Source
} else {
$candidate = Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin" -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match "\\x64\\signtool\.exe$" } |
Sort-Object FullName -Descending |
Select-Object -First 1
if ($candidate) {
$signtoolPath = $candidate.FullName
}
}
if (-not $signtoolPath) {
throw "signtool.exe not found on runner."
}
$timestampUrl = $env:CODE_SIGN_TIMESTAMP_URL
if (-not [string]::IsNullOrWhiteSpace($env:TIMESTAMP_URL_OVERRIDE)) {
$timestampUrl = $env:TIMESTAMP_URL_OVERRIDE
}
"ENABLE_CODESIGN=true" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"CODE_SIGN_PFX=$pfxPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"CODE_SIGN_PWD=${{ secrets.WINDOWS_CODESIGN_PFX_PASSWORD }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SIGNTOOL_EXE=$signtoolPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"CODE_SIGN_TIMESTAMP_URL=$timestampUrl" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "Optional Authenticode signing enabled."
Write-Host "Using SignTool: $signtoolPath"
- name: Show MSBuild Version
shell: pwsh
run: |
msbuild -version
- name: Install WiX CLI
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
$globalTools = dotnet tool list --global
if ($globalTools -match '(^|\s)wix(\s|$)') {
dotnet tool update --global wix --version $env:WIX_VERSION
}
else {
dotnet tool install --global wix --version $env:WIX_VERSION
}
$extensions = ""
$PSNativeCommandUseErrorActionPreference = $false
try {
$extensions = wix extension list --global 2>$null
if ($extensions -match 'WixToolset\.Bal\.wixext') {
wix extension remove --global WixToolset.Bal.wixext 2>$null
}
}
finally {
$PSNativeCommandUseErrorActionPreference = $true
}
wix extension add --global "WixToolset.Bal.wixext/$env:WIX_VERSION"
$wixVersion = (wix --version).Trim()
if (-not $wixVersion.StartsWith("6.")) {
throw "Expected WiX 6.x but found '$wixVersion'."
}
Write-Host "Using WiX $wixVersion"
- name: Read App Version
id: version
shell: pwsh
run: |
$version = python scripts/get_version.py
if (-not $version) { throw "Failed to read version from Version.h" }
"app_version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
- name: Validate Tag Matches Version
if: startsWith(github.ref, 'refs/tags/')
shell: pwsh
run: |
$expected = "v${{ steps.version.outputs.app_version }}"
$actual = "${{ github.ref_name }}"
if ($actual -ne $expected) {
throw "Tag '$actual' does not match Version.h ('$expected')."
}
- name: Build Release Binary
shell: pwsh
run: |
$solutionDir = Join-Path $env:GITHUB_WORKSPACE "src\"
$repoUrl = "${{ github.server_url }}/${{ github.repository }}"
$branch = "${{ github.ref_name }}"
$commit = "${{ github.sha }}"
$buildVersion = "${{ steps.version.outputs.app_version }}"
Write-Host "Building with Configuration=$env:BUILD_CONFIGURATION Platform=$env:BUILD_PLATFORM PlatformToolset=$env:MSVC_PLATFORM_TOOLSET"
Write-Host "Build metadata: repo=$repoUrl branch=$branch commit=$commit version=$buildVersion"
msbuild src\BinXray\BinXray.vcxproj /t:Build /m `
/p:Configuration=$env:BUILD_CONFIGURATION `
/p:Platform=$env:BUILD_PLATFORM `
/p:PlatformToolset=$env:MSVC_PLATFORM_TOOLSET `
/p:BxrBuildRepoUrl="$repoUrl" `
/p:BxrBuildBranch="$branch" `
/p:BxrBuildCommit="$commit" `
/p:BxrBuildVersion="$buildVersion" `
/p:SolutionDir="$solutionDir" `
/p:IntDir="$env:GITHUB_WORKSPACE\build\obj\" `
/p:OutDir="$env:GITHUB_WORKSPACE\build\bin\"
# Packaging script supports both unsigned and signed modes.
# IMPORTANT signing-order constraint (handled in packaging/build-packages.ps1):
# sign app EXE -> build MSI -> sign MSI -> build bundle -> sign bundle
# Do not move MSI signing after bundle creation.
- name: Build MSI and Setup EXE Packages
shell: pwsh
run: |
if ($env:ENABLE_CODESIGN -eq "true") {
Write-Host "Packaging with optional Authenticode signing enabled."
.\packaging\build-packages.ps1 `
-AppExePath "$env:GITHUB_WORKSPACE\build\bin\BinXray.exe" `
-Version "${{ steps.version.outputs.app_version }}" `
-RequiredWixVersion "$env:WIX_VERSION" `
-OutputDir "artifacts\release" `
-SignArtifacts `
-SignToolPath "$env:SIGNTOOL_EXE" `
-CodeSignPfxPath "$env:CODE_SIGN_PFX" `
-CodeSignPfxPassword "$env:CODE_SIGN_PWD" `
-TimestampUrl "$env:CODE_SIGN_TIMESTAMP_URL"
}
else {
.\packaging\build-packages.ps1 `
-AppExePath "$env:GITHUB_WORKSPACE\build\bin\BinXray.exe" `
-Version "${{ steps.version.outputs.app_version }}" `
-RequiredWixVersion "$env:WIX_VERSION" `
-OutputDir "artifacts\release"
}
# Verification is optional and only meaningful in signed mode.
# Keep this step after packaging and before upload/publish so failures block releases.
- name: Verify Authenticode Signatures (Optional)
shell: pwsh
run: |
if ($env:ENABLE_CODESIGN -ne "true") {
Write-Host "Skipping signature verification (no signing certificate configured)."
return
}
$ErrorActionPreference = "Stop"
$targets = @(
"$env:GITHUB_WORKSPACE\build\bin\BinXray.exe",
"$env:GITHUB_WORKSPACE\artifacts\release\*.exe",
"$env:GITHUB_WORKSPACE\artifacts\release\*.msi"
)
foreach ($pattern in $targets) {
Get-ChildItem $pattern -ErrorAction SilentlyContinue | ForEach-Object {
$sig = Get-AuthenticodeSignature $_.FullName
$subject = if ($sig.SignerCertificate) { $sig.SignerCertificate.Subject } else { "<none>" }
Write-Host "$($_.Name): $($sig.Status) / $subject"
if ($sig.Status -ne "Valid") {
throw "Invalid signature on '$($_.FullName)': $($sig.Status)"
}
}
}
# Upload is unchanged for signed/unsigned mode. Artifact filenames remain stable.
- name: Upload Packaging Artifacts
uses: actions/upload-artifact@v4
with:
name: binxray-packages-${{ steps.version.outputs.app_version }}
path: artifacts/release/*
# Tag pushes publish to GitHub Releases. workflow_dispatch builds artifacts only unless the
# run is manually attached/published through another process.
- name: Publish GitHub Release Assets
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/release/*.exe
artifacts/release/*.msi