igneum/.github/workflows/windows.yml

234 lines
12 KiB
YAML

# Windows one-click app, built on GitHub's Windows runners so no PC is needed (4 October 2026).
#
# parse: every .ps1 under the Windows folders through the Windows PowerShell 5.1 parser (powershell.exe, the PowerShell
# on the PCs; 5.1 rejects "$name: text" and that class broke two launchers on 4 October), PSScriptAnalyzer as
# warnings, and a parenthesis check of every .bat/.cmd (the bare ")" class of 3 October). Required: build
# needs it.
# build: the engine (app/igneum-app, cargo on the MSVC target, so one fewer input), the window host exactly as
# app\windows\BUILD-APP.bat does it (MSVC, WebView2 SDK from NuGet, static loader, host.rc with the coin icon),
# the payload with packaging/windows/make-payload.sh in Git Bash, the installer with build-installer.ps1
# (Inno Setup, rcedit), a smoke run of both exes (--version, --help), the launcher's DRY_RUN, then the installer,
# the payload zip and the host as artifacts (90 days).
#
# Inputs that are not in git (igneumd.exe, igneum-miner.exe from the node fork; the prebuilt GPU workers with NVIDIA's
# NVRTC DLLs) come from payload-inputs.zip on the downloads host, published by packaging/windows/push-inputs.sh on the
# Mac; the DL_TOKEN repository secret is the path token (gh secret set DL_TOKEN < ~/.config/igneum/dl-token).
# The Mac side of the loop is packaging/windows/fetch-ci-artifacts.sh (gh run download into the downloads folder).
name: windows-ci
on:
push:
branches: [master]
paths:
- 'app/**'
- 'packaging/windows/**'
- 'packaging/mac/packaged-config.sh'
- 'proto-cuda/windows-app/**'
- 'proto-cuda/windows-miner/**'
- 'proto-cuda/windows-node/**'
- 'proto-cuda/nvrtc/**'
- 'proto-cuda/build.bat'
- 'proto-opencl/**'
- 'proving/windows-wsl2/**'
- 'relay/clients/**'
- 'relay/playbooks/**'
- 'brand/icons/**'
- 'tools/ci/windows/**'
- '.github/workflows/windows.yml'
workflow_dispatch:
concurrency:
group: windows-${{ github.ref }}
cancel-in-progress: true
jobs:
parse:
name: PowerShell 5.1 parse, PSScriptAnalyzer, batch parentheses
runs-on: windows-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Windows PowerShell 5.1 parse of every .ps1 (with the negative self-test)
shell: powershell
run: |
$PSVersionTable.PSVersion.ToString()
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\ci\windows\check-ps51.ps1
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: parentheses in every .bat and .cmd (with the negative self-test)
shell: powershell
run: |
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\ci\windows\check-bat.ps1
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: PSScriptAnalyzer (warnings only, never fails the job)
shell: powershell
continue-on-error: true
run: |
try {
if (-not (Get-Module -ListAvailable PSScriptAnalyzer)) {
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser | Out-Null
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser -AllowClobber
}
Import-Module PSScriptAnalyzer
$folders = @('proto-cuda/windows-app', 'proto-cuda/windows-miner', 'proto-cuda/windows-node', 'proving/windows-wsl2', 'relay/clients', 'relay/playbooks', 'packaging/windows', 'tools/ci/windows')
$total = 0
foreach ($f in $folders) {
$results = Invoke-ScriptAnalyzer -Path $f -Recurse -Severity Warning, Error -ExcludeRule PSAvoidUsingWriteHost, PSUseShouldProcessForStateChangingFunctions, PSUseSingularNouns, PSAvoidUsingPositionalParameters
foreach ($r in $results) {
$total += 1
$file = ($r.ScriptPath -replace '\\', '/')
Write-Host ("::warning file={0},line={1}::{2}: {3}" -f $file, $r.Line, $r.RuleName, $r.Message)
}
}
Write-Host "PSScriptAnalyzer: $total warnings (informational)"
} catch {
Write-Host "::warning::PSScriptAnalyzer could not run: $_"
}
build:
name: engine, window host, payload, installer, smoke run
needs: parse
runs-on: windows-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: versions
shell: bash
run: |
set -euo pipefail
v="$(sed -n 's/^version = "\(.*\)"/\1/p' app/igneum-app/Cargo.toml | head -1)"
echo "APP_VERSION=$v" >> "$GITHUB_ENV"
echo "app version $v"
rustc --version; cargo --version
git --version; bash --version | head -1; perl --version | sed -n 2p; 7z 2>/dev/null | head -2 | tail -1 || true
- name: engine (app/igneum-app, cargo build --release on the MSVC target)
shell: bash
working-directory: app/igneum-app
run: |
set -euo pipefail
cargo build --release --locked
ls -la target/release/igneum-app.exe
- name: payload inputs (payload-inputs.zip from the downloads host, sha256 checked)
shell: bash
env:
DL_TOKEN: ${{ secrets.DL_TOKEN }}
run: |
set -euo pipefail
if [ -z "${DL_TOKEN:-}" ]; then
echo "::error::the DL_TOKEN repository secret is not set. On the Mac: tr -d '[:space:]' < ~/.config/igneum/dl-token | gh secret set DL_TOKEN --repo igneum-network/igneum"
exit 1
fi
base="https://dl.igneum.network/dl/$DL_TOKEN"
mkdir -p build/inputs "$HOME/.config/igneum"
printf '%s' "$DL_TOKEN" > "$HOME/.config/igneum/dl-token" # make-payload.sh reads it for the update manifest URL
curl -fsSL --retry 3 -o build/payload-inputs.json "$base/payload-inputs.json"
curl -fsSL --retry 3 -o build/payload-inputs.sha256 "$base/payload-inputs.sha256"
curl -fsSL --retry 3 -o build/payload-inputs.zip "$base/payload-inputs.zip"
echo "$(tr -d '[:space:]' < build/payload-inputs.sha256) build/payload-inputs.zip" | sha256sum -c -
7z x -y -bso0 -bsp0 -obuild/inputs-unpacked build/payload-inputs.zip
mv build/inputs-unpacked/payload-inputs/* build/inputs/
echo "inputs manifest:"; cat build/payload-inputs.json
echo "inputs:"; ls -la build/inputs
for f in igneumd.exe igneum-miner.exe; do [ -f "build/inputs/$f" ] || { echo "::error::payload-inputs.zip has no $f"; exit 1; }; done
- name: window host (app\windows\BUILD-APP.bat, exactly as on the PC)
shell: cmd
working-directory: app\windows
run: call BUILD-APP.bat < nul
- name: payload (packaging/windows/make-payload.sh, as on the Mac, in Git Bash)
shell: bash
run: |
set -euo pipefail
export IGNEUM_WIN_RELEASE="$PWD/build/inputs"
export IGNEUM_WORKERS_DIR="$PWD/build/inputs"
export IGNEUM_APP_EXE="$PWD/app/igneum-app/target/release/igneum-app.exe"
packaging/windows/make-payload.sh "$PWD/packaging/windows/dist/igneum-windows-app.zip"
test -f "packaging/windows/igneum-windows-app/Igneum Miner.exe" || { echo "::error::the window host did not land in the payload"; exit 1; }
- name: installer (packaging/windows/build-installer.ps1 in Windows PowerShell 5.1, Inno Setup, rcedit)
shell: powershell
working-directory: packaging\windows
run: |
$iscc = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe"
if (-not (Test-Path $iscc)) { choco install innosetup -y --no-progress | Out-Null }
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\build-installer.ps1 -NoWinget -Version $env:APP_VERSION
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Get-ChildItem dist | Format-Table Name, Length
- name: smoke run (igneum-app.exe --version, Igneum Miner.exe --version and --help)
shell: powershell
run: |
$payload = Resolve-Path 'packaging\windows\igneum-windows-app'
function Run-Capture([string]$exe, [string]$flag) {
$out = Join-Path $env:RUNNER_TEMP ('smoke-' + [IO.Path]::GetRandomFileName() + '.txt')
$p = Start-Process -FilePath $exe -ArgumentList $flag -Wait -NoNewWindow -PassThru -RedirectStandardOutput $out
$text = if (Test-Path $out) { (Get-Content $out -Raw) } else { '' }
Write-Host ("{0} {1} -> exit {2}: {3}" -f (Split-Path -Leaf $exe), $flag, $p.ExitCode, $text.Trim())
if ($p.ExitCode -ne 0) { throw "$exe $flag exited $($p.ExitCode)" }
return $text
}
$v = $env:APP_VERSION
$a = Run-Capture (Join-Path $payload 'igneum-app.exe') '--version'
if ($a -notmatch "igneum-app $([regex]::Escape($v))") { throw "igneum-app --version did not print 'igneum-app $v'" }
$b = Run-Capture (Join-Path $payload 'Igneum Miner.exe') '--version'
if ($b -notmatch "Igneum Miner $([regex]::Escape($v))") { throw "Igneum Miner.exe --version did not print 'Igneum Miner $v' (version.h and Cargo.toml differ?)" }
$c = Run-Capture (Join-Path $payload 'Igneum Miner.exe') '--help'
if ($c -notmatch 'Usage') { throw 'Igneum Miner.exe --help did not print the usage line' }
$info = (Get-Item (Join-Path $payload 'Igneum Miner.exe')).VersionInfo
Write-Host ("host version block: {0} {1} {2}" -f $info.ProductName, $info.ProductVersion, $info.FileDescription)
if ($info.ProductName -ne 'Igneum Miner') { throw 'the host exe carries no Igneum Miner version block (host.rc)' }
- name: launcher dry run (proto-cuda/windows-app, DRY_RUN=1, Windows PowerShell 5.1 via the .ps1 and the .bat)
shell: powershell
run: |
$stage = Join-Path $env:RUNNER_TEMP 'launcher'
New-Item -ItemType Directory -Force -Path $stage | Out-Null
Copy-Item -Path 'proto-cuda\windows-app\*' -Destination $stage -Recurse -Force
foreach ($f in @('igneumd.exe', 'igneum-miner.exe', 'igneum-worker-cuda.exe', 'igneum-worker-opencl.exe')) {
if (Test-Path "build\inputs\$f") { Copy-Item "build\inputs\$f" $stage }
}
Get-ChildItem 'build\inputs' -Filter 'nvrtc*.dll' | Copy-Item -Destination $stage
$env:DRY_RUN = '1'
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $stage 'start-igneum.ps1')
if ($LASTEXITCODE -ne 0) { throw "start-igneum.ps1 DRY_RUN=1 exited $LASTEXITCODE" }
$bat = cmd /c "cd /d `"$stage`" && START-IGNEUM.bat < nul 2>&1" | Out-String
Write-Host $bat
if ($bat -notmatch 'dry run done') { throw 'START-IGNEUM.bat with DRY_RUN=1 did not reach the end of the plan' }
- name: sizes
shell: bash
run: |
set -euo pipefail
{
echo "## Windows build $APP_VERSION"
echo
echo "| file | bytes |"
echo "|---|---:|"
for f in packaging/windows/dist/Igneum-Miner-Setup-*.exe packaging/windows/dist/igneum-windows-app.zip "app/windows/dist/Igneum Miner.exe" app/igneum-app/target/release/igneum-app.exe; do
printf '| %s | %s |\n' "$(basename "$f")" "$(stat -c %s "$f")"
done
echo
echo "inputs: $(tr -d '\n' < build/payload-inputs.json | head -c 400)"
} | tee -a "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@v4
with:
name: igneum-windows-installer
path: packaging/windows/dist/Igneum-Miner-Setup-*.exe
retention-days: 90
if-no-files-found: error
- uses: actions/upload-artifact@v4
with:
name: igneum-windows-payload
path: packaging/windows/dist/igneum-windows-app.zip
retention-days: 90
if-no-files-found: error
- uses: actions/upload-artifact@v4
with:
name: igneum-windows-host
path: app/windows/dist/Igneum Miner.exe
retention-days: 90
if-no-files-found: error