The docs already told users where to get it — the Bazzite sysext, an Arch package, a NixOS option — and none of the three were true. `rpm.yml` built the sysext without ever passing `--gamescope`, `arch.yml` did not know the PKGBUILD existed, and the nix derivation had never been evaluated once. Evaluating it found its central assumption wrong: `gamescope.unwrapped` does not exist on current nixpkgs, where `gamescope` IS the buildable derivation, so the override threw on every build. It now prefers `.unwrapped` where a future nixpkgs wraps it and checks the RESULT is patchable — `overrideAttrs` on a symlinkJoin succeeds and does nothing, which would install an UNPATCHED gamescope under a name the host reads as a promise of HDR. Both it and the PKGBUILD had also drifted to a stale patch list: two patches named where three exist, one of them under the level-1 filename retired when the cursor patch landed. Both read the patch directory now, so the list cannot go stale again. The PKGBUILD additionally delegates the whole build to `build-punktfunk-gamescope.sh` rather than re-deriving the meson invocation — its copy had already lost `force_fallback_for=wlroots`, and unlike Fedora 43, Arch ships wlroots, so that package would have linked it shared and shipped a binary that starts only on machines carrying the dev library. It asserts its own pinned rev matches the script's, the one thing makepkg needs statically. Both CI builds are cached on `packaging/gamescope/**`, which is the only reason this is affordable: that tree depends on nothing else in the repo, so a normal push restores a binary instead of spending ten minutes on someone else's C++. And both are best-effort. punktfunk works without this binary — SDR on the gamescope backend, which is what every release before this one did — so a hiccup building gamescope must not cost the packages those workflows exist to publish. A failure warns and is never cached, so the next run retries. `rpm.yml` also installs Fedora's own gamescope for its runtime libraries: the sysext verifies our binary by executing `--version`, and on a cache hit nothing else in the job would have pulled libavif/luajit/seatd in. Verified by evaluating and patch-phase-building the nix derivation against nixos-unstable: all three patches apply to nixpkgs' already-patched 3.16.25 tree, and the banner stamps +pfhdr2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
221 lines
12 KiB
YAML
221 lines
12 KiB
YAML
# Build the punktfunk-host RPM and publish it to Gitea's RPM package registry, so Bazzite /
|
|
# Fedora Atomic hosts layer + update it with rpm-ostree. Counterpart to deb.yml (apt). Runs in
|
|
# the Fedora 43 builder image (ci/fedora-rpm.Dockerfile) so the RPM's auto library Requires
|
|
# (libavcodec.so.NN, …) match the target's sonames.
|
|
#
|
|
# Registry (public, unom org), group "bazzite":
|
|
# repo file https://git.unom.io/api/packages/unom/rpm/bazzite.repo
|
|
# Box setup (once): see packaging/rpm/README.md
|
|
#
|
|
# REGISTRY_TOKEN: repo Actions secret, a PAT with write:package scope (shared with docker.yml).
|
|
name: rpm
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
# Single project version: a `vX.Y.Z` tag is THE release. main publishes to the `*-canary` rpm
|
|
# groups, tags to the base groups (`bazzite`/`fedora-44`) — separate repos, so the old
|
|
# version-shadow (a release outranking rolling builds in one group) is structurally gone.
|
|
tags: ['v*']
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
REGISTRY: git.unom.io
|
|
OWNER: unom
|
|
|
|
jobs:
|
|
build-publish:
|
|
runs-on: ubuntu-24.04
|
|
# One RPM per target whose ffmpeg soname must match (a binary RPM is soname-coupled to its
|
|
# base): Fedora 43 == Bazzite (libavcodec.so.61), Fedora 44 == the Fedora KDE spin (.so.62).
|
|
# Each builds in its matching builder image and publishes to its own registry group.
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- image: punktfunk-fedora-rpm # Fedora 43 == Bazzite base
|
|
group: bazzite
|
|
fedver: 43
|
|
- image: punktfunk-fedora44-rpm # Fedora 44 == Fedora KDE spin
|
|
group: fedora-44
|
|
fedver: 44
|
|
container:
|
|
image: git.unom.io/unom/${{ matrix.image }}:latest
|
|
timeout-minutes: 90
|
|
env:
|
|
CARGO_HOME: /usr/local/cargo
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
# rpmbuild + git archive need the checkout trusted; cache the crates download.
|
|
# The client link deps are also baked into the fedora-rpm image, but this job runs
|
|
# against the image from the PREVIOUS push (docker.yml bootstrap note) — keep it
|
|
# green across image changes; a no-op once the image has them.
|
|
- name: Prep
|
|
run: |
|
|
git config --global --add safe.directory "$PWD"
|
|
# vulkan-headers: the client's pf-ffvk crate runs bindgen over FFmpeg's
|
|
# libavutil/hwcontext_vulkan.h (#include <vulkan/vulkan.h>).
|
|
dnf -y install gtk4-devel libadwaita-devel SDL3-devel vulkan-headers
|
|
# sysext build (packaging/bazzite/build-sysext.sh): squashfs + SELinux labeling.
|
|
dnf -y install squashfs-tools cpio libselinux-utils selinux-policy-targeted
|
|
# Fedora's own gamescope, for its RUNTIME libraries only — never shipped, never run. The
|
|
# sysext folds in our punktfunk-gamescope and verifies it by executing `--version`, and
|
|
# on a cache hit (the common case) nothing else in this job would have pulled libavif /
|
|
# luajit / seatd / SDL2 in. Cheap, and it tracks gamescope's dep list for us.
|
|
dnf -y install gamescope || true
|
|
# bun builds the punktfunk-web console (--with web). Baked into the image; install it
|
|
# here too so the job stays green against the PREVIOUS image (docker.yml bootstrap note).
|
|
command -v bun >/dev/null || {
|
|
dnf -y install unzip
|
|
curl -fsSL https://bun.sh/install | bash
|
|
install -m0755 "$HOME/.bun/bin/bun" /usr/local/bin/bun
|
|
}
|
|
bun --version
|
|
- uses: actions/cache@v4
|
|
with:
|
|
path: /usr/local/cargo/registry
|
|
key: cargo-home-${{ hashFiles('Cargo.lock') }}
|
|
restore-keys: cargo-home-
|
|
|
|
- name: Version + channel
|
|
# vX.Y.Z tag -> X.Y.Z-1 in the base group (a real release); main push -> <next-minor>-0.ciN.g<sha>
|
|
# in the `<base>-canary` group, whose "0." release sorts below the eventual <next-minor>-1 yet
|
|
# climbs by run number. The canary base is derived one minor ahead of the latest stable tag
|
|
# (scripts/ci/pf-version.sh) so a stable->canary box re-point still moves forward. The spec %build stamps
|
|
# PUNKTFUNK_BUILD_VERSION from these macros into the binary (--version provenance).
|
|
run: |
|
|
eval "$(bash scripts/ci/pf-version.sh)" # -> PF_BASE (one minor ahead of the latest stable tag)
|
|
SHORT=$(echo "$GITHUB_SHA" | cut -c1-8)
|
|
case "$GITHUB_REF" in
|
|
refs/tags/v*) V="${GITHUB_REF_NAME#v}"; R="1"; GROUP="${{ matrix.group }}" ;;
|
|
*) V="$PF_BASE"; R="0.ci${GITHUB_RUN_NUMBER}.g${SHORT}"; GROUP="${{ matrix.group }}-canary" ;;
|
|
esac
|
|
echo "PF_VERSION=$V" >> "$GITHUB_ENV"
|
|
echo "PF_RELEASE=$R" >> "$GITHUB_ENV"
|
|
echo "GROUP=$GROUP" >> "$GITHUB_ENV"
|
|
echo "rpm $V-$R -> group '$GROUP'"
|
|
|
|
- name: Build RPM
|
|
# PF_WITH_WEB=1 / PF_WITH_SCRIPTING=1 → also build the punktfunk-web console + the
|
|
# punktfunk-scripting runner subpackages (the publish loop globs them in; the host RPM
|
|
# Recommends both). Both need bun (ensured in Prep).
|
|
run: PF_VERSION="$PF_VERSION" PF_RELEASE="$PF_RELEASE" PF_WITH_WEB=1 PF_WITH_SCRIPTING=1 bash packaging/rpm/build-rpm.sh
|
|
|
|
- name: Sign RPMs (dormant until RPM_GPG_PRIVATE_KEY is set — see packaging/rpm/README.md)
|
|
env:
|
|
RPM_GPG_PRIVATE_KEY: ${{ secrets.RPM_GPG_PRIVATE_KEY }}
|
|
RPM_GPG_PASSPHRASE: ${{ secrets.RPM_GPG_PASSPHRASE }}
|
|
run: bash packaging/rpm/sign-rpms.sh
|
|
|
|
- name: Publish to the Gitea RPM registry
|
|
env:
|
|
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
# Publish only the main package (skip -debuginfo/-debugsource subpackages).
|
|
for rpm in dist/*.rpm; do
|
|
case "$rpm" in *debuginfo*|*debugsource*) echo "skip $rpm"; continue;; esac
|
|
echo "uploading $rpm"
|
|
# A re-tagged release re-fires this workflow and the rpm registry 409s on duplicate
|
|
# package versions — delete any prior copy of this exact name/version-release/arch
|
|
# first (404 on the first publish is fine).
|
|
NAME=$(rpm -qp --qf '%{NAME}' "$rpm" 2>/dev/null)
|
|
VR=$(rpm -qp --qf '%{VERSION}-%{RELEASE}' "$rpm" 2>/dev/null)
|
|
ARCH=$(rpm -qp --qf '%{ARCH}' "$rpm" 2>/dev/null)
|
|
curl -fsS -o /dev/null --user "enricobuehler:$TOKEN" -X DELETE \
|
|
"https://$REGISTRY/api/packages/$OWNER/rpm/$GROUP/package/$NAME/$VR/$ARCH" || true
|
|
curl -fsS --user "enricobuehler:$TOKEN" --upload-file "$rpm" \
|
|
"https://$REGISTRY/api/packages/$OWNER/rpm/$GROUP/upload"
|
|
done
|
|
echo "published to $OWNER/rpm/$GROUP"
|
|
|
|
# The HDR-capable gamescope the sysext carries (packaging/gamescope) — what lets the
|
|
# gamescope backend stream 10-bit BT.2020 PQ instead of 8-bit SDR.
|
|
#
|
|
# CACHED, and that is the whole reason this is affordable: it is a ~10-minute C++ meson build
|
|
# of an entirely separate tree that depends on NOTHING in this repo except
|
|
# `packaging/gamescope/**` (the patches and the upstream pin, which lives in the build
|
|
# script). So the key is that directory's hash and a normal push restores a binary instead of
|
|
# building one. Per-Fedora-major, because the binary is soname-coupled to its base exactly
|
|
# like the RPM is — an f43 build does not start on f44 (libavutil.so.59 vs .60).
|
|
- uses: actions/cache@v4
|
|
id: gamescope
|
|
with:
|
|
path: gs-cache
|
|
key: punktfunk-gamescope-f${{ matrix.fedver }}-${{ hashFiles('packaging/gamescope/**') }}
|
|
|
|
- name: Build the HDR gamescope
|
|
if: steps.gamescope.outputs.cache-hit != 'true'
|
|
# Best-effort ON PURPOSE. The sysext is the primary Bazzite delivery path and works without
|
|
# this binary (the host just stays SDR on the gamescope backend, which is what every
|
|
# release before this one did) — so a hiccup building someone else's tree must not cost the
|
|
# whole image. It is loud, though: the warning below, and `--gamescope` silently absent
|
|
# downstream is impossible because build-sysext.sh verifies the +pfhdr marker itself.
|
|
run: |
|
|
set -x
|
|
# `dnf builddep` resolves Fedora's PACKAGED gamescope, which is older than the master we
|
|
# pin, so it can come up short — xorg-x11-server-Xwayland-devel is the one that actually
|
|
# bites (wlroots' configure dies on a missing xserver.wrap several minutes in).
|
|
dnf -y install dnf-plugins-core meson ninja-build glslc || true
|
|
dnf builddep -y gamescope || true
|
|
dnf -y install xorg-x11-server-Xwayland-devel || true
|
|
if bash packaging/gamescope/build-punktfunk-gamescope.sh \
|
|
--destdir "$PWD/gs-stage" --prefix /usr --jobs "$(nproc)"; then
|
|
install -Dm0755 gs-stage/usr/bin/punktfunk-gamescope gs-cache/punktfunk-gamescope
|
|
else
|
|
echo "::warning::punktfunk-gamescope failed to build for f${{ matrix.fedver }} — the sysext ships without it (gamescope sessions stay SDR)"
|
|
fi
|
|
|
|
# The no-layering Bazzite path: wrap the just-built host + web RPMs into a systemd-sysext
|
|
# image and publish it to the per-Fedora-major feed (punktfunk-sysext/f43[-canary], …) that
|
|
# `punktfunk-sysext install|update` reads. Same RPMs, same channels — just no rpm-ostree.
|
|
- name: Build the sysext image
|
|
run: |
|
|
# Execute it here rather than only handing it over: build-sysext.sh treats an unusable
|
|
# --version as fatal (rightly — it is how the +pfhdr marker is read), and a cached binary
|
|
# whose runtime libs are missing from this container must cost the image its HDR, not the
|
|
# image itself.
|
|
gs=()
|
|
if [ -x gs-cache/punktfunk-gamescope ] && gs-cache/punktfunk-gamescope --version >/dev/null 2>&1; then
|
|
gs=(--gamescope gs-cache/punktfunk-gamescope)
|
|
echo "folding in $(gs-cache/punktfunk-gamescope --version 2>&1 | head -1)"
|
|
else
|
|
echo "::warning::no usable punktfunk-gamescope for f${{ matrix.fedver }} — the sysext ships without it (gamescope sessions stay SDR)"
|
|
fi
|
|
bash packaging/bazzite/build-sysext.sh --version-id "${{ matrix.fedver }}" \
|
|
--out "dist-sysext/punktfunk-${PF_VERSION}-${PF_RELEASE}-x86-64.raw" \
|
|
"${gs[@]}" \
|
|
dist/punktfunk-"${PF_VERSION}-${PF_RELEASE}"*.rpm \
|
|
dist/punktfunk-web-"${PF_VERSION}-${PF_RELEASE}"*.rpm \
|
|
dist/punktfunk-scripting-"${PF_VERSION}-${PF_RELEASE}"*.rpm
|
|
|
|
- name: Publish the sysext feed
|
|
env:
|
|
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
case "$GROUP" in
|
|
*-canary) FEED="f${{ matrix.fedver }}-canary"; KEEP=6 ;; # rolling: bound the pile-up
|
|
*) FEED="f${{ matrix.fedver }}"; KEEP=0 ;; # stable: keep every release
|
|
esac
|
|
KEEP=$KEEP bash packaging/bazzite/publish-sysext-feed.sh "$FEED" \
|
|
"dist-sysext/punktfunk-${PF_VERSION}-${PF_RELEASE}-x86-64.raw"
|
|
|
|
# On a real release, also attach the .rpms to the unified Gitea Release. Both Fedora bases
|
|
# (bazzite=F43, fedora-44) build the SAME filename, so suffix the asset with the base to keep
|
|
# both on the release; canary builds live in the `*-canary` rpm groups (no release page).
|
|
- name: Attach .rpms to the Gitea release (stable tags only)
|
|
if: startsWith(gitea.ref, 'refs/tags/v')
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
. scripts/ci/gitea-release.sh
|
|
RID=$(ensure_release "$GITHUB_REF_NAME" "$GITHUB_REF_NAME" auto)
|
|
for rpm in dist/*.rpm; do
|
|
case "$rpm" in *debuginfo*|*debugsource*) continue;; esac
|
|
base="$(basename "$rpm" .rpm)"
|
|
upsert_asset "$RID" "$rpm" "${base}.${{ matrix.group }}.rpm"
|
|
done
|
|
for raw in dist-sysext/*.raw; do
|
|
upsert_asset "$RID" "$raw" "$(basename "$raw" .raw).f${{ matrix.fedver }}.raw"
|
|
done
|