# Builds dist\Igneum-Miner-Setup-.exe on a Windows PC (started by BUILD-INSTALLER.bat, or run directly). # 1. Finds ISCC.exe (Inno Setup 6.3 or newer); installs Inno Setup 6 through winget when it is missing. # 2. Finds the art (brand\icons in the repo layout, or an art\ folder next to this script). # 3. Finds the payload: the extracted igneum-windows-app folder (igneum-app.exe, igneumd.exe, igneum-miner.exe, made by # make-payload.sh on the Mac) next to this script, in the parent folder, or given with -Payload; otherwise the zip at # -PayloadUrl is downloaded. "Igneum Miner.exe" (app\windows\BUILD-APP.bat) is shipped when it is in the payload. # 4. Copies the payload into build\payload without logs, packs and per-PC worker builds. # 5. Stamps the coin icon and the version block into igneum-app.exe, igneumd.exe and igneum-miner.exe with rcedit when # the exes do not carry them yet (the Mac relink with packaging\windows\embed-resources.sh --relink makes this a no-op). # 6. Compiles Igneum-Miner.iss and prints where the installer is. # # build-installer.ps1 [-Payload ] [-PayloadUrl ] [-Version 0.3.0] [-NoRcedit] [-NoWinget] param( [string]$Payload = '', [string]$PayloadUrl = $(if ($env:IGNEUM_PAYLOAD_URL) { $env:IGNEUM_PAYLOAD_URL } else { 'https://dl.igneum.network/igneum-windows-app.zip' }), [string]$Version = '0.3.0', [switch]$NoRcedit, [switch]$NoWinget ) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $here = Split-Path -Parent $MyInvocation.MyCommand.Path $build = Join-Path $here 'build' $dist = Join-Path $here 'dist' $rceditUrl = 'https://github.com/electron/rcedit/releases/download/v2.0.0/rcedit-x64.exe' New-Item -ItemType Directory -Force -Path $build, $dist | Out-Null function Say([string]$text) { Write-Host ('[build] ' + $text) } function Find-Iscc { $candidates = @() foreach ($base in @(${env:ProgramFiles(x86)}, $env:ProgramFiles, (Join-Path $env:LOCALAPPDATA 'Programs'))) { if ($base) { $candidates += (Join-Path $base 'Inno Setup 6\ISCC.exe') } } foreach ($key in @('HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1')) { try { $loc = (Get-ItemProperty -Path $key -ErrorAction Stop).InstallLocation if ($loc) { $candidates += (Join-Path $loc 'ISCC.exe') } } catch { } } foreach ($c in $candidates) { if ($c -and (Test-Path $c)) { return $c } } return $null } # ---- 1. Inno Setup ---------------------------------------------------------------------------------------------------- $iscc = Find-Iscc if (-not $iscc) { if ($NoWinget) { throw 'Inno Setup 6 is not installed (and -NoWinget was given). Install it from https://jrsoftware.org/isdl.php and run again.' } if (-not (Get-Command winget.exe -ErrorAction SilentlyContinue)) { throw 'Inno Setup 6 is not installed and winget is not available. Install Inno Setup 6 from https://jrsoftware.org/isdl.php and run again.' } Say 'Inno Setup 6 is not installed; installing it through winget (a UAC prompt may appear)' & winget.exe install --id JRSoftware.InnoSetup --exact --silent --accept-package-agreements --accept-source-agreements $iscc = Find-Iscc if (-not $iscc) { throw 'winget finished but ISCC.exe was not found. Install Inno Setup 6 from https://jrsoftware.org/isdl.php and run again.' } } # The version: ISCC.exe's own file version first; some installs (the GitHub runner image) ship an ISCC.exe without a # version block (0.0.0.0), then the uninstall key's DisplayVersion or ISCmplr.dll next to it. Unknown = continue. function Get-IsccVersion([string]$exe) { $candidates = @((Get-Item $exe).VersionInfo.FileVersion, (Get-Item $exe).VersionInfo.ProductVersion) foreach ($key in @('HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1')) { try { $candidates += (Get-ItemProperty -Path $key -ErrorAction Stop).DisplayVersion } catch { } } $dll = Join-Path (Split-Path -Parent $exe) 'ISCmplr.dll' if (Test-Path $dll) { $candidates += (Get-Item $dll).VersionInfo.FileVersion } foreach ($c in $candidates) { if (-not $c) { continue } $clean = ($c -replace '[^0-9.].*$', '') try { $v = [Version]$clean } catch { continue } if ($v.Major -gt 0) { return $v } } return $null } $v = Get-IsccVersion $iscc if ($v) { Say "Inno Setup $v at $iscc" if ($v -lt [Version]'6.3.0') { throw "Inno Setup $v is too old; 6.3 or newer is needed (winget upgrade --id JRSoftware.InnoSetup)." } } else { Say "Inno Setup at $iscc (version unknown: no version block, no uninstall key; continuing)" } # ---- 2. the art ------------------------------------------------------------------------------------------------------ $art = $null foreach ($dir in @((Join-Path $here '..\..\brand\icons'), (Join-Path $here 'art'))) { if (Test-Path (Join-Path $dir 'igneum.ico')) { $art = (Resolve-Path $dir).Path; break } } if (-not $art) { throw "No art folder: expected brand\icons two levels up (the repo layout) or an art\ folder next to this script with igneum.ico, inno-wizard-164x314.bmp and inno-wizard-small-55x58.bmp (made by brand\icons\make-icons.py)." } foreach ($f in @('igneum.ico', 'inno-wizard-164x314.bmp', 'inno-wizard-small-55x58.bmp')) { if (-not (Test-Path (Join-Path $art $f))) { throw "missing $f in $art" } } Say "art: $art" # ---- 3. the payload -------------------------------------------------------------------------------------------------- function Test-PayloadDir([string]$dir) { if (-not $dir -or -not (Test-Path $dir)) { return $false } foreach ($f in @('igneum-app.exe', 'stop-igneum.ps1', 'igneumd.exe', 'igneum-miner.exe')) { if (-not (Test-Path (Join-Path $dir $f))) { return $false } } return $true } $source = $null if ($Payload) { if (-not (Test-PayloadDir $Payload)) { throw "-Payload $Payload does not hold START-IGNEUM.bat, the .ps1 files and both exes." } $source = (Resolve-Path $Payload).Path } else { foreach ($dir in @((Join-Path $here 'igneum-windows-app'), (Join-Path $here '..\igneum-windows-app'), (Join-Path $here 'payload'), (Join-Path $here '..\..\igneum-windows-app'))) { if (Test-PayloadDir $dir) { $source = (Resolve-Path $dir).Path; break } } } if (-not $source) { $zip = Join-Path $build 'igneum-windows.zip' $unpacked = Join-Path $build 'payload-src' Say "no igneum-windows folder next to this script; downloading $PayloadUrl" Invoke-WebRequest -Uri $PayloadUrl -OutFile $zip -UseBasicParsing if (Test-Path $unpacked) { Remove-Item -Recurse -Force $unpacked } Expand-Archive -Path $zip -DestinationPath $unpacked -Force $hit = Get-ChildItem -Path $unpacked -Recurse -Filter 'igneum-app.exe' | Select-Object -First 1 if (-not $hit -or -not (Test-PayloadDir $hit.DirectoryName)) { throw "the zip from $PayloadUrl holds no complete payload (igneum-app.exe, igneumd.exe, igneum-miner.exe, stop-igneum.ps1)." } $source = $hit.DirectoryName } Say "payload source: $source" # ---- 4. assemble build\payload --------------------------------------------------------------------------------------- $payloadDir = Join-Path $build 'payload' if (Test-Path $payloadDir) { Remove-Item -Recurse -Force $payloadDir } New-Item -ItemType Directory -Force -Path $payloadDir | Out-Null $skipNames = @('.DS_Store') Get-ChildItem -Path $source -Recurse -Force | ForEach-Object { $rel = $_.FullName.Substring($source.Length).TrimStart('\') if ($rel -match '(^|\\)packs(\\|$)') { return } if ($rel -match '\.(log|seeds)$') { return } if ($rel -match '^proto-(cuda|opencl)\\[^\\]+\.exe$') { return } # GPU workers built per PC by the fallback path if ($rel -match '^app\\windows\\(build|dist)(\\|$)') { return } # BUILD-APP.bat's intermediate files if ($skipNames -contains $_.Name) { return } $dest = Join-Path $payloadDir $rel if ($_.PSIsContainer) { New-Item -ItemType Directory -Force -Path $dest | Out-Null } else { New-Item -ItemType Directory -Force -Path (Split-Path -Parent $dest) | Out-Null Copy-Item -Path $_.FullName -Destination $dest -Force } } $count = (Get-ChildItem -Path $payloadDir -Recurse -File | Measure-Object).Count $bytes = (Get-ChildItem -Path $payloadDir -Recurse -File | Measure-Object -Property Length -Sum).Sum Say ("payload: {0} files, {1:N1} MB" -f $count, ($bytes / 1MB)) # ---- 5. icon and version block in the exes --------------------------------------------------------------------------- $rcedit = $null foreach ($exeName in @('igneum-app.exe', 'igneumd.exe', 'igneum-miner.exe')) { $exe = Join-Path $payloadDir $exeName if (-not (Test-Path $exe)) { continue } $info = (Get-Item $exe).VersionInfo if ($info.ProductName -eq 'Igneum Miner') { Say "$exeName already carries the Igneum Miner version block (relinked on the Mac)"; continue } if ($NoRcedit) { Say "$exeName has no version block; -NoRcedit given, so Explorer shows it without the coin icon"; continue } if (-not $rcedit) { if ($env:RCEDIT -and (Test-Path $env:RCEDIT)) { $rcedit = $env:RCEDIT } else { $rcedit = Join-Path $build 'rcedit-x64.exe' if (-not (Test-Path $rcedit)) { Say "downloading rcedit from $rceditUrl"; Invoke-WebRequest -Uri $rceditUrl -OutFile $rcedit -UseBasicParsing } } } $desc = switch ($exeName) { 'igneumd.exe' { 'Igneum Miner node' } 'igneum-app.exe' { 'Igneum Miner engine' } default { 'Igneum Miner' } } $rcArgs = @($exe, '--set-icon', (Join-Path $art 'igneum.ico'), '--set-version-string', 'CompanyName', 'Igneum', '--set-version-string', 'ProductName', 'Igneum Miner', '--set-version-string', 'FileDescription', $desc, '--set-version-string', 'OriginalFilename', $exeName, '--set-version-string', 'LegalCopyright', 'Igneum. Nothing is bought or sold.', '--set-file-version', $Version, '--set-product-version', $Version) & $rcedit @rcArgs if ($LASTEXITCODE -ne 0) { throw "rcedit failed on $exeName (exit $LASTEXITCODE)" } Say "$exeName stamped: coin icon, Igneum Miner $Version" } # ---- 6. compile ------------------------------------------------------------------------------------------------------ $iss = Join-Path $here 'Igneum-Miner.iss' $isccArgs = @('/Qp', "/DPayload=$payloadDir", "/DArtDir=$art", "/DAppVersion=$Version", "/O$dist", $iss) Say "compiling $iss" & $iscc @isccArgs if ($LASTEXITCODE -ne 0) { throw "ISCC failed (exit $LASTEXITCODE)" } $out = Join-Path $dist "Igneum-Miner-Setup-$Version.exe" if (-not (Test-Path $out)) { throw "ISCC finished but $out is missing" } $size = (Get-Item $out).Length Say ("done: {0} ({1:N1} MB)" -f $out, ($size / 1MB)) Say 'The installer is unsigned until Igneum has a code-signing certificate: SmartScreen shows "Windows protected your PC" (More info > Run anyway).'