feat(packaging/arch): Arch + SteamOS install target (PKGBUILD + sysext)
ci / web (push) Successful in 27s
ci / docs-site (push) Successful in 31s
apple / swift (push) Successful in 1m17s
ci / rust (push) Successful in 2m8s
ci / bench (push) Successful in 1m35s
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 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
deb / build-publish (push) Successful in 2m17s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 4m48s
docker / deploy-docs (push) Successful in 18s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 4m22s
ci / web (push) Successful in 27s
ci / docs-site (push) Successful in 31s
apple / swift (push) Successful in 1m17s
ci / rust (push) Successful in 2m8s
ci / bench (push) Successful in 1m35s
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 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
deb / build-publish (push) Successful in 2m17s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 4m48s
docker / deploy-docs (push) Successful in 18s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 4m22s
Add packaging/arch: a PKGBUILD mirroring the rpm/deb artifact set (binary, udev rule, 32MB sysctl, systemd USER units with ExecStart rewritten, headless helpers, env templates, openapi), a pacman .install scriptlet, a systemd-sysext builder for immutable SteamOS, and a README. Builds the working tree via PF_SRCDIR (CI/dev) or a git tag (AUR). Arch's stock ffmpeg already ships NVENC, so deps collapse to ~10 packages with nvidia-utils/compositors as optdepends (never hard-depend on the driver, same invariant as rpm/deb). SteamOS delivery is a **systemd-sysext** (overlays /usr read-only from writable /var/lib/extensions/, survives A/B OS updates, no steamos-readonly disable) — pacman/distrobox/flatpak are all unsuitable for a host that needs uinput/uhid, the host PipeWire socket, the GPU node, and to spawn a compositor. KNOWN GAP, documented prominently: encode is NVENC-only (src/encode/linux.rs has no VAAPI backend), so this works on Arch+NVIDIA (and bazzite-deck-nvidia) but an AMD Steam Deck installs yet cannot encode until a hevc_vaapi backend is written — a code change, not packaging. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
|||||||
|
# Maintainer: unom <noreply@anthropic.com>
|
||||||
|
#
|
||||||
|
# Arch Linux / SteamOS package for the punktfunk streaming HOST. Mirrors the artifact
|
||||||
|
# set of packaging/rpm/punktfunk.spec and packaging/debian/build-deb.sh exactly.
|
||||||
|
#
|
||||||
|
# Two build modes:
|
||||||
|
# - AUR / standalone: makepkg in this dir (fetches the git tag below).
|
||||||
|
# - In-tree / CI: PF_SRCDIR=$(git rev-parse --show-toplevel) makepkg --holdver
|
||||||
|
# (builds the working tree instead of the tagged source — see build()).
|
||||||
|
#
|
||||||
|
# IMPORTANT: encode is NVENC-only (crates/punktfunk-host/src/encode/linux.rs). This package is
|
||||||
|
# functional on NVIDIA hosts (nvidia-utils + Arch's NVENC-enabled ffmpeg). On an AMD Steam Deck
|
||||||
|
# it installs but CANNOT encode until a VAAPI (hevc_vaapi) backend exists — see packaging/arch/README.md.
|
||||||
|
pkgname=punktfunk-host
|
||||||
|
pkgver=0.0.1
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="Low-latency desktop/game streaming host (Moonlight-compatible + punktfunk/1)"
|
||||||
|
arch=('x86_64')
|
||||||
|
url="https://git.unom.io/unom/punktfunk"
|
||||||
|
license=('MIT OR Apache-2.0')
|
||||||
|
|
||||||
|
# Runtime libs the binary links or drives. NVENC + GPU EGL/CUDA come from the NVIDIA driver
|
||||||
|
# (nvidia-utils) — kept an optdepend, never a hard dep, exactly as the RPM (__requires_exclude
|
||||||
|
# libcuda) and deb (shlibdeps grep-filter) do: the driver is installed out of band.
|
||||||
|
depends=(
|
||||||
|
'ffmpeg' # libavcodec/format/util — Arch's ffmpeg ships hevc_nvenc/av1_nvenc
|
||||||
|
'pipewire' # capture + audio transport
|
||||||
|
'pipewire-pulse' # desktop audio bridge
|
||||||
|
'wireplumber' # PipeWire session manager
|
||||||
|
'opus' # GameStream/punktfunk Opus audio
|
||||||
|
'libei' # input injection (RemoteDesktop portal / EIS)
|
||||||
|
'mesa' # libgbm (+ EGL on non-NVIDIA)
|
||||||
|
'libglvnd' # libGL/libEGL loader
|
||||||
|
'libxkbcommon' # virtual keyboard keymap
|
||||||
|
'wayland' # wayland-client (vdisplay backends)
|
||||||
|
)
|
||||||
|
optdepends=(
|
||||||
|
'nvidia-utils: NVENC hardware encode + GPU EGL/CUDA zero-copy (REQUIRED to encode on NVIDIA)'
|
||||||
|
'gamescope: per-session nested compositor backend (no desktop login needed) — needs >=3.16.22'
|
||||||
|
'kwin: stream a KDE Plasma desktop (kwin VirtualDisplay backend)'
|
||||||
|
'mutter: stream a GNOME desktop (Mutter RecordVirtual backend)'
|
||||||
|
'sway: stream a wlroots desktop (Sway VirtualDisplay backend)'
|
||||||
|
'xdg-desktop-portal-kde: portal for the headless KDE session helper'
|
||||||
|
'xdg-desktop-portal-wlr: portal for the headless Sway session helper'
|
||||||
|
)
|
||||||
|
makedepends=('rust' 'cargo' 'clang' 'cmake' 'nasm' 'pkgconf' 'git')
|
||||||
|
|
||||||
|
# AUR source (a tagged release). For an in-tree CI build, set PF_SRCDIR to the repo root and
|
||||||
|
# build() uses it instead; see the README.
|
||||||
|
source=("git+https://git.unom.io/unom/punktfunk.git#tag=v${pkgver}")
|
||||||
|
sha256sums=('SKIP')
|
||||||
|
install="${pkgname}.install"
|
||||||
|
|
||||||
|
_repo() { printf '%s' "${PF_SRCDIR:-$srcdir/punktfunk}"; }
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "$(_repo)"
|
||||||
|
export RUSTUP_TOOLCHAIN=stable CARGO_TARGET_DIR="$srcdir/target"
|
||||||
|
# The zero-copy FFI link-needs libcuda at build time; nvidia-utils provides it on an NVIDIA
|
||||||
|
# builder. On a GPU-less builder, symlink the CUDA stub into the link path first (same caveat
|
||||||
|
# the RPM documents): ln -s "$(find / -name libcuda.so -path '*stubs*' | head -1)" /usr/lib/
|
||||||
|
cargo build --release --locked -p punktfunk-host
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
local R; R="$(_repo)"; local T="$srcdir/target/release"
|
||||||
|
|
||||||
|
install -Dm0755 "$T/punktfunk-host" "$pkgdir/usr/bin/punktfunk-host"
|
||||||
|
|
||||||
|
# /dev/uinput + /dev/uhid -> input group (virtual gamepads + DualSense UHID)
|
||||||
|
install -Dm0644 "$R/scripts/60-punktfunk.rules" "$pkgdir/usr/lib/udev/rules.d/60-punktfunk.rules"
|
||||||
|
# 32 MB UDP socket buffers (send-side headroom at high bitrate)
|
||||||
|
install -Dm0644 "$R/scripts/99-punktfunk-net.conf" "$pkgdir/usr/lib/sysctl.d/99-punktfunk-net.conf"
|
||||||
|
|
||||||
|
# systemd USER units (the host runs in the graphical session, not as root); the source units
|
||||||
|
# point at the dev tree — repoint ExecStart at the installed paths (mirrors rpm/deb).
|
||||||
|
install -Dm0644 "$R/scripts/punktfunk-host.service" "$pkgdir/usr/lib/systemd/user/punktfunk-host.service"
|
||||||
|
sed -i 's#%h/punktfunk/target/release/punktfunk-host#/usr/bin/punktfunk-host#' \
|
||||||
|
"$pkgdir/usr/lib/systemd/user/punktfunk-host.service"
|
||||||
|
install -Dm0644 "$R/scripts/punktfunk-kde-session.service" "$pkgdir/usr/lib/systemd/user/punktfunk-kde-session.service" 2>/dev/null || true
|
||||||
|
[ -f "$pkgdir/usr/lib/systemd/user/punktfunk-kde-session.service" ] && \
|
||||||
|
sed -i 's#%h/punktfunk/scripts/headless/run-headless-kde.sh#/usr/share/punktfunk/headless/run-headless-kde.sh#' \
|
||||||
|
"$pkgdir/usr/lib/systemd/user/punktfunk-kde-session.service"
|
||||||
|
|
||||||
|
# headless session helpers + env templates + OpenAPI doc
|
||||||
|
install -Dm0755 "$R/scripts/headless/run-headless-kde.sh" "$pkgdir/usr/share/punktfunk/headless/run-headless-kde.sh"
|
||||||
|
install -Dm0755 "$R/scripts/headless/run-headless-sway.sh" "$pkgdir/usr/share/punktfunk/headless/run-headless-sway.sh"
|
||||||
|
install -Dm0644 "$R/scripts/headless/kde-authorized" "$pkgdir/usr/share/punktfunk/headless/kde-authorized"
|
||||||
|
install -Dm0644 "$R/scripts/headless/punktfunk-sink.conf" "$pkgdir/usr/share/punktfunk/headless/punktfunk-sink.conf"
|
||||||
|
install -Dm0644 "$R/scripts/host.env.example" "$pkgdir/usr/share/punktfunk/host.env.example"
|
||||||
|
install -Dm0644 "$R/packaging/bazzite/host.env" "$pkgdir/usr/share/punktfunk/host.env.bazzite"
|
||||||
|
install -Dm0644 "$R/packaging/kde/host.env" "$pkgdir/usr/share/punktfunk/host.env.kde"
|
||||||
|
install -Dm0644 "$R/docs/api/openapi.json" "$pkgdir/usr/share/punktfunk/openapi.json"
|
||||||
|
|
||||||
|
install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/$pkgname/LICENSE-MIT"
|
||||||
|
install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE-APACHE"
|
||||||
|
install -Dm0644 "$R/README.md" "$pkgdir/usr/share/doc/$pkgname/README.md"
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# punktfunk on Arch Linux / SteamOS
|
||||||
|
|
||||||
|
Packaging for the punktfunk streaming **host** on Arch and Arch-derived immutable distros
|
||||||
|
(SteamOS 3, etc.). Mirrors the artifact set of `packaging/rpm/punktfunk.spec` and
|
||||||
|
`packaging/debian/build-deb.sh`.
|
||||||
|
|
||||||
|
> ⚠️ **Encode is NVENC-only today.** `crates/punktfunk-host/src/encode/linux.rs` implements
|
||||||
|
> `hevc_nvenc`/`av1_nvenc`/`h264_nvenc` and a CUDA zero-copy path — there is **no VAAPI backend**.
|
||||||
|
> So this package is functional on **Arch + NVIDIA** (the realistic target). On an **AMD Steam
|
||||||
|
> Deck it installs but cannot encode** until a `hevc_vaapi`/`av1_vaapi` encoder is added in
|
||||||
|
> `src/encode/` — a code change, not a packaging one. (`bazzite-deck-nvidia`, i.e. SteamOS-style
|
||||||
|
> images running on NVIDIA hardware, work fine — they're NVIDIA.)
|
||||||
|
|
||||||
|
## Arch Linux (mutable)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd packaging/arch
|
||||||
|
# Build the working tree (CI / dev) — no git fetch:
|
||||||
|
PF_SRCDIR="$(git rev-parse --show-toplevel)" makepkg -f --holdver
|
||||||
|
# …or build the tagged release the AUR way:
|
||||||
|
makepkg -si
|
||||||
|
```
|
||||||
|
Then the standard first-run (printed by the install scriptlet):
|
||||||
|
```sh
|
||||||
|
sudo usermod -aG input "$USER" # virtual gamepads; re-login after
|
||||||
|
mkdir -p ~/.config/punktfunk
|
||||||
|
cp /usr/share/punktfunk/host.env.bazzite ~/.config/punktfunk/host.env # gamescope backend
|
||||||
|
systemctl --user enable --now punktfunk-host
|
||||||
|
```
|
||||||
|
NVENC/EGL come from the NVIDIA driver: `sudo pacman -S --needed nvidia-utils`. Arch's stock
|
||||||
|
`ffmpeg` already has NVENC built in — no RPM-Fusion-style swap needed (unlike Fedora).
|
||||||
|
|
||||||
|
### Runtime dependency map (Fedora/Debian → Arch)
|
||||||
|
|
||||||
|
| Need | Arch package |
|
||||||
|
|------|--------------|
|
||||||
|
| FFmpeg + NVENC | `ffmpeg` (NVENC built in) |
|
||||||
|
| PipeWire + Pulse + session mgr | `pipewire` `pipewire-pulse` `wireplumber` |
|
||||||
|
| Opus / input injection | `opus` `libei` |
|
||||||
|
| GL/EGL + gbm + xkb + wayland | `libglvnd` `mesa` `libxkbcommon` `wayland` |
|
||||||
|
| NVIDIA driver (NVENC/EGL/CUDA) | `nvidia-utils` *(optdepend — never a hard dep)* |
|
||||||
|
| Compositor backends | `gamescope` (≥3.16.22) / `kwin` / `mutter` / `sway` *(optdepends)* |
|
||||||
|
|
||||||
|
## SteamOS 3 (immutable) — use a systemd-sysext
|
||||||
|
|
||||||
|
SteamOS has a **read-only `/usr` on A/B partitions**, and every OS update reimages the rootfs —
|
||||||
|
so `steamos-readonly disable` + `pacman` (and flatpak/distrobox) are fragile or unusable for a
|
||||||
|
host that needs `/dev/uinput`, `/dev/uhid`, the host PipeWire socket, the GPU render node, and the
|
||||||
|
right to spawn a compositor. The update-survivable, SteamOS-blessed mechanism is a
|
||||||
|
**systemd-sysext**: an overlay image merged read-only over `/usr` at boot, living in the writable
|
||||||
|
`/var/lib/extensions/` (so it persists across A/B updates, no readonly-disable).
|
||||||
|
|
||||||
|
Build the package, then wrap its `/usr` payload into a sysext image:
|
||||||
|
```sh
|
||||||
|
# 1. build the pacman package (needs an Arch environment / container)
|
||||||
|
cd packaging/arch && PF_SRCDIR="$(git rev-parse --show-toplevel)" makepkg -f --holdver
|
||||||
|
# 2. turn it into a sysext .raw (extracts the package's /usr into an image + extension-release)
|
||||||
|
bash build-sysext.sh punktfunk-host-*.pkg.tar.zst
|
||||||
|
# 3. on the SteamOS box:
|
||||||
|
sudo cp punktfunk-host.raw /var/lib/extensions/
|
||||||
|
sudo systemctl enable --now systemd-sysext # merges it; survives OS updates
|
||||||
|
systemctl --user enable --now punktfunk-host # the user unit is now under /usr/lib
|
||||||
|
```
|
||||||
|
The udev rule, sysctl, and systemd **user** unit all live under `/usr/lib`, so the merged sysext
|
||||||
|
exposes them. `systemd-sysext refresh` re-merges after a reboot.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
- `PKGBUILD` — the package recipe (builds the working tree via `PF_SRCDIR`, or a git tag for AUR).
|
||||||
|
- `punktfunk-host.install` — pacman scriptlet (udev reload + sysctl + first-run hint), mirrors RPM `%post`.
|
||||||
|
- `build-sysext.sh` — wraps a built `.pkg.tar.zst` into a `systemd-sysext` `.raw` for SteamOS.
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Wrap a built punktfunk-host pacman package into a systemd-sysext image — the update-survivable
|
||||||
|
# way to add the host to an immutable Arch-derived distro (SteamOS 3): the .raw overlays /usr
|
||||||
|
# read-only from the writable /var/lib/extensions/, so it persists across A/B OS updates with no
|
||||||
|
# `steamos-readonly disable`. Needs `bsdtar`/`tar`, `squashfs-tools` (mksquashfs).
|
||||||
|
#
|
||||||
|
# Usage: bash build-sysext.sh <punktfunk-host-*.pkg.tar.zst>
|
||||||
|
# Output: punktfunk-host.raw
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PKG="${1:?usage: build-sysext.sh <punktfunk-host-*.pkg.tar.zst>}"
|
||||||
|
[ -f "$PKG" ] || { echo "no such package: $PKG" >&2; exit 1; }
|
||||||
|
NAME=punktfunk-host
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
|
|
||||||
|
# A pacman package is a (zstd) tarball; a sysext only carries /usr (the host /etc, /var are the
|
||||||
|
# system's). Extract just usr/ from the payload.
|
||||||
|
if command -v bsdtar >/dev/null 2>&1; then
|
||||||
|
bsdtar -C "$STAGE" -xf "$PKG" usr
|
||||||
|
else
|
||||||
|
tar -C "$STAGE" -xf "$PKG" usr
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The marker systemd-sysext requires to merge the image. ID=_any merges onto ANY host os-release
|
||||||
|
# (SteamOS, Arch, Bazzite); ARCHITECTURE pins it to x86-64 so it's never merged on the wrong arch.
|
||||||
|
install -d "$STAGE/usr/lib/extension-release.d"
|
||||||
|
cat > "$STAGE/usr/lib/extension-release.d/extension-release.$NAME" <<EOF
|
||||||
|
ID=_any
|
||||||
|
ARCHITECTURE=x86-64
|
||||||
|
EOF
|
||||||
|
|
||||||
|
OUT="$NAME.raw"
|
||||||
|
rm -f "$OUT"
|
||||||
|
mksquashfs "$STAGE" "$OUT" -all-root -noappend -quiet
|
||||||
|
echo "built $OUT"
|
||||||
|
echo " install: sudo cp $OUT /var/lib/extensions/ && sudo systemctl enable --now systemd-sysext"
|
||||||
|
echo " then: systemctl --user enable --now $NAME"
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# pacman install scriptlet — mirrors the RPM %post / deb postinst.
|
||||||
|
post_install() {
|
||||||
|
udevadm control --reload-rules 2>/dev/null || true
|
||||||
|
udevadm trigger --subsystem-match=misc 2>/dev/null || true
|
||||||
|
# Apply the UDP socket-buffer tuning now (also auto-applied at boot by systemd-sysctl).
|
||||||
|
sysctl -p /usr/lib/sysctl.d/99-punktfunk-net.conf >/dev/null 2>&1 || true
|
||||||
|
cat <<'MSG'
|
||||||
|
punktfunk-host installed.
|
||||||
|
1. Add yourself to the 'input' group for virtual gamepads:
|
||||||
|
sudo usermod -aG input "$USER" # then re-login
|
||||||
|
2. Pick a backend config (gamescope is the no-desktop default on SteamOS/Deck):
|
||||||
|
mkdir -p ~/.config/punktfunk
|
||||||
|
cp /usr/share/punktfunk/host.env.bazzite ~/.config/punktfunk/host.env
|
||||||
|
3. Enable the host:
|
||||||
|
systemctl --user enable --now punktfunk-host
|
||||||
|
|
||||||
|
NOTE: encode is NVENC-only. Install 'nvidia-utils' on an NVIDIA host. An AMD Steam Deck is NOT
|
||||||
|
yet supported — it needs a VAAPI (hevc_vaapi) encoder backend (see packaging/arch/README.md).
|
||||||
|
MSG
|
||||||
|
}
|
||||||
|
|
||||||
|
post_upgrade() {
|
||||||
|
udevadm control --reload-rules 2>/dev/null || true
|
||||||
|
sysctl -p /usr/lib/sysctl.d/99-punktfunk-net.conf >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user