b54f781524
apple / swift (push) Successful in 55s
ci / rust (push) Successful in 1m18s
ci / web (push) Successful in 36s
ci / docs-site (push) Successful in 1m1s
android / android (push) Successful in 3m24s
deb / build-publish (push) Successful in 2m37s
decky / build-publish (push) Successful in 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
ci / bench (push) Successful in 4m44s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m25s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m19s
docker / deploy-docs (push) Successful in 17s
windows-host / package (push) Has been cancelled
The first windows-host run with the bundled console failed at "bun not found": the self-hosted runner executes as SYSTEM, so the dev user's bun (and its ~/.npmrc with the @unom registry token) aren't on PATH. Make the web-build step self-sufficient: - Install bun via bun.sh/install.ps1 when it isn't already present (checking PATH + the SYSTEM/Public profile locations first), like deb.yml bootstraps it. - Write the private @unom registry mapping + auth token (REGISTRY_TOKEN) into the SYSTEM home .npmrc so `bun install` can fetch the @unom packages — kept out of the project tree and the shipped .output bundle (.output\server\.npmrc stays mapping-only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
220 lines
12 KiB
YAML
220 lines
12 KiB
YAML
# 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 + the web management console, run by a scheduled task on a bundled
|
|
# Node) 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) — single project version:
|
|
# vX.Y.Z tag -> X.Y.Z (THE release; published + stable `latest/` alias + attached to the
|
|
# unified Gitea Release).
|
|
# main push / dispatch -> 0.3.<run_number> (canary; `canary/` alias; climbs 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.
|
|
#
|
|
# GPU backends: the host builds with --features nvenc,amf-qsv = all three vendors in one installer.
|
|
# - NVENC (NVIDIA, direct SDK): the only link need is nvencodeapi.lib, synthesised from a 2-export
|
|
# .def with llvm-dlltool (no GPU/SDK at build time).
|
|
# - AMF/QSV (AMD/Intel, libavcodec): link-imports the FFmpeg libs from FFMPEG_DIR (the BtbN gpl-shared
|
|
# tree the client uses; includes the *_amf/*_qsv encoders) and bundles its DLLs into the installer.
|
|
# CI never launches the exe, so no GPU is needed here — this is build + Windows clippy coverage only.
|
|
name: windows-host
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'crates/punktfunk-host/**'
|
|
- 'crates/punktfunk-core/**'
|
|
- 'packaging/windows/**'
|
|
- 'scripts/windows/**'
|
|
- 'web/**'
|
|
- 'Cargo.lock'
|
|
- 'Cargo.toml'
|
|
- '.gitea/workflows/windows-host.yml'
|
|
tags: ['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
|
|
# FFMPEG_DIR: the same BtbN gpl-shared x64 tree the Windows CLIENT links against (provisioned
|
|
# by scripts/ci/setup-windows-runner.ps1). The host's AMD/Intel AMF/QSV encode backend
|
|
# (--features amf-qsv) link-imports avcodec/avutil/swscale from it; pack-host-installer.ps1
|
|
# then bundles its bin\*.dll into the installer. LIBCLANG_PATH is in the runner daemon env.
|
|
if (-not $env:FFMPEG_DIR) {
|
|
"FFMPEG_DIR=C:\Users\Public\ffmpeg" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
|
}
|
|
$v = if ($env:GITHUB_REF -like 'refs/tags/v*') {
|
|
$env:GITHUB_REF_NAME -replace '^v', ''
|
|
} else {
|
|
"0.3.$($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 + amf-qsv)
|
|
shell: pwsh
|
|
# All-vendor host: NVENC (NVIDIA, direct SDK) + AMF/QSV (AMD/Intel, libavcodec via FFMPEG_DIR).
|
|
run: cargo build --release -p punktfunk-host --features nvenc,amf-qsv
|
|
|
|
- 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,amf-qsv -- -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: Fetch portable Node runtime (bundled to run the console)
|
|
shell: pwsh
|
|
run: |
|
|
# The installer ships a self-contained node.exe so the web console runs with no system-Node
|
|
# prerequisite. Pinned LTS (>= 20, matching the punktfunk-web .deb's `nodejs (>= 20)`); the
|
|
# smoke test below validates the .output bundle runs under exactly this node before shipping.
|
|
$ver = 'v22.11.0'
|
|
$url = "https://nodejs.org/dist/$ver/node-$ver-win-x64.zip"
|
|
New-Item -ItemType Directory -Force -Path C:\t | Out-Null
|
|
$zip = 'C:\t\node.zip'; $dst = 'C:\t\nodedist'
|
|
Invoke-WebRequest -Uri $url -OutFile $zip
|
|
if (Test-Path $dst) { Remove-Item $dst -Recurse -Force }
|
|
Expand-Archive -Path $zip -DestinationPath $dst -Force
|
|
$node = (Get-ChildItem -Path $dst -Recurse -Filter node.exe | Select-Object -First 1).FullName
|
|
if (-not $node) { throw "node.exe not found in $url" }
|
|
"NODE_EXE=$node" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
|
& $node --version
|
|
|
|
- name: Build + smoke-boot web console (node-server preset)
|
|
shell: pwsh
|
|
env:
|
|
# PAT with read access to the unom org packages — the @unom npm registry needs auth.
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
# Same shape as deb.yml: bun builds the Nitro node-server bundle, node runs it; the installer
|
|
# bundles web\.output (handed over via WEB_OUTPUT_DIR) + the portable node above. The runner
|
|
# runs as SYSTEM (no dev-user PATH/npmrc), so bootstrap bun if absent and supply the private
|
|
# @unom registry token via the SYSTEM home .npmrc — kept OUT of the shipped bundle.
|
|
run: |
|
|
$bun = (Get-Command bun -ErrorAction SilentlyContinue).Source
|
|
if (-not $bun) { foreach ($p in @("$env:USERPROFILE\.bun\bin\bun.exe", 'C:\Users\Public\bun\bin\bun.exe')) { if (Test-Path $p) { $bun = $p; break } } }
|
|
if (-not $bun) {
|
|
Write-Output "bun not found - installing via bun.sh"
|
|
Invoke-RestMethod https://bun.sh/install.ps1 | Invoke-Expression
|
|
$bun = Join-Path $env:USERPROFILE '.bun\bin\bun.exe'
|
|
}
|
|
if (-not (Test-Path $bun)) { throw "bun unavailable (install failed?): $bun" }
|
|
& $bun --version
|
|
# @unom is a private Gitea npm registry. The committed web\.npmrc has only the registry
|
|
# mapping; put the mapping + auth token in the SYSTEM home .npmrc so the token never lands in
|
|
# the shipped bundle (.output\server\.npmrc stays the clean mapping-only copy).
|
|
if ($env:REGISTRY_TOKEN) {
|
|
$rc = Join-Path $env:USERPROFILE '.npmrc'
|
|
Add-Content -Path $rc -Value '@unom:registry=https://git.unom.io/api/packages/unom/npm/'
|
|
Add-Content -Path $rc -Value "//git.unom.io/api/packages/unom/npm/:_authToken=$env:REGISTRY_TOKEN"
|
|
}
|
|
Push-Location web
|
|
& $bun install --frozen-lockfile; if ($LASTEXITCODE) { throw "bun install failed ($LASTEXITCODE)" }
|
|
& $bun run build; if ($LASTEXITCODE) { throw "web build failed ($LASTEXITCODE)" }
|
|
if (Select-String -Path .output\server\index.mjs -Pattern 'Bun\.serve' -Quiet) {
|
|
throw "web build is a bun bundle (Bun.serve) - need the node-server preset"
|
|
}
|
|
# Externalized @unom SSR deps must be installed inside .output\server (registry mapping via .npmrc).
|
|
Copy-Item .npmrc .output\server\.npmrc -Force
|
|
Push-Location .output\server; & $bun install; if ($LASTEXITCODE) { throw ".output/server dep install failed ($LASTEXITCODE)" }; Pop-Location
|
|
Pop-Location
|
|
# Gate the installer on a real node boot serving /login (the runtime the installer ships).
|
|
$env:PORT = '3009'; $env:HOST = '127.0.0.1'; $env:PUNKTFUNK_UI_PASSWORD = 'ci'
|
|
$server = (Resolve-Path 'web\.output\server\index.mjs').Path
|
|
$p = Start-Process -FilePath $env:NODE_EXE -ArgumentList $server -PassThru -WindowStyle Hidden
|
|
Start-Sleep -Seconds 4
|
|
try { $code = (Invoke-WebRequest -Uri 'http://127.0.0.1:3009/login' -UseBasicParsing -TimeoutSec 10).StatusCode } catch { $code = 0 }
|
|
Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue
|
|
Write-Output "web console smoke: /login -> $code"
|
|
if ($code -ne 200) { throw "web console failed to boot under node" }
|
|
"WEB_OUTPUT_DIR=$((Resolve-Path 'web\.output').Path)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
|
|
|
- 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)" }
|
|
# Refresh the channel alias (delete-then-reupload, like flatpak.yml/decky.yml) for a
|
|
# predictable download URL: stable release -> `latest/`, canary main build -> `canary/`.
|
|
$alias = if ($env:GITHUB_REF -like 'refs/tags/v*') { 'latest' } else { 'canary' }
|
|
$aliasNames = @{ $env:HOST_SETUP_PATH = 'punktfunk-host-setup.exe'; $env:HOST_CER_PATH = 'punktfunk-host-windows.cer' }
|
|
foreach ($f in $files) {
|
|
$an = $aliasNames[$f]; if (-not $an) { continue }
|
|
curl.exe -fsS -o NUL --user "enricobuehler:$($env:REGISTRY_TOKEN)" -X DELETE "$base/$alias/$an" 2>$null
|
|
Publish-File $f "$base/$alias/$an"
|
|
}
|
|
|
|
# On a real release, also attach the signed installer (+ its .cer) to the unified Gitea Release.
|
|
- name: Attach host installer to the Gitea release (stable tags only)
|
|
if: startsWith(gitea.ref, 'refs/tags/v')
|
|
shell: pwsh
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
. scripts/ci/gitea-release.ps1
|
|
$rid = Ensure-GiteaRelease -Tag $env:GITHUB_REF_NAME -Name $env:GITHUB_REF_NAME -Prerelease 'auto'
|
|
foreach ($f in @($env:HOST_SETUP_PATH, $env:HOST_CER_PATH)) {
|
|
if ($f -and (Test-Path $f)) { Upsert-GiteaAsset -ReleaseId $rid -File $f }
|
|
}
|