Repository navigation
P3-A1: native ARStack Studio binary profile deployment and installer candidate #465
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: ARStack Studio Release | |
| on: | |
| workflow_dispatch: | |
| pull_request: | |
| paths: | |
| - 'apps/arstack_studio/**' | |
| - 'embedded/esp32p4_smv_injector/**' | |
| - 'include/ariec61850/**' | |
| - 'src/**' | |
| - '.github/workflows/arstack-studio-release.yml' | |
| - '.github/workflows/esp32p4-smv-injector.yml' | |
| push: | |
| tags: | |
| - 'v*' | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: arstack-studio-release-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| ARSTACK_VERSION: '0.1.1' | |
| QT_VERSION: '6.10.3' | |
| ESPFLASH_VERSION: '4.5.0' | |
| ESPFLASH_WINDOWS_ZIP_SHA256: '854c82c947c20e7f337f120f0398042ed1760f2269cb0f9be3d2beae3b66fbfb' | |
| jobs: | |
| firmware: | |
| uses: ./.github/workflows/esp32p4-smv-injector.yml | |
| windows-package: | |
| name: Windows x64 package | |
| needs: firmware | |
| runs-on: windows-2022 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Validate release version contract | |
| shell: powershell | |
| run: | | |
| $cmake = Get-Content 'apps/arstack_studio/CMakeLists.txt' -Raw | |
| if ($cmake -notmatch "project\(ARStackStudio VERSION $([regex]::Escape($env:ARSTACK_VERSION)) ") { | |
| throw "CMake project version does not match ARSTACK_VERSION=$env:ARSTACK_VERSION" | |
| } | |
| if ($env:GITHUB_REF -like 'refs/tags/*') { | |
| $expected = "v$env:ARSTACK_VERSION" | |
| if ($env:GITHUB_REF_NAME -ne $expected) { | |
| throw "P0 release only accepts tag $expected; got $env:GITHUB_REF_NAME" | |
| } | |
| } | |
| - name: Download verified ESP32-P4 firmware package | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: arstack-esp32p4-smv-firmware | |
| path: firmware-package | |
| - name: Install Qt | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: ${{ env.QT_VERSION }} | |
| arch: win64_msvc2022_64 | |
| modules: 'qtserialport' | |
| cache: true | |
| - name: Configure native Release build | |
| shell: powershell | |
| run: | | |
| cmake -S apps/arstack_studio -B build-arstack-studio -G "Visual Studio 17 2022" -A x64 ` | |
| -DARIEC61850_WARNINGS_AS_ERRORS=ON | |
| - name: Build native Release | |
| shell: powershell | |
| run: | | |
| cmake --build build-arstack-studio --config Release ` | |
| --target arstack_studio arstack_session_harness arstack_windows_ownership_harness --parallel | |
| - name: S8A/S8B native Windows integration gates | |
| timeout-minutes: 2 | |
| shell: powershell | |
| run: | | |
| $sessionHarness = Join-Path $PWD 'build-arstack-studio\Release\arstack_session_harness.exe' | |
| & $sessionHarness | |
| $sessionExit = $LASTEXITCODE | |
| if ($sessionExit -ne 0) { | |
| throw "S8A deterministic session harness on Windows failed with exit code $sessionExit" | |
| } | |
| $ownershipHarness = Join-Path $PWD 'build-arstack-studio\Release\arstack_windows_ownership_harness.exe' | |
| & $ownershipHarness | |
| $ownershipExit = $LASTEXITCODE | |
| if ($ownershipExit -ne 0) { | |
| throw "S8B Windows ownership harness failed with exit code $ownershipExit" | |
| } | |
| - name: Stage Qt application | |
| shell: powershell | |
| run: | | |
| $stage = Join-Path $PWD 'dist\ARStack-Studio' | |
| New-Item -ItemType Directory -Force $stage | Out-Null | |
| $sourceExe = Join-Path $PWD 'build-arstack-studio\Release\arstack_studio.exe' | |
| if (-not (Test-Path $sourceExe)) { throw "Release executable missing: $sourceExe" } | |
| $stageExe = Join-Path $stage 'ARStackStudio.exe' | |
| Copy-Item $sourceExe $stageExe | |
| windeployqt --release --qmldir "$PWD\apps\arstack_studio\qml" --compiler-runtime $stageExe | |
| Copy-Item LICENSE, NOTICE, THIRD_PARTY_NOTICES.md, COPYRIGHT.md -Destination $stage | |
| New-Item -ItemType Directory -Force (Join-Path $stage 'firmware') | Out-Null | |
| Copy-Item 'firmware-package\*' -Destination (Join-Path $stage 'firmware') -Recurse | |
| - name: Bundle pinned standalone espflash | |
| shell: powershell | |
| run: | | |
| $url = "https://github.com/esp-rs/espflash/releases/download/v$env:ESPFLASH_VERSION/espflash-x86_64-pc-windows-msvc.zip" | |
| $zip = Join-Path $env:RUNNER_TEMP 'espflash.zip' | |
| Invoke-WebRequest -Uri $url -OutFile $zip | |
| $actual = (Get-FileHash $zip -Algorithm SHA256).Hash.ToLowerInvariant() | |
| if ($actual -ne $env:ESPFLASH_WINDOWS_ZIP_SHA256) { | |
| throw "espflash asset SHA-256 mismatch: $actual" | |
| } | |
| $extract = Join-Path $env:RUNNER_TEMP 'espflash' | |
| Expand-Archive -Path $zip -DestinationPath $extract -Force | |
| $exe = Get-ChildItem $extract -Filter espflash.exe -Recurse | Select-Object -First 1 | |
| if (-not $exe) { throw 'espflash.exe not found in verified archive' } | |
| $tools = Join-Path $PWD 'dist\ARStack-Studio\tools' | |
| New-Item -ItemType Directory -Force $tools | Out-Null | |
| Copy-Item $exe.FullName (Join-Path $tools 'espflash.exe') | |
| $licenses = Join-Path $PWD 'dist\ARStack-Studio\licenses\espflash' | |
| New-Item -ItemType Directory -Force $licenses | Out-Null | |
| Invoke-WebRequest -Uri "https://raw.githubusercontent.com/esp-rs/espflash/v$env:ESPFLASH_VERSION/LICENSE-APACHE" -OutFile (Join-Path $licenses 'LICENSE-APACHE') | |
| Invoke-WebRequest -Uri "https://raw.githubusercontent.com/esp-rs/espflash/v$env:ESPFLASH_VERSION/LICENSE-MIT" -OutFile (Join-Path $licenses 'LICENSE-MIT') | |
| - name: Package contract acceptance | |
| shell: powershell | |
| run: | | |
| function Assert-ExitZero([string]$FilePath, [string[]]$Arguments, [string]$Label) { | |
| $process = Start-Process -FilePath $FilePath -ArgumentList $Arguments -Wait -PassThru | |
| if ($process.ExitCode -ne 0) { | |
| throw "$Label failed with exit code $($process.ExitCode)" | |
| } | |
| } | |
| $stage = Join-Path $PWD 'dist\ARStack-Studio' | |
| $studio = Join-Path $stage 'ARStackStudio.exe' | |
| $espflash = Join-Path $stage 'tools\espflash.exe' | |
| $env:ARSTACK_STUDIO_FIRMWARE_DIR = Join-Path $stage 'firmware' | |
| $env:ARSTACK_STUDIO_ESPFLASH = $espflash | |
| $env:ESPFLASH_SKIP_UPDATE_CHECK = 'true' | |
| Assert-ExitZero $studio @('--check-reference-template') 'Packaged reference-template regression' | |
| Assert-ExitZero $studio @('--check-firmware-contract') 'Packaged firmware contract' | |
| Assert-ExitZero $studio @('--check-p0-policy') 'Packaged native policy check' | |
| Assert-ExitZero $studio @('--check-app-lifecycle') 'Packaged primary-window shutdown check' | |
| & "$PWD\apps\arstack_studio\packaging\verify-single-instance.ps1" -Executable $studio | |
| Assert-ExitZero $espflash @('--version') 'Bundled espflash execution' | |
| Assert-ExitZero $espflash @('board-info', '--non-interactive', '--help') 'espflash board-info recovery CLI contract' | |
| Assert-ExitZero $espflash @('write-bin', '--chip', 'esp32p4', '--non-interactive', '--help') 'espflash write-bin recovery CLI contract' | |
| Assert-ExitZero $espflash @('reset', '--non-interactive', '--help') 'espflash reset recovery CLI contract' | |
| - name: Build portable ZIP | |
| shell: powershell | |
| run: | | |
| $zip = Join-Path $PWD "dist\ARStack-Studio-$env:ARSTACK_VERSION-win-x64-portable.zip" | |
| Compress-Archive -Path 'dist\ARStack-Studio\*' -DestinationPath $zip -CompressionLevel Optimal -Force | |
| - name: Install NSIS | |
| shell: powershell | |
| run: choco install nsis -y --no-progress | |
| - name: Build Windows installer | |
| shell: powershell | |
| run: | | |
| $stage = (Resolve-Path 'dist\ARStack-Studio').Path | |
| $out = Join-Path $PWD "dist\ARStack-Studio-$env:ARSTACK_VERSION-win-x64-setup.exe" | |
| $script = (Resolve-Path 'apps\arstack_studio\packaging\ARStackStudio.nsi').Path | |
| & makensis "/DSOURCE_DIR=$stage" "/DOUTPUT_FILE=$out" "/DAPP_VERSION=$env:ARSTACK_VERSION" $script | |
| if ($LASTEXITCODE -ne 0 -or -not (Test-Path $out)) { throw 'NSIS installer build failed' } | |
| - name: Generate release checksums | |
| shell: powershell | |
| run: | | |
| $files = @( | |
| "dist\ARStack-Studio-$env:ARSTACK_VERSION-win-x64-portable.zip", | |
| "dist\ARStack-Studio-$env:ARSTACK_VERSION-win-x64-setup.exe" | |
| ) | |
| $lines = foreach ($file in $files) { | |
| $hash = (Get-FileHash $file -Algorithm SHA256).Hash.ToLowerInvariant() | |
| "$hash $([IO.Path]::GetFileName($file))" | |
| } | |
| $lines | Set-Content 'dist\SHA256SUMS.txt' -Encoding ascii | |
| Get-Content 'dist\SHA256SUMS.txt' | |
| - name: Upload Windows release candidates | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: arstack-studio-windows-x64 | |
| path: | | |
| dist/ARStack-Studio-${{ env.ARSTACK_VERSION }}-win-x64-portable.zip | |
| dist/ARStack-Studio-${{ env.ARSTACK_VERSION }}-win-x64-setup.exe | |
| dist/SHA256SUMS.txt | |
| if-no-files-found: error | |
| retention-days: 14 | |
| clean-windows-smoke: | |
| name: Fresh Windows package smoke | |
| needs: windows-package | |
| runs-on: windows-2022 | |
| steps: | |
| - name: Checkout ownership probe | |
| uses: actions/checkout@v4 | |
| - name: Download release candidates only | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: arstack-studio-windows-x64 | |
| path: release-candidates | |
| - name: Verify release checksums | |
| shell: powershell | |
| run: | | |
| $expected = @{} | |
| Get-Content 'release-candidates\SHA256SUMS.txt' | ForEach-Object { | |
| if ($_ -match '^([0-9a-f]{64})\s+(.+)$') { $expected[$Matches[2]] = $Matches[1] } | |
| } | |
| foreach ($name in @( | |
| "ARStack-Studio-$env:ARSTACK_VERSION-win-x64-portable.zip", | |
| "ARStack-Studio-$env:ARSTACK_VERSION-win-x64-setup.exe")) { | |
| $path = Join-Path 'release-candidates' $name | |
| if (-not (Test-Path $path)) { throw "Missing release candidate: $name" } | |
| $actual = (Get-FileHash $path -Algorithm SHA256).Hash.ToLowerInvariant() | |
| if ($expected[$name] -ne $actual) { throw "Release checksum mismatch: $name" } | |
| } | |
| - name: Portable package smoke with restricted PATH | |
| shell: powershell | |
| run: | | |
| function Assert-ExitZero([string]$FilePath, [string[]]$Arguments, [string]$Label) { | |
| $process = Start-Process -FilePath $FilePath -ArgumentList $Arguments -Wait -PassThru | |
| if ($process.ExitCode -ne 0) { | |
| throw "$Label failed with exit code $($process.ExitCode)" | |
| } | |
| } | |
| $root = Join-Path $env:RUNNER_TEMP 'ARStackPortable' | |
| Expand-Archive -Path "release-candidates\ARStack-Studio-$env:ARSTACK_VERSION-win-x64-portable.zip" -DestinationPath $root -Force | |
| $exe = Join-Path $root 'ARStackStudio.exe' | |
| $espflash = Join-Path $root 'tools\espflash.exe' | |
| $env:ARSTACK_STUDIO_FIRMWARE_DIR = Join-Path $root 'firmware' | |
| $env:ARSTACK_STUDIO_ESPFLASH = $espflash | |
| $env:ESPFLASH_SKIP_UPDATE_CHECK = 'true' | |
| $savedPath = $env:PATH | |
| $env:PATH = "$env:SystemRoot\System32;$env:SystemRoot" | |
| try { | |
| Assert-ExitZero $exe @('--check-reference-template') 'Portable reference-template check' | |
| Assert-ExitZero $exe @('--check-firmware-contract') 'Portable firmware contract' | |
| Assert-ExitZero $exe @('--check-p0-policy') 'Portable P0 policy' | |
| Assert-ExitZero $exe @('--check-app-lifecycle') 'Portable primary-window shutdown check' | |
| & "$PWD\apps\arstack_studio\packaging\verify-single-instance.ps1" -Executable $exe | |
| Assert-ExitZero $espflash @('--version') 'Portable espflash' | |
| } finally { | |
| $env:PATH = $savedPath | |
| } | |
| - name: Installer smoke with restricted PATH | |
| shell: powershell | |
| run: | | |
| function Assert-ExitZero([string]$FilePath, [string[]]$Arguments, [string]$Label) { | |
| $process = Start-Process -FilePath $FilePath -ArgumentList $Arguments -Wait -PassThru | |
| if ($process.ExitCode -ne 0) { | |
| throw "$Label failed with exit code $($process.ExitCode)" | |
| } | |
| } | |
| $installer = (Resolve-Path "release-candidates\ARStack-Studio-$env:ARSTACK_VERSION-win-x64-setup.exe").Path | |
| $installDir = Join-Path $env:RUNNER_TEMP 'ARStackStudioInstall' | |
| $savedPath = $env:PATH | |
| $env:PATH = "$env:SystemRoot\System32;$env:SystemRoot" | |
| try { | |
| Assert-ExitZero $installer @('/S', "/D=$installDir") 'Silent installer' | |
| $exe = Join-Path $installDir 'ARStackStudio.exe' | |
| if (-not (Test-Path $exe)) { throw 'Installer did not deploy ARStackStudio.exe' } | |
| $espflash = Join-Path $installDir 'tools\espflash.exe' | |
| $env:ARSTACK_STUDIO_FIRMWARE_DIR = Join-Path $installDir 'firmware' | |
| $env:ARSTACK_STUDIO_ESPFLASH = $espflash | |
| $env:ESPFLASH_SKIP_UPDATE_CHECK = 'true' | |
| Assert-ExitZero $exe @('--check-reference-template') 'Installed reference-template check' | |
| Assert-ExitZero $exe @('--check-firmware-contract') 'Installed firmware contract' | |
| Assert-ExitZero $exe @('--check-p0-policy') 'Installed P0 policy' | |
| Assert-ExitZero $exe @('--check-app-lifecycle') 'Installed primary-window shutdown check' | |
| & "$PWD\apps\arstack_studio\packaging\verify-single-instance.ps1" -Executable $exe | |
| Assert-ExitZero $espflash @('--version') 'Installed espflash' | |
| $uninstaller = Join-Path $installDir 'Uninstall.exe' | |
| if (Test-Path $uninstaller) { | |
| Assert-ExitZero $uninstaller @('/S') 'Silent uninstaller' | |
| } | |
| } finally { | |
| $env:PATH = $savedPath | |
| } | |
| github-release: | |
| name: Publish tagged release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| needs: clean-windows-smoke | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download release candidates | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: arstack-studio-windows-x64 | |
| path: release | |
| - name: Publish GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| name: ARStack Studio v${{ env.ARSTACK_VERSION }} | |
| prerelease: true | |
| generate_release_notes: true | |
| files: | | |
| release/ARStack-Studio-${{ env.ARSTACK_VERSION }}-win-x64-portable.zip | |
| release/ARStack-Studio-${{ env.ARSTACK_VERSION }}-win-x64-setup.exe | |
| release/SHA256SUMS.txt |