Skip to content

Use VS2026 runner for release build #6

Use VS2026 runner for release build

Use VS2026 runner for release build #6

Workflow file for this run

# (260405Ch) Build ReciProSetup on v* tag push and publish a GitHub Release using the latest Version.cs history line
name: Build And Release Installer
on:
push:
tags:
- "v*"
permissions:
contents: write
jobs:
release:
runs-on: windows-2025-vs2026 # (260405Ch) net10.0-windows + WinForms setup build needs Visual Studio 18 on GitHub-hosted runners.
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup .NET 10
uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"
- name: Restore managed dependencies
shell: pwsh
run: dotnet restore ReciPro\ReciPro.csproj
- name: Confirm installer project sources
shell: pwsh
run: |
# (260405Ch) Fail early with a clear message if installer project sources were not committed to GitHub.
$requiredSources = @(
"ReciProSetup\ReciProSetup.vdproj",
"ReciProSetup\EnableLaunchApplication.js",
"LICENSE.rtf",
"REQUIREMENT.rtf"
)
$missingSources = $requiredSources | Where-Object { -not (Test-Path $_) }
if ($missingSources.Count -gt 0) {
throw "Installer source files are missing from the checkout: $($missingSources -join ', ')"
}
- name: Read release info from Version.cs
id: version_info
shell: pwsh
run: |
$versionFile = "ReciPro\Version.cs"
$content = Get-Content $versionFile -Raw
$historyRegex = [regex]::Matches($content, 'ver[^\r\n"]+')
if ($historyRegex.Count -eq 0) {
throw "Could not find a release history line in $versionFile."
}
$latestHistory = $historyRegex[0].Value.Trim()
if ($latestHistory -notmatch '^ver(?<Version>[0-9][0-9A-Za-z\.\-_]*)') {
throw "Could not parse version number from history line: $latestHistory"
}
$historyTag = "v$($Matches.Version)"
$tagName = "${{ github.ref_name }}"
if ($historyTag -ne $tagName) {
throw "Tag '$tagName' does not match Version.cs latest history '$historyTag'."
}
$releaseTitle = "ReciPro $tagName"
$notesPath = Join-Path $env:RUNNER_TEMP "release-notes.txt"
@(
$latestHistory
""
"Built automatically from tag $tagName."
) | Set-Content -Path $notesPath -Encoding UTF8
"latest_history=$latestHistory" >> $env:GITHUB_OUTPUT
"release_title=$releaseTitle" >> $env:GITHUB_OUTPUT
"notes_path=$notesPath" >> $env:GITHUB_OUTPUT
- name: Disable out-of-proc setup project build
shell: pwsh
run: |
# (260405Ch) Force installer builds to run in-proc so devenv does not return before MSI generation completes.
$visualStudioRoot = "HKCU:\Software\Microsoft\VisualStudio"
$configKeys = Get-ChildItem $visualStudioRoot -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -like '17.0*_Config' -or $_.PSChildName -like '18.0*_Config' }
if ($configKeys.Count -eq 0) {
Write-Warning "No Visual Studio *_Config registry keys were found under $visualStudioRoot."
}
foreach ($configKey in $configKeys) {
$msbuildKey = Join-Path $configKey.PSPath "MSBuild"
if (-not (Test-Path $msbuildKey)) {
New-Item -Path $msbuildKey -Force | Out-Null
}
New-ItemProperty -Path $msbuildKey -Name DisableOutOfProcBuild -PropertyType DWord -Value 1 -Force | Out-Null
Write-Host "Set DisableOutOfProcBuild=1 at $msbuildKey"
}
- name: Build installer with Visual Studio
id: build_installer
shell: pwsh
run: |
# (260405Ch) Use devenv.com for command-line builds so the workflow waits for vdproj build completion and captures logs.
$devenvExe = (& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property productPath).Trim()
if (-not $devenvExe) {
throw "Visual Studio was not found."
}
$devenvCli = [System.IO.Path]::ChangeExtension($devenvExe, ".com")
if (-not (Test-Path $devenvCli)) {
throw "devenv.com was not found next to $devenvExe."
}
$buildLogPath = Join-Path $env:RUNNER_TEMP "devenv-build.log"
& $devenvCli "ReciPro.sln" /Build "Release|x64" /Project "ReciProSetup" /Out $buildLogPath
$buildExitCode = $LASTEXITCODE
Write-Host "devenv exit code: $buildExitCode"
if (Test-Path $buildLogPath) {
Get-Content $buildLogPath
}
"build_exit_code=$buildExitCode" >> $env:GITHUB_OUTPUT
"build_log_path=$buildLogPath" >> $env:GITHUB_OUTPUT
- name: Confirm installer outputs
id: installer_outputs
shell: pwsh
run: |
# (260405Ch) Some environments materialize setup project outputs slightly after devenv returns, so wait and locate them dynamically.
$requiredNames = @("ReciProSetup.msi")
$foundFiles = @{}
$deadline = (Get-Date).AddMinutes(2)
do {
Get-ChildItem -Path . -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $requiredNames -contains $_.Name } |
ForEach-Object { $foundFiles[$_.Name] = $_.FullName }
$missingFiles = $requiredNames | Where-Object { -not $foundFiles.ContainsKey($_) }
if ($missingFiles.Count -eq 0) {
break
}
Start-Sleep -Seconds 2
} while ((Get-Date) -lt $deadline)
$missingFiles = $requiredNames | Where-Object { -not $foundFiles.ContainsKey($_) }
if ($missingFiles.Count -gt 0) {
Write-Host "Located installer-like files:"
Get-ChildItem -Path . -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -eq ".msi" -or $_.Name -eq "setup.exe" } |
Select-Object FullName, Length, LastWriteTime
if (Test-Path "${{ steps.build_installer.outputs.build_log_path }}") {
Write-Host "devenv build log:"
Get-Content "${{ steps.build_installer.outputs.build_log_path }}"
}
throw "Required output was not found: $($missingFiles -join ', ')"
}
if ("${{ steps.build_installer.outputs.build_exit_code }}" -ne "0") {
Write-Warning "devenv returned exit code ${{ steps.build_installer.outputs.build_exit_code }}, but the MSI output was found. Continuing with the generated file."
}
"msi_path=$($foundFiles['ReciProSetup.msi'])" >> $env:GITHUB_OUTPUT
- name: Upload workflow artifacts
uses: actions/upload-artifact@v4
with:
name: ReciPro-installer-${{ github.ref_name }}
path: |
${{ steps.installer_outputs.outputs.msi_path }}
- name: Create GitHub Release
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ github.ref_name }}" `
"${{ steps.installer_outputs.outputs.msi_path }}" `
--verify-tag `
--title "${{ steps.version_info.outputs.release_title }}" `
--notes-file "${{ steps.version_info.outputs.notes_path }}"