feat(packaging): signed Inno Setup installer for the Windows host + CI
apple / swift (push) Successful in 54s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
windows-host / package (push) Failing after 6m18s
android / android (push) Failing after 2m12s
ci / web (push) Successful in 38s
ci / rust (push) Failing after 1m40s
ci / docs-site (push) Successful in 29s
deb / build-publish (push) Successful in 2m35s
decky / build-publish (push) Successful in 24s
ci / bench (push) Successful in 4m32s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 14s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 3m35s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 20s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 2m33s
docker / deploy-docs (push) Successful in 22s
apple / swift (push) Successful in 54s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
windows-host / package (push) Failing after 6m18s
android / android (push) Failing after 2m12s
ci / web (push) Successful in 38s
ci / rust (push) Failing after 1m40s
ci / docs-site (push) Successful in 29s
deb / build-publish (push) Successful in 2m35s
decky / build-publish (push) Successful in 24s
ci / bench (push) Successful in 4m32s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 14s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 3m35s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 20s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 2m33s
docker / deploy-docs (push) Successful in 22s
MSIX (the client's format) can't install the host's LocalSystem secure-desktop service or the SudoVDA kernel driver, so the host ships as a signed Inno Setup setup.exe that runs elevated and delegates to the existing idempotent `punktfunk-host service install`. - packaging/windows/punktfunk-host.iss: lay exe into Program Files, optional SudoVDA driver task, run service install/start; [Code] stops+waits the service before file copy on upgrade; uninstall runs service uninstall. - pack-host-installer.ps1: cert (reuses MSIX_CERT_PFX_B64 / self-signed CN=unom), sign inner exe + setup.exe, fetch/stage SudoVDA, run ISCC, export public .cer. - fetch-sudovda.ps1 / install-sudovda.ps1: pinned SudoVDA + nefcon download, cert import, gated device-node create (no phantom dup), pnputil install (warn-not-abort). - nvenc/: synthesize nvencodeapi.lib via llvm-dlltool from a 2-export .def so --features nvenc links with no GPU/SDK at build time. - .gitea/workflows/windows-host.yml: build (nvenc) -> clippy -> ISCC -> sign -> publish setup.exe + .cer to the generic registry pkg punktfunk-host-windows. Tag host-win-v* -> X.Y.Z (+ latest/ alias); main push -> rolling 0.2.<run>. - setup-windows-runner.ps1: provision Inno Setup; docs: installer instructions. SudoVDA/nefcon release URLs+SHA-256s in fetch-sudovda.ps1 are placeholders (baseline v0.2.1) — fetch warns + prints the computed hash until pinned. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
# Build the punktfunk Windows HOST as a signed Inno Setup installer and publish it to Gitea's generic
|
||||
# package registry, so a Windows GPU box can install the streaming host (SYSTEM service + bundled
|
||||
# SudoVDA virtual-display driver) from one signed setup.exe. Runs on the self-hosted Windows runner
|
||||
# (host mode; scripts/ci/setup-windows-runner.ps1) — same MSVC/Windows-SDK/LLVM env as windows.yml.
|
||||
#
|
||||
# Why an installer and not MSIX (like the client): the host installs a LocalSystem SCM service that
|
||||
# CreateProcessAsUserW's into the interactive session for secure-desktop capture, and bundles a
|
||||
# kernel/IDD driver — neither is expressible in MSIX's sandbox. The real install logic already lives
|
||||
# in `punktfunk-host service install` (crates/punktfunk-host/src/service.rs); the installer just lays
|
||||
# the exe down and calls it elevated. Packaging internals: packaging/windows/README.md.
|
||||
#
|
||||
# Registry (public reads, unom org): https://git.unom.io/unom/-/packages (generic group)
|
||||
#
|
||||
# Versioning (free-form; not MSIX's 4-part rule):
|
||||
# host-win-vX.Y.Z tag -> X.Y.Z (a real host release; own tag namespace, off host-v*/win-v*/v*
|
||||
# to avoid the version-shadow bug class — see deb.yml).
|
||||
# main push / dispatch -> 0.2.<run_number> (rolling; climbs monotonically by run number).
|
||||
#
|
||||
# Signing reuses the client's MSIX_CERT_PFX_B64 / MSIX_CERT_PASSWORD secrets (CN=unom). Without them
|
||||
# an ephemeral self-signed cert is generated and its public .cer published next to the installer
|
||||
# (import once to LocalMachine\TrustedPublisher). See packaging/windows/pack-host-installer.ps1.
|
||||
#
|
||||
# NVENC: the host builds with --features nvenc; the only link need is nvencodeapi.lib, synthesised
|
||||
# from a 2-export .def with llvm-dlltool (no GPU/SDK at build time). The resulting exe is NVIDIA-only
|
||||
# by design — CI never launches it, so no GPU is needed here.
|
||||
name: windows-host
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'crates/punktfunk-host/**'
|
||||
- 'crates/punktfunk-core/**'
|
||||
- 'packaging/windows/**'
|
||||
- 'scripts/windows/host.env.example'
|
||||
- 'Cargo.lock'
|
||||
- 'Cargo.toml'
|
||||
- '.gitea/workflows/windows-host.yml'
|
||||
tags: ['host-win-v*']
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: git.unom.io
|
||||
OWNER: unom
|
||||
PKG: punktfunk-host-windows
|
||||
|
||||
jobs:
|
||||
package:
|
||||
runs-on: windows-amd64
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Configure + version
|
||||
shell: pwsh
|
||||
run: |
|
||||
# CARGO_TARGET_DIR=C:\t dodges the MAX_PATH wall in the CMake-from-source crates (aws-lc,
|
||||
# opus) the host pulls; CARGO_WORKSPACE_DIR mirrors the client workflows. Both via GITHUB_ENV
|
||||
# (pwsh Out-File utf8 = no BOM, unlike Windows PowerShell 5.1 — keeps the first line clean).
|
||||
"CARGO_TARGET_DIR=C:\t" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||
"CARGO_WORKSPACE_DIR=$env:GITHUB_WORKSPACE" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||
$v = if ($env:GITHUB_REF -like 'refs/tags/host-win-v*') {
|
||||
$env:GITHUB_REF_NAME -replace '^host-win-v', ''
|
||||
} else {
|
||||
"0.2.$($env:GITHUB_RUN_NUMBER)"
|
||||
}
|
||||
"HOST_VERSION=$v" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||
"PUNKTFUNK_BUILD_VERSION=$v" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||
Write-Output "host version $v"
|
||||
|
||||
- name: Generate NVENC import lib
|
||||
shell: pwsh
|
||||
run: |
|
||||
& packaging/windows/nvenc/gen-nvenc-importlib.ps1 -OutDir C:\t\nvenc
|
||||
"PUNKTFUNK_NVENC_LIB_DIR=C:\t\nvenc" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||
|
||||
- name: Build (release, nvenc)
|
||||
shell: pwsh
|
||||
run: cargo build --release -p punktfunk-host --features nvenc
|
||||
|
||||
- name: Clippy (host, Windows)
|
||||
shell: pwsh
|
||||
# First-ever Windows lint coverage for the host (Linux CI never lints the windows-cfg code).
|
||||
run: cargo clippy -p punktfunk-host --features nvenc -- -D warnings
|
||||
|
||||
- name: Ensure Inno Setup
|
||||
shell: pwsh
|
||||
run: |
|
||||
if (-not (Test-Path 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe') -and -not (Get-Command iscc -ErrorAction SilentlyContinue)) {
|
||||
Write-Output "installing Inno Setup via choco"
|
||||
choco install innosetup -y --no-progress
|
||||
}
|
||||
|
||||
- name: Pack + sign installer
|
||||
shell: pwsh
|
||||
env:
|
||||
MSIX_CERT_PFX_B64: ${{ secrets.MSIX_CERT_PFX_B64 }}
|
||||
MSIX_CERT_PASSWORD: ${{ secrets.MSIX_CERT_PASSWORD }}
|
||||
run: |
|
||||
& packaging/windows/pack-host-installer.ps1 `
|
||||
-Version $env:HOST_VERSION -TargetDir C:\t\release -OutDir C:\t\out
|
||||
|
||||
- name: Publish to Gitea generic registry
|
||||
shell: pwsh
|
||||
env:
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
run: |
|
||||
# Check curl's exit code ourselves — a best-effort DELETE (404 on first run) must not abort.
|
||||
$PSNativeCommandUseErrorActionPreference = $false
|
||||
function Publish-File($f, $url) {
|
||||
curl.exe -fsS --user "enricobuehler:$($env:REGISTRY_TOKEN)" --upload-file "$f" "$url"
|
||||
if ($LASTEXITCODE -ne 0) { throw "upload failed ($LASTEXITCODE): $url" }
|
||||
Write-Output "published $url"
|
||||
}
|
||||
$files = @($env:HOST_SETUP_PATH, $env:HOST_CER_PATH) | Where-Object { $_ -and (Test-Path $_) }
|
||||
if (-not $files) { throw "pack produced no artifacts to publish" }
|
||||
$base = "https://$($env:REGISTRY)/api/packages/$($env:OWNER)/generic/$($env:PKG)"
|
||||
foreach ($f in $files) { Publish-File $f "$base/$($env:HOST_VERSION)/$(Split-Path $f -Leaf)" }
|
||||
# On a tagged release, also refresh the stable `latest/` alias (delete-then-reupload, like
|
||||
# flatpak.yml/decky.yml) so there's a predictable download URL.
|
||||
if ($env:GITHUB_REF -like 'refs/tags/host-win-v*') {
|
||||
$aliases = @{ $env:HOST_SETUP_PATH = 'punktfunk-host-setup.exe'; $env:HOST_CER_PATH = 'punktfunk-host-windows.cer' }
|
||||
foreach ($f in $files) {
|
||||
$alias = $aliases[$f]; if (-not $alias) { continue }
|
||||
curl.exe -fsS -o NUL --user "enricobuehler:$($env:REGISTRY_TOKEN)" -X DELETE "$base/latest/$alias" 2>$null
|
||||
Publish-File $f "$base/latest/$alias"
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,22 @@ to be up by the time a client connects, so the ordering is soft.)
|
||||
On Bazzite, the host launches its own gamescope/Steam session per client, so you don't need a separate
|
||||
session unit — see [Bazzite](/docs/bazzite).
|
||||
|
||||
## Windows
|
||||
|
||||
On Windows the host runs as a `LocalSystem` service that launches into the interactive session, so it
|
||||
captures the secure desktop (UAC / lock screen) and survives reboots with nobody logged in — the same
|
||||
model Sunshine/Apollo use.
|
||||
|
||||
The easy path is the **signed installer**: download `punktfunk-host-setup-<ver>.exe` from the package
|
||||
registry ([`punktfunk-host-windows`](https://git.unom.io/unom/-/packages)) and run it. It drops the host
|
||||
into `C:\Program Files\punktfunk`, optionally installs the bundled **SudoVDA** virtual-display driver,
|
||||
and registers + starts the service for you (`/VERYSILENT` for unattended). Upgrades and uninstall are
|
||||
handled through Add/Remove Programs.
|
||||
|
||||
Prefer the CLI? Run `punktfunk-host service install` from an elevated prompt — see
|
||||
[Windows service](https://git.unom.io/unom/punktfunk/src/branch/main/docs/windows-service.md). Either
|
||||
way you need an NVIDIA GPU + driver (the host is NVENC-only on Windows).
|
||||
|
||||
## Verifying
|
||||
|
||||
After a reboot, from another machine on the network:
|
||||
|
||||
+18
-1
@@ -4,7 +4,24 @@ The `PunktfunkHost` Windows service is the end-user way to run the host on Windo
|
||||
manual bring-up chain (a scheduled task → `PsExec64 -s -i 1` → `wscript launch.vbs` → `host-run.cmd`)
|
||||
with one command, auto-start on boot, and supervision.
|
||||
|
||||
## Install
|
||||
## Install (installer — recommended)
|
||||
|
||||
Download the signed installer from the package registry
|
||||
(`punktfunk-host-windows`, <https://git.unom.io/unom/-/packages>) and run it (it elevates itself):
|
||||
|
||||
```
|
||||
punktfunk-host-setup-<ver>.exe # wizard
|
||||
punktfunk-host-setup-<ver>.exe /VERYSILENT # unattended
|
||||
```
|
||||
|
||||
It lays the host into `C:\Program Files\punktfunk`, optionally installs the bundled **SudoVDA**
|
||||
virtual-display driver, then runs `service install` + `service start` for you. Upgrades stop the
|
||||
service first and re-point it; uninstall (Add/Remove Programs) runs `service uninstall`. Packaging
|
||||
details: [`packaging/windows/README.md`](../packaging/windows/README.md). A self-signed CI build also
|
||||
publishes a `.cer` — import it once (`Import-Certificate -FilePath punktfunk-host-windows.cer
|
||||
-CertStoreLocation Cert:\LocalMachine\TrustedPublisher`) so Windows trusts the signed setup.
|
||||
|
||||
## Install (manual / CLI)
|
||||
|
||||
From an **elevated** (Administrator) prompt:
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# Windows host packaging — signed Inno Setup installer
|
||||
|
||||
A one-file, signed `setup.exe` for the punktfunk streaming **host** on Windows, published to Gitea's
|
||||
generic package registry (`punktfunk-host-windows`) by `.gitea/workflows/windows-host.yml`.
|
||||
|
||||
## Why not MSIX (like the client)
|
||||
|
||||
The host installs a **`LocalSystem` SCM service** that `CreateProcessAsUserW`'s from Session 0 into the
|
||||
interactive session for secure-desktop (UAC / lock screen) capture, adds firewall rules, and depends
|
||||
on the **SudoVDA** kernel/IDD virtual-display driver. MSIX's sandbox can install **neither** a SYSTEM
|
||||
service of this kind **nor** a driver. So the host ships as a classic elevated installer.
|
||||
|
||||
The installer is deliberately thin: the real install logic — SCM registration, firewall rules, the
|
||||
default `host.env`, and the SYSTEM→interactive-session supervisor — already lives in
|
||||
`punktfunk-host service install` (`crates/punktfunk-host/src/service.rs`). The installer just lays the
|
||||
exe into `C:\Program Files\punktfunk\` and calls that subcommand, elevated.
|
||||
|
||||
## What the installer does
|
||||
|
||||
- Installs `punktfunk-host.exe` (+ `host.env.example`, this README) to `{app}` (`C:\Program Files\punktfunk`).
|
||||
- **Optional task** *Install the SudoVDA virtual display driver* — imports the driver's self-signed
|
||||
cert (machine `Root` + `TrustedPublisher`), creates the `root\sudomaker\sudovda` device node (only
|
||||
if absent — `install-sudovda.ps1`), and stages the driver with `pnputil /add-driver /install`.
|
||||
Best-effort: a driver failure warns but never aborts the install (the host degrades to a physical
|
||||
display without it).
|
||||
- Runs `punktfunk-host service install` (idempotent; writes a default `host.env` only if absent, so
|
||||
user config survives upgrades) and, by the *Start service now* task, `service start`.
|
||||
- **Upgrade:** stops a running `PunktfunkHost` service and waits for `STOPPED` before replacing files
|
||||
(otherwise the locked exe / respawning supervisor would block the copy), then re-points the service.
|
||||
- **Uninstall** (Add/Remove Programs): runs `service uninstall` (stop + delete service + remove
|
||||
firewall rules). The SudoVDA driver is intentionally left installed.
|
||||
|
||||
Silent install: `punktfunk-host-setup-<ver>.exe /VERYSILENT` (omit the driver with `/MERGETASKS="!installdriver"`).
|
||||
|
||||
## Prerequisites on the target box
|
||||
|
||||
- An **NVIDIA GPU + driver** — the installer's exe is built `--features nvenc` and load-depends on the
|
||||
driver's `nvEncodeAPI64.dll`.
|
||||
- **ViGEmBus** (optional) for virtual gamepads — still a manual prerequisite (not bundled yet):
|
||||
<https://github.com/nefarius/ViGEmBus/releases>.
|
||||
|
||||
## Files here
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `punktfunk-host.iss` | Inno Setup script (the installer definition). |
|
||||
| `pack-host-installer.ps1` | Orchestrator: cert + sign, fetch/stage SudoVDA, run ISCC, sign setup.exe, emit registry paths. |
|
||||
| `fetch-sudovda.ps1` | Download + SHA-256-verify the **pinned** SudoVDA + nefcon releases; stage the driver payload. |
|
||||
| `install-sudovda.ps1` | Runs at install time (elevated): trust cert → gated device-node create → `pnputil` install. |
|
||||
| `nvenc/nvenc.def`, `nvenc/gen-nvenc-importlib.ps1` | Synthesise `nvencodeapi.lib` for the `--features nvenc` link (llvm-dlltool / lib.exe). |
|
||||
|
||||
> **Pinning:** the SudoVDA / nefcon release URLs + SHA-256s in `fetch-sudovda.ps1` are the source of
|
||||
> truth for what ships. Confirm the latest asset URLs and fill the SHA-256s to lock a release.
|
||||
|
||||
## Build locally (Windows, MSVC + Windows SDK + Inno Setup)
|
||||
|
||||
```powershell
|
||||
# 1. import lib for the nvenc link
|
||||
pwsh -File packaging\windows\nvenc\gen-nvenc-importlib.ps1 -OutDir C:\t\nvenc
|
||||
$env:PUNKTFUNK_NVENC_LIB_DIR = 'C:\t\nvenc'
|
||||
|
||||
# 2. build the host
|
||||
cargo build --release -p punktfunk-host --features nvenc
|
||||
|
||||
# 3. pack (self-signed unless MSIX_CERT_PFX_B64/MSIX_CERT_PASSWORD are set; -NoDriver to skip SudoVDA)
|
||||
pwsh -File packaging\windows\pack-host-installer.ps1 -Version 0.0.0-dev -TargetDir C:\t\release -OutDir C:\t\out
|
||||
```
|
||||
|
||||
## Release
|
||||
|
||||
Push a `host-win-vX.Y.Z` tag — the workflow builds, signs, and publishes
|
||||
`punktfunk-host-setup-X.Y.Z.exe` + the public `.cer`, and refreshes the `latest/` alias. Main pushes
|
||||
publish rolling `0.2.<run>` builds (no `latest/` update).
|
||||
@@ -0,0 +1,91 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Download + verify the SudoVDA virtual-display driver and the nefcon device tool, and stage the
|
||||
files the installer bundles into -OutDir.
|
||||
|
||||
.DESCRIPTION
|
||||
The host uses SudoVDA (the SudoMaker Indirect Display Driver the Apollo Sunshine-fork ships) for
|
||||
client-native-resolution virtual outputs (crates/punktfunk-host/src/vdisplay/sudovda.rs). The
|
||||
driver isn't in this repo; CI fetches a PINNED release at pack time so the installer can carry it.
|
||||
nefcon (nefarius/nefcon) provides `nefconc.exe`, used to create the root-enumerated device node
|
||||
(pnputil cannot create a software/root device node from nothing).
|
||||
|
||||
Output (consumed by punktfunk-host.iss): -OutDir gets the driver payload (*.inf/*.cat/*.dll/*.sys
|
||||
+ the signing *.cer) and nefconc.exe, flattened. install-sudovda.ps1 is copied in by the packer.
|
||||
|
||||
PINNING: the URLs + SHA-256s below are the single source of truth for what ships. If a Sha256 is
|
||||
left empty, the script downloads, prints the computed hash, and continues with a loud warning —
|
||||
fill it in to lock the release. Override any field with the params for a one-off build.
|
||||
|
||||
.EXAMPLE
|
||||
pwsh -File fetch-sudovda.ps1 -OutDir C:\t\out\stage
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$OutDir,
|
||||
|
||||
# --- PINNED SudoVDA release (https://github.com/SudoMaker/SudoVDA/releases) ---------------
|
||||
# Baseline validated by the host backend: SudoVDA 0.2.1 (sudovda.rs header). Confirm the latest
|
||||
# asset name/URL at impl time; fill Sha256 to lock it.
|
||||
[string]$SudoVdaTag = 'v0.2.1',
|
||||
[string]$SudoVdaUrl = 'https://github.com/SudoMaker/SudoVDA/releases/download/v0.2.1/SudoVDA-v0.2.1.zip',
|
||||
[string]$SudoVdaSha256 = '',
|
||||
|
||||
# --- PINNED nefcon release (https://github.com/nefarius/nefcon/releases) ------------------
|
||||
[string]$NefconUrl = 'https://github.com/nefarius/nefcon/releases/download/v1.0.2.0/nefconw.zip',
|
||||
[string]$NefconSha256 = ''
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
function Get-Verified([string]$Url, [string]$ExpectedSha256, [string]$Dest) {
|
||||
Write-Host "==> downloading $Url"
|
||||
Invoke-WebRequest -Uri $Url -OutFile $Dest -UseBasicParsing
|
||||
$got = (Get-FileHash -Algorithm SHA256 -Path $Dest).Hash.ToLowerInvariant()
|
||||
if ($ExpectedSha256) {
|
||||
if ($got -ne $ExpectedSha256.ToLowerInvariant()) {
|
||||
throw "SHA-256 mismatch for $Url`n expected $ExpectedSha256`n got $got"
|
||||
}
|
||||
Write-Host " sha256 ok ($got)"
|
||||
}
|
||||
else {
|
||||
Write-Warning "no pinned SHA-256 for $Url — computed $got (PIN THIS in fetch-sudovda.ps1)"
|
||||
}
|
||||
}
|
||||
|
||||
# fresh staging dir
|
||||
if (Test-Path $OutDir) { Remove-Item -Recurse -Force $OutDir }
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
$work = Join-Path $OutDir '_work'
|
||||
New-Item -ItemType Directory -Force -Path $work | Out-Null
|
||||
|
||||
# --- SudoVDA driver payload -------------------------------------------------------------------
|
||||
$sudoZip = Join-Path $work 'sudovda.zip'
|
||||
Get-Verified -Url $SudoVdaUrl -ExpectedSha256 $SudoVdaSha256 -Dest $sudoZip
|
||||
$sudoEx = Join-Path $work 'sudovda'
|
||||
Expand-Archive -Path $sudoZip -DestinationPath $sudoEx -Force
|
||||
|
||||
# Flatten the driver files we need into -OutDir. SudoVDA is a user-mode IDD (a .dll registered by an
|
||||
# .inf); pull the .inf/.cat/.dll plus a .sys if present and the signing .cer.
|
||||
$driverFiles = Get-ChildItem -Path $sudoEx -Recurse -Include *.inf, *.cat, *.dll, *.sys, *.cer
|
||||
if (-not ($driverFiles | Where-Object Extension -eq '.inf')) {
|
||||
throw "no .inf found in the SudoVDA archive ($SudoVdaUrl) — wrong asset?"
|
||||
}
|
||||
$driverFiles | ForEach-Object { Copy-Item $_.FullName (Join-Path $OutDir $_.Name) -Force }
|
||||
|
||||
# --- nefcon (nefconc.exe) ---------------------------------------------------------------------
|
||||
$nefZip = Join-Path $work 'nefcon.zip'
|
||||
Get-Verified -Url $NefconUrl -ExpectedSha256 $NefconSha256 -Dest $nefZip
|
||||
$nefEx = Join-Path $work 'nefcon'
|
||||
Expand-Archive -Path $nefZip -DestinationPath $nefEx -Force
|
||||
# Prefer the x64 console build.
|
||||
$nefc = Get-ChildItem -Path $nefEx -Recurse -Filter 'nefconc.exe' |
|
||||
Where-Object { $_.FullName -match '(?i)(x64|amd64)' } |
|
||||
Select-Object -First 1
|
||||
if (-not $nefc) { $nefc = Get-ChildItem -Path $nefEx -Recurse -Filter 'nefconc.exe' | Select-Object -First 1 }
|
||||
if (-not $nefc) { throw "nefconc.exe not found in $NefconUrl" }
|
||||
Copy-Item $nefc.FullName (Join-Path $OutDir 'nefconc.exe') -Force
|
||||
|
||||
Remove-Item -Recurse -Force $work
|
||||
Write-Host "==> staged SudoVDA + nefcon in $OutDir :"
|
||||
Get-ChildItem $OutDir -File | ForEach-Object { " $($_.Name)" }
|
||||
@@ -0,0 +1,78 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Install the bundled SudoVDA virtual-display driver. Runs ELEVATED at setup time (invoked from the
|
||||
installer's [Run] section). Best-effort: warns and exits 0 on any failure — the host degrades to a
|
||||
physical display without SudoVDA, so a driver hiccup must never abort the whole install.
|
||||
|
||||
.DESCRIPTION
|
||||
-Dir holds the staged payload from fetch-sudovda.ps1 (the .inf/.cat/.dll + signing .cer + nefconc.exe).
|
||||
Steps:
|
||||
1. Trust the self-signed driver cert (machine Root + TrustedPublisher) so PnP installs it silently.
|
||||
2. Create the root device node IF ABSENT (gated — a blind re-create spawns a phantom duplicate, and
|
||||
the host's open_device() binds interface index 0; crates/punktfunk-host/src/vdisplay/sudovda.rs).
|
||||
3. Stage + bind the driver (pnputil /add-driver /install — modern, in-box, idempotent).
|
||||
|
||||
Class/ClassGuid are read from the .inf so they always match the shipped driver.
|
||||
|
||||
.EXAMPLE
|
||||
powershell -ExecutionPolicy Bypass -File install-sudovda.ps1 -Dir C:\path\to\sudovda
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Dir = $PSScriptRoot,
|
||||
[string]$HardwareId = 'root\sudomaker\sudovda' # verified live (docs/windows-host.md)
|
||||
)
|
||||
# Never abort the installer on a driver failure.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
trap { Write-Warning "SudoVDA install error: $_"; exit 0 }
|
||||
|
||||
function Test-SudoVdaPresent {
|
||||
$devs = Get-PnpDevice -Class Display -PresentOnly -ErrorAction SilentlyContinue
|
||||
foreach ($d in $devs) {
|
||||
$hw = (Get-PnpDeviceProperty -InstanceId $d.InstanceId -KeyName 'DEVPKEY_Device_HardwareIds' `
|
||||
-ErrorAction SilentlyContinue).Data
|
||||
if ($hw -and ($hw | Where-Object { $_ -ieq $HardwareId })) { return $true }
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
$inf = Get-ChildItem -Path $Dir -Filter *.inf -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
$cer = Get-ChildItem -Path $Dir -Filter *.cer -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
$nef = Get-ChildItem -Path $Dir -Filter 'nefconc.exe' -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if (-not $inf) { Write-Warning "no SudoVDA .inf in $Dir; skipping driver install."; exit 0 }
|
||||
Write-Host "SudoVDA inf: $($inf.FullName)"
|
||||
|
||||
# 1) Trust the self-signed driver cert (a self-signed driver needs the cert in BOTH the machine
|
||||
# Root store, so the chain validates, and TrustedPublisher, so PnP installs without a prompt).
|
||||
if ($cer) {
|
||||
Write-Host "==> importing $($cer.Name) to Root + TrustedPublisher"
|
||||
certutil.exe -addstore -f Root "$($cer.FullName)" | Out-Null
|
||||
certutil.exe -addstore -f TrustedPublisher "$($cer.FullName)" | Out-Null
|
||||
}
|
||||
else { Write-Warning "no .cer in $Dir — driver may not install silently (untrusted publisher)" }
|
||||
|
||||
# 2) Create the root device node only if it isn't already there.
|
||||
if (Test-SudoVdaPresent) {
|
||||
Write-Host "SudoVDA device node already present — leaving it as-is."
|
||||
}
|
||||
elseif ($nef) {
|
||||
$infText = Get-Content -Raw $inf.FullName
|
||||
$classGuid = ([regex]::Match($infText, '(?im)^\s*ClassGuid\s*=\s*(\{[0-9A-Fa-f-]+\})')).Groups[1].Value
|
||||
$className = ([regex]::Match($infText, '(?im)^\s*Class\s*=\s*([^\s;]+)')).Groups[1].Value
|
||||
if (-not $classGuid) { $classGuid = '{4d36e968-e325-11ce-bfc1-08002be10318}'; $className = 'Display' } # Display class
|
||||
Write-Host "==> nefconc --create-device-node hwid=$HardwareId class=$className $classGuid"
|
||||
& $nef.FullName --create-device-node --hardware-id $HardwareId --class-name $className --class-guid $classGuid
|
||||
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 3010) {
|
||||
Write-Warning "nefconc --create-device-node returned $LASTEXITCODE"
|
||||
}
|
||||
}
|
||||
else { Write-Warning "nefconc.exe not found in $Dir — cannot create the SudoVDA device node." }
|
||||
|
||||
# 3) Stage + bind the driver (idempotent; re-staging the same .inf is harmless).
|
||||
Write-Host "==> pnputil /add-driver $($inf.Name) /install"
|
||||
& pnputil.exe /add-driver "$($inf.FullName)" /install
|
||||
$rc = $LASTEXITCODE
|
||||
if ($rc -eq 3010) { Write-Host " driver installed; a reboot is recommended." }
|
||||
elseif ($rc -ne 0) { Write-Warning "pnputil /add-driver returned $rc" }
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,57 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generate the NVENC import library (nvencodeapi.lib) into -OutDir, so the host links with
|
||||
`--features nvenc` on a box that has no NVIDIA Video Codec SDK and no GPU.
|
||||
|
||||
.DESCRIPTION
|
||||
The host links against nvencodeapi.lib (crates/punktfunk-host/build.rs). That import lib is just
|
||||
a link-time stub for two exports of nvEncodeAPI64.dll (the real DLL ships with the NVIDIA driver
|
||||
and resolves at runtime). We synthesise it from nvenc.def:
|
||||
|
||||
1. llvm-dlltool — preferred; LLVM is on the CI runner PATH (C:\Program Files\LLVM\bin) and this
|
||||
works without a Visual Studio developer shell.
|
||||
2. MSVC lib.exe — fallback; located via vswhere (no vcvars needed).
|
||||
|
||||
Point PUNKTFUNK_NVENC_LIB_DIR at -OutDir before `cargo build --features nvenc`.
|
||||
|
||||
.EXAMPLE
|
||||
pwsh -File gen-nvenc-importlib.ps1 -OutDir C:\t\nvenc
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$OutDir,
|
||||
[string]$DefPath = (Join-Path $PSScriptRoot 'nvenc.def')
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$PSNativeCommandUseErrorActionPreference = $false # check $LASTEXITCODE ourselves (pwsh 7.4 safe)
|
||||
|
||||
if (-not (Test-Path $DefPath)) { throw "module-definition file not found: $DefPath" }
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
$out = Join-Path $OutDir 'nvencodeapi.lib'
|
||||
|
||||
# 1) llvm-dlltool (preferred) ------------------------------------------------------------------
|
||||
$dlltool = Get-Command llvm-dlltool -ErrorAction SilentlyContinue
|
||||
if ($dlltool) {
|
||||
Write-Host "==> llvm-dlltool -> $out"
|
||||
& $dlltool.Source -m i386:x86-64 -d $DefPath -D nvEncodeAPI64.dll -l $out
|
||||
if ($LASTEXITCODE -ne 0) { throw "llvm-dlltool failed ($LASTEXITCODE)" }
|
||||
Write-Host " ok ($((Get-Item $out).Length) bytes)"
|
||||
return
|
||||
}
|
||||
|
||||
# 2) MSVC lib.exe via vswhere (fallback) -------------------------------------------------------
|
||||
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
|
||||
if (Test-Path $vswhere) {
|
||||
$lib = & $vswhere -latest -prerelease -products * -find 'VC\Tools\MSVC\**\bin\Hostx64\x64\lib.exe' |
|
||||
Select-Object -First 1
|
||||
if ($lib -and (Test-Path $lib)) {
|
||||
Write-Host "==> lib.exe -> $out"
|
||||
& $lib "/def:$DefPath" /machine:x64 "/out:$out"
|
||||
if ($LASTEXITCODE -ne 0) { throw "lib.exe failed ($LASTEXITCODE)" }
|
||||
Write-Host " ok ($((Get-Item $out).Length) bytes)"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
throw "neither llvm-dlltool (LLVM bin on PATH) nor MSVC lib.exe (via vswhere) was found to build $out"
|
||||
@@ -0,0 +1,14 @@
|
||||
; Module-definition file for the NVENC import library the host links against with `--features nvenc`.
|
||||
;
|
||||
; The real entry points live in nvEncodeAPI64.dll, which ships with the NVIDIA driver. At LINK time
|
||||
; the host only needs an import library exporting these two symbols (see crates/punktfunk-host/build.rs:
|
||||
; it emits `cargo:rustc-link-lib=dylib=nvencodeapi` and searches PUNKTFUNK_NVENC_LIB_DIR). No GPU,
|
||||
; driver, or NVIDIA Video Codec SDK is required to BUILD — only to run, where the DLL resolves from
|
||||
; the installed driver. Generate nvencodeapi.lib from this file with gen-nvenc-importlib.ps1.
|
||||
;
|
||||
; The LIBRARY line names the DLL the import records point at — required for MSVC `lib.exe /def`
|
||||
; (without it the import name would default to "nvenc.dll"). llvm-dlltool takes the name from `-D`.
|
||||
LIBRARY nvEncodeAPI64.dll
|
||||
EXPORTS
|
||||
NvEncodeAPICreateInstance
|
||||
NvEncodeAPIGetMaxSupportedVersion
|
||||
@@ -0,0 +1,143 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Build + sign the punktfunk Windows host installer (Inno Setup setup.exe).
|
||||
|
||||
.DESCRIPTION
|
||||
From a release `cargo build -p punktfunk-host --features nvenc` output (the exe), this:
|
||||
1. resolves a code-signing cert (supplied stable .pfx from CI secrets OR an ephemeral self-signed
|
||||
CN=unom — same scheme as the client's pack-msix.ps1) and exports the public .cer,
|
||||
2. signs the inner punktfunk-host.exe,
|
||||
3. fetches + stages the SudoVDA driver bundle (unless -NoDriver),
|
||||
4. runs ISCC to build punktfunk-host-setup-<ver>.exe,
|
||||
5. signs the setup.exe (timestamp best-effort),
|
||||
6. emits HOST_SETUP_PATH / HOST_CER_PATH to GITHUB_ENV for the publish step.
|
||||
|
||||
Idempotent; safe to re-run. Run on the Windows runner / dev box (MSVC + Windows SDK + Inno Setup).
|
||||
|
||||
.EXAMPLE
|
||||
pwsh -File pack-host-installer.ps1 -Version 0.2.137 -TargetDir C:\t\release -OutDir C:\t\out
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Version, # e.g. 0.2.137 or 1.4.0 (free-form)
|
||||
[Parameter(Mandatory = $true)][string]$TargetDir, # cargo --release dir (has punktfunk-host.exe)
|
||||
[string]$OutDir = (Join-Path $TargetDir 'installer'),
|
||||
[string]$Publisher = 'CN=unom',
|
||||
[string]$PfxBase64 = $env:MSIX_CERT_PFX_B64, # reuse the client's signing secret
|
||||
[string]$PfxPassword = $env:MSIX_CERT_PASSWORD,
|
||||
[switch]$NoDriver, # build without the bundled SudoVDA driver
|
||||
[switch]$NoSign # skip signing (local debug)
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
# Keep the traditional "check $LASTEXITCODE myself" model: don't let pwsh 7.4 turn a non-zero native
|
||||
# exit into a terminating error (it would bypass Sign-File's timestamp-then-retry fallback below).
|
||||
$PSNativeCommandUseErrorActionPreference = $false
|
||||
|
||||
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$iss = Join-Path $here 'punktfunk-host.iss'
|
||||
$exe = Join-Path $TargetDir 'punktfunk-host.exe'
|
||||
if (-not (Test-Path $exe)) { throw "missing build artifact 'punktfunk-host.exe' in $TargetDir (did 'cargo build --release -p punktfunk-host --features nvenc' run?)" }
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
# --- locate ISCC (Inno Setup) + signtool (Windows SDK) ---------------------------------------
|
||||
function Find-Iscc {
|
||||
foreach ($p in @(
|
||||
'C:\Program Files (x86)\Inno Setup 6\ISCC.exe',
|
||||
'C:\Program Files\Inno Setup 6\ISCC.exe')) {
|
||||
if (Test-Path $p) { return $p }
|
||||
}
|
||||
$c = Get-Command iscc -ErrorAction SilentlyContinue
|
||||
if ($c) { return $c.Source }
|
||||
throw "ISCC.exe (Inno Setup 6, any 6.x) not found — install it (choco install innosetup -y)."
|
||||
}
|
||||
function Find-SdkTool([string]$name) {
|
||||
$root = 'C:\Program Files (x86)\Windows Kits\10\bin'
|
||||
$hit = Get-ChildItem -Path $root -Recurse -Filter $name -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.FullName -match '\\(10\.0\.\d+\.\d+)\\x64\\' } |
|
||||
Sort-Object { [version]([regex]::Match($_.FullName, '\\(10\.0\.\d+\.\d+)\\x64\\').Groups[1].Value) } |
|
||||
Select-Object -Last 1
|
||||
if (-not $hit) { throw "$name not found under $root — install the Windows 10/11 SDK." }
|
||||
$hit.FullName
|
||||
}
|
||||
$iscc = Find-Iscc
|
||||
Write-Host "ISCC: $iscc"
|
||||
|
||||
# --- signing cert (supplied stable pfx OR ephemeral self-signed) -----------------------------
|
||||
$pfxPath = Join-Path $OutDir 'signing.pfx'
|
||||
$cerPath = Join-Path $OutDir "punktfunk-host-windows_${Version}.cer"
|
||||
$signtool = $null
|
||||
if (-not $NoSign) {
|
||||
$signtool = Find-SdkTool 'signtool.exe'
|
||||
Write-Host "signtool: $signtool"
|
||||
if ($PfxBase64) {
|
||||
Write-Host "signing with supplied code-signing cert (MSIX_CERT_PFX_B64)"
|
||||
[IO.File]::WriteAllBytes($pfxPath, [Convert]::FromBase64String($PfxBase64))
|
||||
}
|
||||
else {
|
||||
Write-Host "no MSIX_CERT_PFX_B64 -> generating an ephemeral self-signed cert (subject $Publisher)"
|
||||
if (-not $PfxPassword) { $PfxPassword = 'punktfunk' }
|
||||
$tmp = New-SelfSignedCertificate -Type Custom -Subject $Publisher `
|
||||
-KeyUsage DigitalSignature -FriendlyName 'punktfunk host (self-signed)' `
|
||||
-CertStoreLocation 'Cert:\CurrentUser\My' `
|
||||
-TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.3', '2.5.29.19={text}')
|
||||
$sec = ConvertTo-SecureString -String $PfxPassword -Force -AsPlainText
|
||||
Export-PfxCertificate -Cert "Cert:\CurrentUser\My\$($tmp.Thumbprint)" -FilePath $pfxPath -Password $sec | Out-Null
|
||||
Remove-Item "Cert:\CurrentUser\My\$($tmp.Thumbprint)" -Force
|
||||
}
|
||||
# Always export the public .cer. For a self-signed cert it's the file users import once
|
||||
# (LocalMachine\TrustedPublisher) so SmartScreen/UAC trusts the signed setup.exe; for a real CA
|
||||
# cert it's a harmless extra.
|
||||
$pwsec = if ($PfxPassword) { ConvertTo-SecureString -String $PfxPassword -Force -AsPlainText } else { $null }
|
||||
$pubCert = if ($pwsec) { Get-PfxCertificate -FilePath $pfxPath -Password $pwsec } else { Get-PfxCertificate -FilePath $pfxPath }
|
||||
Export-Certificate -Cert $pubCert -FilePath $cerPath | Out-Null
|
||||
Write-Host "signing cert subject=$($pubCert.Subject) thumbprint=$($pubCert.Thumbprint)"
|
||||
}
|
||||
|
||||
function Sign-File([string]$Path) {
|
||||
if ($NoSign) { return }
|
||||
$signArgs = @('sign', '/fd', 'SHA256', '/f', $pfxPath)
|
||||
if ($PfxPassword) { $signArgs += @('/p', $PfxPassword) }
|
||||
& $signtool ($signArgs + @('/tr', 'http://timestamp.digicert.com', '/td', 'SHA256', $Path))
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warning "timestamped sign failed for $Path — retrying without a timestamp"
|
||||
& $signtool ($signArgs + @($Path))
|
||||
if ($LASTEXITCODE -ne 0) { throw "signtool sign failed for $Path ($LASTEXITCODE)" }
|
||||
}
|
||||
}
|
||||
|
||||
# --- sign the inner exe before it's packed ----------------------------------------------------
|
||||
Sign-File $exe
|
||||
|
||||
# --- stage the SudoVDA driver bundle ----------------------------------------------------------
|
||||
$defines = @("/DMyAppVersion=$Version", "/DBinDir=$TargetDir", "/DOutputDir=$OutDir")
|
||||
if (-not $NoDriver) {
|
||||
$stage = Join-Path $OutDir 'stage'
|
||||
& (Join-Path $here 'fetch-sudovda.ps1') -OutDir $stage
|
||||
Copy-Item (Join-Path $here 'install-sudovda.ps1') (Join-Path $stage 'install-sudovda.ps1') -Force
|
||||
$defines += "/DStageDir=$stage"
|
||||
}
|
||||
else { Write-Host "-NoDriver: building installer WITHOUT the bundled SudoVDA driver" }
|
||||
|
||||
# --- build the installer ----------------------------------------------------------------------
|
||||
Write-Host "==> ISCC $($defines -join ' ') $iss"
|
||||
& $iscc @defines $iss
|
||||
if ($LASTEXITCODE -ne 0) { throw "ISCC failed ($LASTEXITCODE)" }
|
||||
|
||||
$setup = Join-Path $OutDir "punktfunk-host-setup-$Version.exe"
|
||||
if (-not (Test-Path $setup)) { throw "expected installer not produced: $setup" }
|
||||
|
||||
# --- sign the setup.exe + clean up ------------------------------------------------------------
|
||||
Sign-File $setup
|
||||
Remove-Item $pfxPath -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "==> installer: $setup"
|
||||
if (-not $NoSign) {
|
||||
Write-Host "==> trust the cert once per machine (self-signed builds), then the signed setup.exe is trusted:"
|
||||
Write-Host " Import-Certificate -FilePath '$cerPath' -CertStoreLocation Cert:\LocalMachine\TrustedPublisher"
|
||||
}
|
||||
if ($env:GITHUB_ENV) {
|
||||
"HOST_SETUP_PATH=$setup" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||
if (-not $NoSign) { "HOST_CER_PATH=$cerPath" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 }
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
; punktfunk host installer (Inno Setup 6).
|
||||
;
|
||||
; Produces a signed setup.exe that lays the host into Program Files, optionally installs the bundled
|
||||
; SudoVDA virtual-display driver, and DELEGATES service registration to `punktfunk-host service
|
||||
; install`. The real, idempotent install logic (SCM registration, firewall rules, default host.env,
|
||||
; the SYSTEM→interactive-session CreateProcessAsUserW supervisor for secure-desktop capture) lives in
|
||||
; crates/punktfunk-host/src/service.rs — this script does NOT duplicate it. That SYSTEM service model
|
||||
; is exactly why MSIX is unusable here and we ship a classic elevated installer instead.
|
||||
;
|
||||
; Built by pack-host-installer.ps1, e.g.:
|
||||
; ISCC.exe /DMyAppVersion=0.2.123 /DBinDir=C:\t\release /DStageDir=C:\t\out\stage \
|
||||
; /DOutputDir=C:\t\out packaging\windows\punktfunk-host.iss
|
||||
; Omit /DStageDir to build an installer WITHOUT the bundled driver (driver becomes a prerequisite).
|
||||
|
||||
#ifndef MyAppVersion
|
||||
#define MyAppVersion "0.0.0"
|
||||
#endif
|
||||
#ifndef BinDir
|
||||
#define BinDir "."
|
||||
#endif
|
||||
#ifndef OutputDir
|
||||
#define OutputDir "."
|
||||
#endif
|
||||
; StageDir (the staged SudoVDA payload + nefconc.exe + install-sudovda.ps1) is optional.
|
||||
#ifdef StageDir
|
||||
#define WithDriver
|
||||
#endif
|
||||
|
||||
[Setup]
|
||||
AppId={{7C9E6A52-1F4B-4E8D-A3C7-2B5D8F1E0A93}
|
||||
AppName=punktfunk host
|
||||
AppVersion={#MyAppVersion}
|
||||
AppPublisher=unom
|
||||
AppPublisherURL=https://git.unom.io/unom/punktfunk
|
||||
DefaultDirName={autopf}\punktfunk
|
||||
DefaultGroupName=punktfunk
|
||||
DisableProgramGroupPage=yes
|
||||
UsePreviousAppDir=yes
|
||||
PrivilegesRequired=admin
|
||||
MinVersion=10.0
|
||||
ArchitecturesAllowed=x64
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
OutputDir={#OutputDir}
|
||||
OutputBaseFilename=punktfunk-host-setup-{#MyAppVersion}
|
||||
Compression=lzma2/max
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
UninstallDisplayName=punktfunk host {#MyAppVersion}
|
||||
UninstallDisplayIcon={app}\punktfunk-host.exe
|
||||
|
||||
[Tasks]
|
||||
#ifdef WithDriver
|
||||
Name: "installdriver"; Description: "Install the SudoVDA virtual display driver (required for native-resolution streaming)"
|
||||
#endif
|
||||
Name: "startservice"; Description: "Start the punktfunk host service now (also starts on every boot)"
|
||||
|
||||
[Files]
|
||||
Source: "{#BinDir}\punktfunk-host.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#SourcePath}..\..\scripts\windows\host.env.example"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#SourcePath}README.md"; DestDir: "{app}"; DestName: "README.txt"; Flags: ignoreversion
|
||||
#ifdef WithDriver
|
||||
; The driver payload + nefconc.exe + install-sudovda.ps1, extracted to {tmp} and removed after install.
|
||||
Source: "{#StageDir}\*"; DestDir: "{tmp}\sudovda"; Flags: deleteafterinstall recursesubdirs createallsubdirs; Tasks: installdriver
|
||||
#endif
|
||||
|
||||
[Run]
|
||||
#ifdef WithDriver
|
||||
Filename: "powershell.exe"; \
|
||||
Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{tmp}\sudovda\install-sudovda.ps1"" -Dir ""{tmp}\sudovda"""; \
|
||||
StatusMsg: "Installing the SudoVDA virtual display driver..."; \
|
||||
Flags: runhidden waituntilterminated; Tasks: installdriver
|
||||
#endif
|
||||
; Register (or re-point, on upgrade — idempotent) the SYSTEM service from its FINAL {app} location:
|
||||
; service install records current_exe() as the SCM binPath, so it must run from {app}, not {tmp}.
|
||||
Filename: "{app}\punktfunk-host.exe"; Parameters: "service install"; WorkingDir: "{app}"; \
|
||||
StatusMsg: "Registering the punktfunk host service..."; Flags: runhidden waituntilterminated
|
||||
Filename: "{app}\punktfunk-host.exe"; Parameters: "service start"; WorkingDir: "{app}"; \
|
||||
StatusMsg: "Starting the punktfunk host service..."; Flags: runhidden waituntilterminated; Tasks: startservice
|
||||
|
||||
[UninstallRun]
|
||||
Filename: "{app}\punktfunk-host.exe"; Parameters: "service uninstall"; Flags: runhidden waituntilterminated; RunOnceId: "PunktfunkHostServiceUninstall"
|
||||
|
||||
[Code]
|
||||
{ On upgrade the running service locks punktfunk-host.exe (and the supervisor would respawn it from
|
||||
the OLD binary), so stop it and WAIT for STOPPED before files are copied. Best-effort; a fresh
|
||||
install is a no-op (the service doesn't exist yet). }
|
||||
procedure StopHostServiceAndWait;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Exec('powershell.exe',
|
||||
'-NoProfile -ExecutionPolicy Bypass -Command "' +
|
||||
'$ErrorActionPreference=''SilentlyContinue''; ' +
|
||||
'$s=Get-Service -Name ''PunktfunkHost''; ' +
|
||||
'if($s -and $s.Status -ne ''Stopped''){Stop-Service -Name ''PunktfunkHost'' -Force; ' +
|
||||
'try{$s.WaitForStatus(''Stopped'',[TimeSpan]::FromSeconds(30))}catch{}}"',
|
||||
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
if CurStep = ssInstall then
|
||||
StopHostServiceAndWait;
|
||||
end;
|
||||
@@ -1,4 +1,4 @@
|
||||
# Provision this Windows box as the Gitea Actions runner for the Windows client CI/packaging.
|
||||
# Provision this Windows box as the Gitea Actions runner for the Windows client + host CI/packaging.
|
||||
# The Windows analogue of scripts/ci/setup-macos-runner.sh. Idempotent — safe to re-run. Run
|
||||
# ELEVATED (admin) on the box, e.g. over SSH:
|
||||
#
|
||||
@@ -84,6 +84,16 @@ if (-not (Test-Path "C:\Users\Public\.rustup\settings.toml")) {
|
||||
robocopy "$env:USERPROFILE\.rustup" "C:\Users\Public\.rustup" /E /NFL /NDL /NJH /NJS /MT:16 | Out-Null
|
||||
}
|
||||
|
||||
# Inno Setup (ISCC.exe) for the host installer build (windows-host.yml). pack-host-installer.ps1
|
||||
# locates it at its fixed Program Files path, so it need not be on PATH — just present.
|
||||
if (-not (Test-Path "C:\Program Files (x86)\Inno Setup 6\ISCC.exe")) {
|
||||
if (Get-Command choco -ErrorAction SilentlyContinue) {
|
||||
Write-Host "==> installing Inno Setup (ISCC)"
|
||||
choco install innosetup -y --no-progress
|
||||
}
|
||||
else { Write-Warning "Inno Setup not found and choco unavailable - install it for windows-host.yml." }
|
||||
}
|
||||
|
||||
# --- daemon env wrapper (the box's MSVC/WinUI/FFmpeg toolchain) ---
|
||||
$wrapper = "$RunnerHome\run-runner.ps1"
|
||||
@'
|
||||
|
||||
Reference in New Issue
Block a user