Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 311 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
name: CapFrameX CI

on:
push:


permissions:
contents: read

jobs:
build:
runs-on: windows-latest

env:
CONFIGURATION: Release
PLATFORM: x64
VS_VERSION: 18.0
AZURE_STORAGE_CONTAINER: builds
REF_NAME: ${{ github.head_ref || github.ref_name }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x

- name: Setup NuGet
uses: nuget/setup-nuget@v2

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
with:
vs-version: '[18.0, 19.0)'

- name: Install Visual Studio Native Desktop + MFC
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
Write-Host "Starting VS native dependency setup..."

$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$vsInstaller = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vs_installer.exe"

Write-Host "vswhere path: $vswhere"
Write-Host "vs_installer path: $vsInstaller"

if (-not (Test-Path $vswhere)) {
throw "vswhere.exe not found at $vswhere"
}
if (-not (Test-Path $vsInstaller)) {
throw "vs_installer.exe not found at $vsInstaller"
}

$instanceJson = & $vswhere -latest -products * -version "[18.0, 19.0)" -requires Microsoft.Component.MSBuild -format json
$instance = ($instanceJson | ConvertFrom-Json | Select-Object -First 1)
if ($null -eq $instance) {
throw "No Visual Studio installation found on runner."
}

$installPath = $instance.installationPath
$productId = $instance.productId

if ([string]::IsNullOrWhiteSpace($installPath) -or [string]::IsNullOrWhiteSpace($productId)) {
throw "Could not resolve Visual Studio installPath/productId."
}

Write-Host "Visual Studio installPath: $installPath"
Write-Host "Visual Studio productId: $productId"
"VS_INSTALL_PATH=$installPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

$beforeMfcInstance = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.ATLMFC -property installationPath
if ([string]::IsNullOrWhiteSpace($beforeMfcInstance)) {
Write-Host "MFC component not detected before install."
} else {
Write-Host "MFC component already detected before install at: $beforeMfcInstance"
}

$net472TargetingPack = Join-Path "${env:ProgramFiles(x86)}" "Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\mscorlib.dll"
$needsNet472Install = -not (Test-Path $net472TargetingPack)
if ($needsNet472Install) {
Write-Host ".NET Framework 4.7.2 targeting pack not found at: $net472TargetingPack"
} else {
Write-Host ".NET Framework 4.7.2 targeting pack found at: $net472TargetingPack"
}

$netFxRelease = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -ErrorAction SilentlyContinue).Release
if ($null -eq $netFxRelease) {
Write-Host ".NET Framework v4 Full Release key not found in registry."
} else {
Write-Host ".NET Framework v4 Full Release key: $netFxRelease"
}

$atlmfcDirsBefore = Get-ChildItem -Path (Join-Path $installPath "VC\Tools\MSVC") -Directory -ErrorAction SilentlyContinue |
ForEach-Object { Join-Path $_.FullName "atlmfc\lib\x64" } |
Where-Object { Test-Path $_ }
$mfcLibsBefore = @()
if ($atlmfcDirsBefore) {
$mfcLibsBefore = @(Get-ChildItem -Path $atlmfcDirsBefore -Filter "mfc*.lib" -ErrorAction SilentlyContinue)
}

$needsInstall = [string]::IsNullOrWhiteSpace($beforeMfcInstance) -or ($mfcLibsBefore.Count -eq 0) -or $needsNet472Install
if ($needsInstall) {
$installArgs = @(
"modify",
"--installPath", "$installPath",
"--add", "Microsoft.VisualStudio.Workload.NativeDesktop",
"--add", "Microsoft.VisualStudio.Component.VC.ATLMFC",
"--add", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"--add", "Microsoft.Net.Component.4.7.2.TargetingPack",
"--add", "Microsoft.Net.Component.4.7.2.SDK",
"--passive",
"--norestart",
"--wait"
)

Write-Host "Running vs_installer with args: $($installArgs -join ' ')"
$proc = Start-Process -FilePath $vsInstaller -ArgumentList $installArgs -Wait -PassThru -NoNewWindow
Write-Host "vs_installer exit code: $($proc.ExitCode)"

if (($proc.ExitCode -ne 0) -and ($proc.ExitCode -ne 3010)) {
throw "Failed to install Native Desktop/MFC components. Exit code: $($proc.ExitCode)"
}
} else {
Write-Host "Skipping vs_installer modify because MFC libraries are already present."
}

