| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- name: Windows Build (tags only)
- on:
- push:
- tags:
- - 'v*' # run only on tags like v1.2.3
- permissions:
- contents: write
- jobs:
- build-windows:
- runs-on: windows-latest
- env:
- BUILD_TYPE: Release
- APP_NAME: standard_of_iron
- APP_DIR: build\bin
- QML_DIR: ui\qml
- QT_VERSION: 6.8.2
- QT_ARCH: win64_msvc2022_64
- QT_ROOT: D:\a\Standard-of-Iron\Qt
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v5
- with:
- python-version: '3.12'
- - uses: ilammy/msvc-dev-cmd@v1
- # Fast path: official installer (allowed to fail)
- - name: Install Qt (fast path)
- id: fastqt
- continue-on-error: true
- uses: jurplel/install-qt-action@v4
- with:
- version: ${{ env.QT_VERSION }}
- host: windows
- arch: ${{ env.QT_ARCH }}
- cache: true
- aqtversion: '==3.3.0'
- modules: 'qt5compat qtmultimedia'
- tools-only: false
- # Fallback: install with aqt directly if fast path fails
- - name: Install Qt (fallback via aqt)
- if: ${{ steps.fastqt.outcome != 'success' }}
- shell: pwsh
- run: |
- python -m pip install --upgrade pip
- python -m pip install "aqtinstall==3.3.0" "py7zr==1.0.*"
- $qtDir = Join-Path $env:QT_ROOT "$env:QT_VERSION\$env:QT_ARCH"
- New-Item -ItemType Directory -Force -Path $qtDir | Out-Null
- try {
- python -m aqt install-qt windows desktop $env:QT_VERSION $env:QT_ARCH `
- --outputdir "$env:QT_ROOT" `
- -m qtmultimedia qt5compat qtdeclarative
- } catch {
- Write-Warning "Explicit module install failed. Falling back to '-m all'. Error: $($_.Exception.Message)"
- python -m aqt install-qt windows desktop $env:QT_VERSION $env:QT_ARCH `
- --outputdir "$env:QT_ROOT" -m all
- }
- if (!(Test-Path "$qtDir\lib\cmake\Qt6\Qt6Config.cmake")) {
- Write-Error "Qt6Config.cmake not found in $qtDir after fallback install."
- exit 1
- }
- # Export env and PATH
- echo "Qt6_DIR=$qtDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- echo "$qtDir\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- # Normalize env for both paths
- - name: Normalize Qt env
- shell: pwsh
- run: |
- $candidates = @()
- if ($env:Qt6_DIR) { $candidates += $env:Qt6_DIR }
- if ($env:QT_ROOT_DIR) { $candidates += $env:QT_ROOT_DIR }
- $candidates += (Join-Path $env:QT_ROOT "$env:QT_VERSION\$env:QT_ARCH")
- $qtDir = $null
- foreach ($c in $candidates | Select-Object -Unique) {
- if ($c -and (Test-Path (Join-Path $c 'lib\cmake\Qt6\Qt6Config.cmake'))) {
- $qtDir = $c
- break
- }
- }
- if (-not $qtDir) {
- Write-Error "Could not locate Qt6. Checked: $($candidates -join ', ')"
- exit 1
- }
- echo "Qt6_DIR=$qtDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- echo "$qtDir\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- Write-Host "Qt6_DIR=$qtDir"
- - name: Verify Qt installation
- shell: pwsh
- run: |
- Write-Host "Qt6_DIR=$env:Qt6_DIR"
- if (!(Test-Path "$env:Qt6_DIR\lib\cmake\Qt6\Qt6Config.cmake")) {
- Write-Error "Qt6Config.cmake not found in $env:Qt6_DIR"
- exit 1
- }
- if (!(Get-Command windeployqt -ErrorAction SilentlyContinue)) {
- Write-Error "windeployqt not found on PATH"
- exit 1
- }
- - name: Configure (CMake + Ninja)
- shell: pwsh
- run: |
- cmake -S . -B build -G "Ninja" `
- -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} `
- -DDEFAULT_LANG=en `
- -DCMAKE_PREFIX_PATH="$env:Qt6_DIR"
- - name: Build
- run: cmake --build build
- # Code signing (optional: signs .exe with Authenticode)
- - name: Sign executable
- if: ${{ secrets.WINDOWS_CERTIFICATE != '' && secrets.WINDOWS_CERTIFICATE_PASSWORD != '' }}
- shell: pwsh
- env:
- CERT_DATA: ${{ secrets.WINDOWS_CERTIFICATE }}
- CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
- run: |
- # Validate that the executable exists
- $exePath = "$env:APP_DIR\${{ env.APP_NAME }}.exe"
- if (!(Test-Path $exePath)) {
- Write-Error "Executable not found at: $exePath"
- exit 1
- }
-
- # Decode certificate from base64 secret and save to temp file
- $certPath = "$env:TEMP\cert.pfx"
- $certBytes = [System.Convert]::FromBase64String($env:CERT_DATA)
- [System.IO.File]::WriteAllBytes($certPath, $certBytes)
-
- try {
- # Find signtool.exe - check multiple potential locations
- $searchPaths = @(
- "$env:ProgramFiles(x86)\Windows Kits",
- "$env:ProgramFiles\Windows Kits"
- )
-
- $signtool = $null
- foreach ($searchPath in $searchPaths) {
- if (Test-Path $searchPath) {
- $signtool = Get-ChildItem $searchPath -Recurse -Filter "signtool.exe" -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match "x64" } |
- Select-Object -First 1
- if ($signtool) { break }
- }
- }
-
- if (-not $signtool) {
- Write-Error "signtool.exe not found in Windows Kits directories"
- exit 1
- }
-
- Write-Host "Using signtool: $($signtool.FullName)"
-
- # Prepare signing arguments - use environment variable for password
- $signArgs = @(
- "sign",
- "/f", $certPath,
- "/p", $env:CERT_PASSWORD,
- "/tr", "http://timestamp.digicert.com",
- "/td", "SHA256",
- "/fd", "SHA256",
- "/v",
- $exePath
- )
-
- Write-Host "Signing $exePath..."
- & $signtool.FullName $signArgs
-
- if ($LASTEXITCODE -ne 0) {
- Write-Error "Code signing failed with exit code $LASTEXITCODE"
- exit 1
- }
-
- Write-Host "Successfully signed $exePath"
-
- # Verify the signature
- Write-Host "Verifying signature..."
- & $signtool.FullName verify /pa /v $exePath
-
- if ($LASTEXITCODE -ne 0) {
- Write-Error "Signature verification failed with exit code $LASTEXITCODE"
- exit 1
- }
-
- Write-Host "Signature verification successful"
-
- } finally {
- # Clean up certificate file
- if (Test-Path $certPath) {
- Remove-Item $certPath -Force
- }
- }
- # Deploy Qt DLLs (dynamic linking ensures LGPL v3 compliance)
- - name: Deploy Qt
- shell: pwsh
- run: |
- $mode = if ("${{ env.BUILD_TYPE }}" -eq "Debug") { "--debug" } else { "--release" }
- windeployqt $mode --compiler-runtime --qmldir "$env:QML_DIR" "$env:APP_DIR\${{ env.APP_NAME }}.exe"
- # SAFE: write qt.conf
- - name: Write qt.conf
- shell: pwsh
- run: |
- @(
- '[Paths]'
- 'Plugins = .'
- 'Imports = qml'
- 'Qml2Imports = qml'
- 'Translations = translations'
- ) | Set-Content -Encoding ASCII "$env:APP_DIR\qt.conf"
- # Copy Windows launcher and debug scripts from dist/windows/
- - name: Copy launcher and debug scripts
- shell: pwsh
- run: |
- Write-Host "Copying Windows launcher and debug scripts..."
- $scripts = @(
- "LAUNCH.bat",
- "run_debug.cmd",
- "run_debug_angle.cmd",
- "run_debug_softwaregl.cmd"
- )
-
- foreach ($script in $scripts) {
- $src = "dist\windows\$script"
- $dst = "$env:APP_DIR\$script"
-
- if (!(Test-Path $src)) {
- Write-Error "Source script not found: $src"
- exit 1
- }
-
- Copy-Item $src $dst -Force
- Write-Host "✓ Copied $script"
- }
-
- Write-Host "All launcher scripts deployed successfully."
- # Add comprehensive README for users
- - name: Add README.txt for distribution
- shell: pwsh
- run: |
- @(
- '==============================================================================='
- ' STANDARD OF IRON'
- ' Windows Distribution'
- '==============================================================================='
- ''
- 'QUICK START'
- '-----------'
- '1. Double-click "LAUNCH.bat" to start the game'
- ' OR'
- '2. Double-click "standard_of_iron.exe" directly'
- ''
- 'If the game does not start, see TROUBLESHOOTING below.'
- ''
- ''
- 'TROUBLESHOOTING'
- '---------------'
- 'If the game does not appear or crashes on startup, try these steps in order:'
- ''
- '1. Run "run_debug.cmd"'
- ' - This will start the game with diagnostic logging'
- ' - Look for error messages in the console window'
- ' - A log file "runlog.txt" will be created'
- ''
- '2. Run "run_debug_angle.cmd" (RECOMMENDED for compatibility)'
- ' - Uses ANGLE rendering (OpenGL via DirectX)'
- ' - Works on most systems, even with outdated GPU drivers'
- ' - Creates "runlog_angle.txt" log file'
- ''
- '3. Run "run_debug_softwaregl.cmd" (FALLBACK - works everywhere)'
- ' - Uses CPU-based software rendering'
- ' - Guaranteed to work but with reduced performance'
- ' - Creates "runlog_softwaregl.txt" log file'
- ''
- ''
- 'COMMON ISSUES'
- '-------------'
- 'Q: I double-click the EXE and nothing happens'
- 'A: Your system may lack proper graphics drivers. Run "run_debug_softwaregl.cmd"'
- ''
- 'Q: The game crashes immediately'
- 'A: Try "run_debug_angle.cmd" for better GPU driver compatibility'
- ''
- 'Q: I see OpenGL errors in the log'
- 'A: Run "run_debug_softwaregl.cmd" to bypass GPU drivers entirely'
- ''
- 'Q: Performance is slow'
- 'A: Make sure you are NOT using "run_debug_softwaregl.cmd"'
- ' Try "run_debug.cmd" or "run_debug_angle.cmd" instead'
- ''
- ''
- 'GRAPHICS MODES EXPLAINED'
- '------------------------'
- 'Desktop OpenGL (run_debug.cmd):'
- ' - Best performance'
- ' - Requires proper GPU drivers'
- ' - Use this if your GPU drivers are up to date'
- ''
- 'ANGLE (run_debug_angle.cmd):'
- ' - Good performance'
- ' - Compatible with most systems'
- ' - Translates OpenGL to DirectX'
- ' - RECOMMENDED for older or problematic GPUs'
- ''
- 'Software OpenGL (run_debug_softwaregl.cmd):'
- ' - CPU-based rendering'
- ' - Works on ALL systems'
- ' - Reduced performance'
- ' - Use as last resort'
- ''
- ''
- 'LOG FILES'
- '---------'
- 'When using the debug scripts, log files are created:'
- ' - runlog.txt (Desktop OpenGL mode)'
- ' - runlog_angle.txt (ANGLE mode)'
- ' - runlog_softwaregl.txt (Software OpenGL mode)'
- ''
- 'These files contain diagnostic information to help identify problems.'
- 'Include these files when reporting issues.'
- ''
- ''
- 'SYSTEM REQUIREMENTS'
- '-------------------'
- 'Minimum:'
- ' - Windows 10 or later'
- ' - 4 GB RAM'
- ' - Any GPU (software rendering supported)'
- ''
- 'Recommended:'
- ' - Windows 10/11'
- ' - 8 GB RAM'
- ' - GPU with OpenGL 3.3+ support'
- ' - Updated graphics drivers'
- ''
- ''
- 'UPDATING GRAPHICS DRIVERS'
- '-------------------------'
- 'For best performance, update your graphics drivers:'
- ''
- 'NVIDIA: https://www.nvidia.com/Download/index.aspx'
- 'AMD: https://www.amd.com/en/support'
- 'Intel: https://www.intel.com/content/www/us/en/download-center/home.html'
- ''
- ''
- 'REPORTING ISSUES'
- '----------------'
- 'If you encounter problems:'
- ''
- '1. Run one of the debug scripts (run_debug.cmd or run_debug_angle.cmd)'
- '2. Save the generated log file (runlog.txt or runlog_angle.txt)'
- '3. Report the issue at: https://github.com/djeada/Standard-of-Iron/issues'
- '4. Include the log file and your system information:'
- ' - Windows version'
- ' - GPU model'
- ' - Which startup method you tried'
- ''
- ''
- 'MORE INFORMATION'
- '----------------'
- 'Website: https://github.com/djeada/Standard-of-Iron'
- 'License: See LICENSE file (MIT License)'
- ''
- 'This software uses Qt Framework (LGPL v3)'
- 'See THIRD_PARTY_LICENSES.md for details'
- ''
- '==============================================================================='
- ) | Set-Content -Encoding ASCII "$env:APP_DIR\README.txt"
- - name: Add GL/ANGLE fallbacks (mandatory)
- shell: pwsh
- run: |
- $qtbin = Join-Path $env:Qt6_DIR "bin"
- $required = @("d3dcompiler_47.dll","opengl32sw.dll","libEGL.dll","libGLESv2.dll")
- $missing = @()
-
- foreach ($f in $required) {
- $src = Join-Path $qtbin $f
- if (Test-Path $src) {
- Copy-Item $src "$env:APP_DIR" -Force
- Write-Host "✓ Copied $f"
- } else {
- Write-Error "✗ Missing required fallback DLL: $f"
- $missing += $f
- }
- }
-
- if ($missing.Count -gt 0) {
- Write-Error "CRITICAL: Missing graphics fallback DLLs: $($missing -join ', ')"
- Write-Error "These DLLs are required for compatibility on systems without proper GPU drivers."
- Write-Error "Without them, the app will fail silently on many user machines."
- exit 1
- }
-
- Write-Host "All graphics fallback DLLs successfully deployed."
- - name: Copy assets
- shell: pwsh
- run: |
- if (!(Test-Path "$env:APP_DIR\assets")) {
- New-Item -ItemType Directory -Path "$env:APP_DIR\assets" | Out-Null
- }
- robocopy assets "$env:APP_DIR\assets" /E /NFL /NDL /NJH /NJS /nc /ns /np
- if ($LASTEXITCODE -ge 8) { exit $LASTEXITCODE } else { exit 0 }
- - name: Sanity check deployed files
- shell: pwsh
- run: |
- Write-Host "=== Verifying Runtime Completeness ==="
-
- # Critical Qt runtime files
- $req = @(
- "$env:APP_DIR\platforms\qwindows.dll",
- "$env:APP_DIR\Qt6Core.dll",
- "$env:APP_DIR\Qt6Gui.dll",
- "$env:APP_DIR\Qt6Qml.dll"
- )
-
- # Graphics fallback DLLs (now mandatory)
- $graphics_fallbacks = @(
- "$env:APP_DIR\d3dcompiler_47.dll",
- "$env:APP_DIR\opengl32sw.dll",
- "$env:APP_DIR\libEGL.dll",
- "$env:APP_DIR\libGLESv2.dll"
- )
-
- $missing = @()
-
- Write-Host "`nChecking Qt runtime DLLs..."
- foreach ($f in $req) {
- if (!(Test-Path $f)) {
- $missing += $f
- Write-Error "✗ Missing: $f"
- } else {
- Write-Host "✓ Found: $(Split-Path -Leaf $f)"
- }
- }
-
- Write-Host "`nChecking graphics fallback DLLs..."
- foreach ($f in $graphics_fallbacks) {
- if (!(Test-Path $f)) {
- $missing += $f
- Write-Error "✗ Missing: $f"
- } else {
- Write-Host "✓ Found: $(Split-Path -Leaf $f)"
- }
- }
-
- if ($missing.Count -gt 0) {
- Write-Error "`nDEPLOYMENT FAILED - Missing files:`n$($missing -join "`n")"
- exit 1
- }
-
- Write-Host "`n✓ All required runtime files are present."
- Write-Host "Deployment is complete and ready for distribution."
- - name: Dependency check (dumpbin)
- shell: pwsh
- run: |
- Write-Host "Checking EXE dependencies with dumpbin..."
-
- # Find dumpbin.exe in Visual Studio installation
- $dumpbin = Get-ChildItem "C:\Program Files\Microsoft Visual Studio" -Recurse -Filter "dumpbin.exe" -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match "Hostx64\\x64" } |
- Select-Object -First 1
-
- if (-not $dumpbin) {
- Write-Warning "dumpbin.exe not found, skipping dependency check"
- exit 0
- }
-
- Write-Host "Using dumpbin: $($dumpbin.FullName)"
-
- # Get dependencies
- $exePath = "$env:APP_DIR\${{ env.APP_NAME }}.exe"
- $output = & $dumpbin.FullName /DEPENDENTS $exePath 2>&1 | Out-String
-
- Write-Host "Dependencies:"
- Write-Host $output
-
- # Parse DLL names from dumpbin output
- $dllPattern = '^\s+([a-zA-Z0-9_\-\.]+\.dll)\s*$'
- $dependencies = @()
- foreach ($line in $output -split "`n") {
- if ($line -match $dllPattern) {
- $dependencies += $matches[1]
- }
- }
-
- # Known system DLLs that don't need to be bundled
- # These include Windows API sets (api-ms-win-*) and core system DLLs
- $knownSystemDlls = @(
- 'KERNEL32.dll',
- 'USER32.dll',
- 'ADVAPI32.dll',
- 'SHELL32.dll',
- 'ole32.dll',
- 'OLEAUT32.dll',
- 'OPENGL32.dll',
- 'GDI32.dll',
- 'WS2_32.dll',
- 'NETAPI32.dll'
- )
-
- # Check if each dependency exists in APP_DIR or is a known system DLL
- $systemDirs = @(
- "$env:SystemRoot\System32",
- "$env:SystemRoot\SysWOW64"
- )
-
- $missingDeps = @()
- foreach ($dll in $dependencies) {
- # Skip Windows API set DLLs (api-ms-win-*, ext-ms-*)
- if ($dll -match '^(api-ms-win-|ext-ms-)') {
- Write-Host "Skipping Windows API set: $dll"
- continue
- }
-
- # Skip known system DLLs
- if ($knownSystemDlls -contains $dll) {
- Write-Host "Skipping known system DLL: $dll"
- continue
- }
-
- $foundInApp = Test-Path (Join-Path $env:APP_DIR $dll)
- $foundInSystem = $false
- foreach ($sysDir in $systemDirs) {
- if (Test-Path (Join-Path $sysDir $dll)) {
- $foundInSystem = $true
- break
- }
- }
-
- if (-not $foundInApp -and -not $foundInSystem) {
- $missingDeps += $dll
- Write-Warning "Missing dependency: $dll"
- } else {
- $location = if ($foundInApp) { "app directory" } else { "system directory" }
- Write-Host "Found $dll in $location"
- }
- }
-
- if ($missingDeps.Count -gt 0) {
- Write-Error "Missing DLL dependencies: $($missingDeps -join ', ')"
- exit 1
- }
-
- Write-Host "All required dependencies are present"
- - name: Smoke-run (software GL, capture exit)
- shell: pwsh
- run: |
- $ErrorActionPreference = 'Stop'
-
- # Exit code constants for better readability
- $EXIT_ACCESS_VIOLATION = -1073741819 # 0xC0000005 - Access violation (common OpenGL crash)
- $EXIT_ORDINAL_NOT_FOUND = -1073741510 # 0xC000007A - Ordinal not found
- $EXIT_DLL_NOT_FOUND = -1073741701 # 0xC0000135 - DLL not found
- $EXIT_APPINIT_FAILURE = -1073741515 # 0xC0000139 - Entry point not found
-
- $env:QT_DEBUG_PLUGINS = "1"
- $env:QT_LOGGING_RULES = "qt.*=true;qt.qml=true;qqml.*=true;qt.rhi.*=true"
- $env:QT_OPENGL = "software"
- $env:QT_QPA_PLATFORM = "windows"
-
- Write-Host "=== Smoke Test Configuration ==="
- Write-Host "QT_OPENGL: $env:QT_OPENGL"
- Write-Host "QT_QPA_PLATFORM: $env:QT_QPA_PLATFORM"
- Write-Host "================================"
-
- Push-Location "$env:APP_DIR"
- try {
- $logFile = "smoke_test.log"
- $errFile = "smoke_test_err.log"
-
- Write-Host "Starting application..."
- $process = Start-Process -FilePath ".\${{ env.APP_NAME }}.exe" `
- -RedirectStandardOutput $logFile `
- -RedirectStandardError $errFile `
- -PassThru -NoNewWindow
-
- $timeoutSeconds = 8
- $checkInterval = 1
- $elapsed = 0
- $exited = $false
- $successMarkers = @()
-
- Write-Host "Monitoring startup for $timeoutSeconds seconds..."
-
- while ($elapsed -lt $timeoutSeconds) {
- Start-Sleep -Seconds $checkInterval
- $elapsed += $checkInterval
-
- # Check if process exited
- if ($process.HasExited) {
- $exited = $true
- break
- }
-
- # Check logs for success markers
- if ((Test-Path $logFile)) {
- $content = Get-Content $logFile -Raw
- if ($content -match "QGuiApplication created successfully") {
- $successMarkers += "QGuiApplication initialized"
- }
- if ($content -match "GameEngine created") {
- $successMarkers += "GameEngine created"
- }
- if ($content -match "QML engine.*loading") {
- $successMarkers += "QML loading"
- }
- }
-
- Write-Host " [$elapsed/$timeoutSeconds s] Process running, found markers: $($successMarkers.Count)"
- }
- # Process still running after timeout - this is GOOD
- if (-not $exited) {
- Write-Host "`n✓ SUCCESS: Application is running after $timeoutSeconds seconds"
- Write-Host " Found initialization markers: $($successMarkers -join ', ')"
-
- # Kill process gracefully
- $process.Kill()
- $process.WaitForExit(3000)
-
- # Show relevant log excerpts
- if (Test-Path $logFile) {
- Write-Host "`n=== Application Output (last 30 lines) ==="
- Get-Content $logFile -Tail 30 | Write-Host
- }
-
- Write-Host "`n✓ Smoke test PASSED - application starts and runs correctly"
- exit 0
- }
- # Process exited early - investigate why
- $code = $process.ExitCode
- Write-Host "`n⚠ Process exited early with code: $code (after $elapsed seconds)"
-
- # Show logs to help diagnose
- if (Test-Path $logFile) {
- Write-Host "`n=== Application Output ==="
- Get-Content $logFile | Write-Host
- }
- if (Test-Path $errFile) {
- Write-Host "`n=== Error Output ==="
- Get-Content $errFile | Write-Host
- }
- # Categorize exit codes
- $expectedOnHeadless = @($EXIT_ACCESS_VIOLATION, $EXIT_ORDINAL_NOT_FOUND)
- $deploymentIssues = @($EXIT_DLL_NOT_FOUND, $EXIT_APPINIT_FAILURE)
- if ($deploymentIssues -contains $code) {
- Write-Error "✗ DEPLOYMENT ISSUE: Missing DLL or symbol (exit code $code)"
- Write-Error "This indicates incomplete deployment or missing dependencies"
- exit 1
- }
- if ($expectedOnHeadless -contains $code) {
- Write-Warning "⚠ Headless-expected exit code ($code)"
- Write-Warning "This is expected on CI without display. Checking for fatal errors..."
-
- # Check if we got far enough in initialization
- if ($successMarkers.Count -ge 2) {
- Write-Host "✓ Found $($successMarkers.Count) success markers before exit"
- Write-Host " This suggests the app initializes correctly on systems with displays"
- exit 0
- }
-
- Write-Warning "Only found $($successMarkers.Count) success markers"
- Write-Warning "Treating as PASS for headless CI, but verify on real systems"
- exit 0
- }
- if ($code -eq 0) {
- Write-Host "✓ App exited cleanly with code 0"
- if ($successMarkers.Count -ge 2) {
- Write-Host "✓ Found adequate success markers: $($successMarkers -join ', ')"
- exit 0
- }
- Write-Warning "App exited cleanly but found only $($successMarkers.Count) success markers"
- Write-Warning "This may indicate early/incomplete initialization"
- exit 0
- }
- Write-Error "✗ UNEXPECTED EXIT: Exit code $code with only $($successMarkers.Count) success markers"
- Write-Error "This suggests a real deployment or runtime issue"
- exit 1
-
- } catch {
- Write-Error "✗ Failed to start process: $_"
- exit 1
- } finally {
- Pop-Location
- }
- # Test ANGLE backend to ensure compatibility mode works
- - name: Verify ANGLE backend (compatibility test)
- shell: pwsh
- run: |
- Write-Host "=== ANGLE Backend Verification ==="
- Write-Host "Testing ANGLE (OpenGL ES via D3D11) for maximum compatibility"
-
- Push-Location "$env:APP_DIR"
- try {
- $env:QT_DEBUG_PLUGINS = "0"
- $env:QT_OPENGL = "angle"
- $env:QT_ANGLE_PLATFORM = "d3d11"
- $env:QT_QPA_PLATFORM = "windows"
-
- $logFile = "angle_test.log"
- $process = Start-Process -FilePath ".\${{ env.APP_NAME }}.exe" `
- -RedirectStandardOutput $logFile `
- -RedirectStandardError "${logFile}.err" `
- -PassThru -NoNewWindow
-
- # Give ANGLE mode 5 seconds to initialize
- Start-Sleep -Seconds 5
-
- if (-not $process.HasExited) {
- Write-Host "✓ ANGLE mode: Process running successfully"
- $process.Kill()
- $process.WaitForExit(2000)
- } else {
- $code = $process.ExitCode
- # ANGLE might fail on headless CI, that's OK
- Write-Warning "ANGLE mode exited with code: $code"
- Write-Warning "This is expected on headless CI without D3D11"
- }
-
- Write-Host "✓ ANGLE backend verification complete"
- } catch {
- Write-Warning "ANGLE test failed (expected on headless CI): $_"
- } finally {
- Pop-Location
- }
- # Final readiness check
- - name: Final runtime readiness verification
- shell: pwsh
- run: |
- Write-Host "=== Final Runtime Readiness Check ==="
- Write-Host ""
-
- $criticalFiles = @(
- "$env:APP_DIR\standard_of_iron.exe",
- "$env:APP_DIR\LAUNCH.bat",
- "$env:APP_DIR\README.txt",
- "$env:APP_DIR\run_debug.cmd",
- "$env:APP_DIR\run_debug_angle.cmd",
- "$env:APP_DIR\run_debug_softwaregl.cmd",
- "$env:APP_DIR\opengl32sw.dll",
- "$env:APP_DIR\libEGL.dll",
- "$env:APP_DIR\libGLESv2.dll",
- "$env:APP_DIR\d3dcompiler_47.dll"
- )
-
- $allPresent = $true
- foreach ($file in $criticalFiles) {
- if (Test-Path $file) {
- Write-Host "✓ $(Split-Path -Leaf $file)"
- } else {
- Write-Error "✗ MISSING: $(Split-Path -Leaf $file)"
- $allPresent = $false
- }
- }
-
- if (-not $allPresent) {
- Write-Error "Deployment is incomplete!"
- exit 1
- }
-
- Write-Host ""
- Write-Host "═══════════════════════════════════════════════════════"
- Write-Host " ✓ BUILD IS SELF-CONTAINED AND RUNTIME READY"
- Write-Host "═══════════════════════════════════════════════════════"
- Write-Host ""
- Write-Host "Included components:"
- Write-Host " • Main executable with fallback rendering"
- Write-Host " • User-friendly launcher (LAUNCH.bat)"
- Write-Host " • Comprehensive documentation (README.txt)"
- Write-Host " • Debug/troubleshooting scripts"
- Write-Host " • Software OpenGL fallback (CPU rendering)"
- Write-Host " • ANGLE fallback (OpenGL ES via DirectX)"
- Write-Host " • All required Qt and system libraries"
- Write-Host ""
- Write-Host "Users can:"
- Write-Host " 1. Double-click LAUNCH.bat for guided startup"
- Write-Host " 2. Use debug scripts if issues occur"
- Write-Host " 3. Fallback to software rendering if GPU fails"
- Write-Host " 4. Read README.txt for detailed troubleshooting"
- Write-Host ""
- - name: Zip
- shell: pwsh
- run: |
- $zip = "standard_of_iron-win-x64-${{ env.BUILD_TYPE }}.zip"
- if (Test-Path $zip) { Remove-Item $zip -Force }
- Compress-Archive -Path "$env:APP_DIR\*" -DestinationPath $zip -Force
- - uses: actions/upload-artifact@v4
- with:
- name: windows-${{ env.BUILD_TYPE }}
- path: standard_of_iron-win-x64-${{ env.BUILD_TYPE }}.zip
- - name: Attach to GitHub Release
- uses: softprops/action-gh-release@v2
- if: startsWith(github.ref, 'refs/tags/')
- with:
- files: standard_of_iron-win-x64-${{ env.BUILD_TYPE }}.zip
|