$afterMfcInstance = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.ATLMFC -property installationPath
if ([string]::IsNullOrWhiteSpace($afterMfcInstance)) {
throw "MFC component is still not detected by vswhere after installation."
}

Write-Host "MFC component detected after install at: $afterMfcInstance"

$atlmfcDirs = Get-ChildItem -Path (Join-Path $installPath "VC\Tools\MSVC") -Directory -ErrorAction SilentlyContinue |
ForEach-Object { Join-Path $_.FullName "atlmfc\lib\x64" } |
Where-Object { Test-Path $_ }

if (-not $atlmfcDirs) {
throw "No atlmfc\\lib\\x64 directories found under $installPath\\VC\\Tools\\MSVC"
}

Write-Host "Detected ATL/MFC library directories:"
$atlmfcDirs | ForEach-Object { Write-Host " - $_" }

$mfcLibs = Get-ChildItem -Path $atlmfcDirs -Filter "mfc*.lib" -ErrorAction SilentlyContinue
if (-not $mfcLibs) {
throw "No mfc*.lib files found in ATL/MFC x64 library directories."
}

Write-Host "Detected MFC library files (sample):"
$mfcLibs | Select-Object -First 10 | ForEach-Object { Write-Host " - $($_.FullName)" }

if (-not (Test-Path $net472TargetingPack)) {
throw ".NET Framework 4.7.2 targeting pack is missing after installation: $net472TargetingPack"
}

$netFxReleaseAfter = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -ErrorAction SilentlyContinue).Release
if (($null -eq $netFxReleaseAfter) -or ($netFxReleaseAfter -lt 461808)) {
throw ".NET Framework runtime 4.7.2 or newer is not installed (Release key: $netFxReleaseAfter)."
}

Write-Host ".NET Framework 4.7.2+ runtime and targeting pack are available."

- name: Install WiX Toolset v3.14.1
shell: pwsh
run: |
$wixInstaller = Join-Path $env:RUNNER_TEMP "wix314.exe"
Invoke-WebRequest `
-Uri "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe" `
-OutFile $wixInstaller

Start-Process `
-FilePath $wixInstaller `
-ArgumentList "/quiet", "/norestart" `
-Wait

$wixRoot = "${env:ProgramFiles(x86)}\WiX Toolset v3.14"
$wixBin = Join-Path $wixRoot "bin"
if (-not (Test-Path $wixBin)) {
throw "WiX Toolset v3.14.1 installation failed; expected path not found: $wixBin"
}

if (-not (Test-Path (Join-Path $wixBin "heat.exe"))) {
throw "WiX heat.exe not found in: $wixBin"
}

"WIX=$wixRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
$wixBin | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Restore
shell: cmd
run: |
nuget restore CapFrameX.sln
nuget restore -SolutionDirectory "%GITHUB_WORKSPACE%" source\CapFrameX.CustomInstallerActions\CapFrameX.CustomInstallerActions.csproj

- name: Build CX
shell: cmd
run: |
msbuild source\CapFrameX\CapFrameX.csproj /p:Configuration=%CONFIGURATION% /p:Platform=%PLATFORM% /p:DeployOnBuild=true /p:VisualStudioVersion=%VS_VERSION%

- name: Build HWInfo
shell: cmd
run: |
msbuild source\CapFrameX.Hwinfo\CapFrameX.Hwinfo.vcxproj /p:SolutionDir="%GITHUB_WORKSPACE%\\" /p:Configuration=%CONFIGURATION% /p:Platform=%PLATFORM% /p:DeployOnBuild=true /p:VisualStudioVersion=%VS_VERSION%

- name: Build IGCL
shell: cmd
run: |
msbuild source\CapFrameX.IGCL\CapFrameX.IGCL.vcxproj /p:SolutionDir="%GITHUB_WORKSPACE%\\" /p:Configuration=%CONFIGURATION% /p:Platform=%PLATFORM% /p:DeployOnBuild=true /p:VisualStudioVersion=%VS_VERSION%

- name: Build ADLX
shell: cmd
run: |
msbuild source\CapFrameX.ADLX\CapFrameX.ADLX.vcxproj /p:SolutionDir="%GITHUB_WORKSPACE%\\" /p:Configuration=%CONFIGURATION% /p:Platform=%PLATFORM% /p:DeployOnBuild=true /p:VisualStudioVersion=%VS_VERSION%

- name: Stage native DLLs
shell: cmd
run: |
copy /y source\CapFrameX\bin\x64\Release\CapFrameX.Hwinfo.dll source\CapFrameX\bin\x64\Release\net9.0-windows\
copy /y source\CapFrameX\bin\x64\Release\CapFrameX.IGCL.dll source\CapFrameX\bin\x64\Release\net9.0-windows\
copy /y source\CapFrameX\bin\x64\Release\CapFrameX.ADLX.dll source\CapFrameX\bin\x64\Release\net9.0-windows\

- name: Build Installer
shell: cmd
run: |
msbuild source\CapFrameXInstaller\CapFrameXInstaller.wixproj /p:SolutionDir="%GITHUB_WORKSPACE%\\" /p:Configuration=%CONFIGURATION% /p:Platform=%PLATFORM% /p:DeployOnBuild=true /p:VisualStudioVersion=%VS_VERSION%

- name: Build Bootstrapper
shell: cmd
run: |
msbuild source\CapFrameXBootstrapper\CapFrameXBootstrapper.wixproj /p:SolutionDir="%GITHUB_WORKSPACE%\\" /p:Configuration=%CONFIGURATION% /p:Platform=%PLATFORM% /p:DeployOnBuild=true /p:VisualStudioVersion=%VS_VERSION%

- name: Prepare portable config
shell: cmd
run: |
copy portable.json.sample source\CapFrameX\bin\x64\Release\net9.0-windows\portable.json

- name: Compute archive metadata
id: vars
shell: pwsh
run: |
if ("${{ github.ref }}".StartsWith("refs/tags/v")) {
$filename = "${{ github.ref_name }}"
} else {
$filename = "${{ github.sha }}"
}

"filename=$filename" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"branch=$env:REF_NAME" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8

- name: Create archives
shell: pwsh
run: |
$filename = "${{ steps.vars.outputs.filename }}"

Compress-Archive `
-Path "source\CapFrameXBootstrapper\bin\x64\Release\CapFrameXBootstrapper.exe" `
-DestinationPath "${filename}_installer.zip" `
-Force

Compress-Archive `
-Path "source\CapFrameX\bin\x64\Release\net9.0-windows\*" `
-DestinationPath "${filename}_portable.zip" `
-Force

- name: Upload workflow artifacts
uses: actions/upload-artifact@v4
with:
name: capframex-${{ steps.vars.outputs.filename }}
path: |
*.zip

publish:
runs-on: windows-latest
needs: build

env:
AZURE_STORAGE_CONTAINER: builds
REF_NAME: ${{ github.head_ref || github.ref_name }}

steps:
- name: Download workflow artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Upload archives to Azure Blob Storage
shell: pwsh
env:
AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }}
AZURE_STORAGE_KEY: ${{ secrets.AZURE_STORAGE_KEY }}
run: |
if ([string]::IsNullOrWhiteSpace($env:AZURE_STORAGE_ACCOUNT) -or [string]::IsNullOrWhiteSpace($env:AZURE_STORAGE_KEY)) {
Write-Host "Skipping Azure Blob upload because AZURE_STORAGE_ACCOUNT or AZURE_STORAGE_KEY is not set."
exit 0
}

if (-not (Get-Command az -ErrorAction SilentlyContinue)) {
throw "Azure CLI is not available on this runner."
}

az storage blob upload-batch `
--account-name "$env:AZURE_STORAGE_ACCOUNT" `
--destination "$env:AZURE_STORAGE_CONTAINER" `
--source "artifacts" `
--pattern "*.zip" `
--destination-path "$env:REF_NAME/${{ github.run_number }}" `
--account-key "$env:AZURE_STORAGE_KEY"
Loading