feat(packaging/flatpak,decky): Steam Deck client flatpak + plugin deploy + CI
apple / swift (push) Successful in 53s
android / android (push) Successful in 3m48s
ci / web (push) Successful in 29s
ci / docs-site (push) Successful in 34s
ci / rust (push) Successful in 2m21s
ci / bench (push) Successful in 1m36s
decky / build-publish (push) Successful in 31s
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 6s
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 3s
flatpak / build-publish (push) Failing after 4s
deb / build-publish (push) Successful in 2m38s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 5m9s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 4m42s
docker / deploy-docs (push) Successful in 16s
apple / swift (push) Successful in 53s
android / android (push) Successful in 3m48s
ci / web (push) Successful in 29s
ci / docs-site (push) Successful in 34s
ci / rust (push) Successful in 2m21s
ci / bench (push) Successful in 1m36s
decky / build-publish (push) Successful in 31s
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 6s
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 3s
flatpak / build-publish (push) Failing after 4s
deb / build-publish (push) Successful in 2m38s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 5m9s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 4m42s
docker / deploy-docs (push) Successful in 16s
Ship the punktfunk Linux client to the Steam Deck as a Flatpak — the only viable
SteamOS install path, since /usr is read-only and lacks libadwaita/SDL3 — and
publish both it and the Decky plugin through Gitea. Built and validated live on a
Steam Deck (SteamOS 3.7): bundle installs user-scope, all libs resolve, libavcodec
resolves to the codecs-extra HEVC build, devices=all for DualSense hidraw.
packaging/flatpak (new):
- io.unom.Punktfunk.yml on GNOME 50 / freedesktop-sdk 25.08. rust-stable//25.08
(rustc 1.96 — the GTK4 chain needs >=1.92; the EOL GNOME-48/24.08 rust-stable at
1.89 could not build it) + llvm20 (libclang for bindgen in ffmpeg-sys-next/sdl3-sys).
HEVC libavcodec comes from the runtime's auto codecs-extra extension point (no
app-side codec declaration). Bundled SDL3 3.4.10 (matches sdl3-sys 0.6.6+SDL-3.4.10).
finish-args: wayland/fallback-x11, --device=all (GPU/VAAPI + evdev + hidraw — flatpak
cannot bind /dev/hidrawN char devices via --filesystem), pulseaudio, network,
~/.config/punktfunk.
- metainfo.xml, desktop, square SVG icon, build-flatpak.sh (offline cargo-sources;
on-Deck org.flatpak.Builder or CI), README.
clients/decky:
- add LICENSE (MIT), fix package.json license (BSD-3-Clause -> Apache-2.0 OR MIT),
add scripts/{package.sh,deploy.sh} (the plugins dir is root-owned: stage to /tmp,
sudo install, restart plugin_loader), align the launcher fallback to the real
flatpak app id io.unom.Punktfunk, rewrite the install section.
.gitea/workflows:
- flatpak.yml: privileged Fedora container builds the bundle and publishes to the
Gitea generic registry (+ release attachment on tags).
- decky.yml: pnpm build -> store-layout zip -> registry (stable latest/ URL for
Decky "install from URL").
docs: packaging/README + packaging/flatpak/README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,126 @@
|
|||||||
|
# Build the punktfunk Decky Loader plugin (Gaming-Mode QAM launcher) into a distribution zip
|
||||||
|
# and publish it to Gitea's GENERIC package registry, giving Decky's "install from URL" a
|
||||||
|
# stable link. On tags the zip is ALSO attached to the Gitea release.
|
||||||
|
#
|
||||||
|
# PUT/GET https://git.unom.io/api/packages/unom/generic/punktfunk-decky/<version>/punktfunk.zip
|
||||||
|
#
|
||||||
|
# The plugin backend is PURE PYTHON (clients/decky/main.py — no compiled binary), so we do NOT
|
||||||
|
# need the Decky CLI (which requires Docker + rust-nightly only to compile native backends).
|
||||||
|
# We build the frontend with pnpm and assemble the store-layout zip by hand:
|
||||||
|
#
|
||||||
|
# punktfunk.zip
|
||||||
|
# punktfunk/ <- single top-level dir == plugin.json "name"
|
||||||
|
# plugin.json [required]
|
||||||
|
# package.json [required]
|
||||||
|
# main.py [required: python backend]
|
||||||
|
# dist/index.js [required: rollup output]
|
||||||
|
# README.md (recommended)
|
||||||
|
# LICENSE [required by the plugin store]
|
||||||
|
#
|
||||||
|
# REGISTRY_TOKEN: repo Actions secret, a PAT with write:package scope (shared with deb/rpm/docker).
|
||||||
|
name: decky
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
tags: ['v*']
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.unom.io
|
||||||
|
OWNER: unom
|
||||||
|
PACKAGE: punktfunk-decky # generic-registry package name
|
||||||
|
PLUGIN: punktfunk # plugin.json "name" == zip top-level dir
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-publish:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 30
|
||||||
|
container:
|
||||||
|
image: node:22-bookworm # node + corepack(pnpm); matches the @decky toolchain
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: clients/decky
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: pnpm
|
||||||
|
run: |
|
||||||
|
corepack enable
|
||||||
|
# The repo's pnpm-lock.yaml + package.json devDeps target pnpm 9 (the version the
|
||||||
|
# @decky toolchain and the local build use). Pin it so --frozen-lockfile holds.
|
||||||
|
corepack prepare pnpm@9 --activate
|
||||||
|
|
||||||
|
- name: Build frontend
|
||||||
|
run: |
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
pnpm run build # rollup -> clients/decky/dist/index.js
|
||||||
|
|
||||||
|
- name: Version
|
||||||
|
# Tag v1.2.3 -> 1.2.3; main push -> 0.0.1-ciN.g<sha>. Used only for the registry
|
||||||
|
# version path + the zip name (the plugin.json version is the source of truth Decky
|
||||||
|
# reads after install).
|
||||||
|
working-directory: ${{ gitea.workspace }}
|
||||||
|
run: |
|
||||||
|
SHORT=$(echo "$GITHUB_SHA" | cut -c1-8)
|
||||||
|
case "$GITHUB_REF" in
|
||||||
|
refs/tags/v*) V="${GITHUB_REF_NAME#v}" ;;
|
||||||
|
*) V="0.0.1-ci${GITHUB_RUN_NUMBER}.g${SHORT}" ;;
|
||||||
|
esac
|
||||||
|
echo "VERSION=$V" >> "$GITHUB_ENV"
|
||||||
|
echo "decky version $V"
|
||||||
|
|
||||||
|
- name: Assemble store-layout zip
|
||||||
|
working-directory: ${{ gitea.workspace }}
|
||||||
|
run: |
|
||||||
|
apt-get update && apt-get install -y --no-install-recommends zip >/dev/null
|
||||||
|
STAGE="$RUNNER_TEMP/decky"
|
||||||
|
DEST="$STAGE/$PLUGIN"
|
||||||
|
rm -rf "$STAGE"; mkdir -p "$DEST/dist"
|
||||||
|
cp clients/decky/plugin.json "$DEST/"
|
||||||
|
cp clients/decky/package.json "$DEST/"
|
||||||
|
cp clients/decky/main.py "$DEST/"
|
||||||
|
cp clients/decky/dist/index.js "$DEST/dist/"
|
||||||
|
cp clients/decky/README.md "$DEST/"
|
||||||
|
# Store requires a LICENSE in the plugin root; the project is MIT OR Apache-2.0.
|
||||||
|
cp LICENSE-MIT "$DEST/LICENSE"
|
||||||
|
( cd "$STAGE" && zip -r "$RUNNER_TEMP/punktfunk.zip" "$PLUGIN" )
|
||||||
|
ls -lh "$RUNNER_TEMP/punktfunk.zip"
|
||||||
|
unzip -l "$RUNNER_TEMP/punktfunk.zip"
|
||||||
|
|
||||||
|
- name: Publish to the Gitea generic registry
|
||||||
|
working-directory: ${{ gitea.workspace }}
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
BASE="https://$REGISTRY/api/packages/$OWNER/generic/$PACKAGE"
|
||||||
|
# 1) Immutable, versioned URL.
|
||||||
|
curl -fsS --user "enricobuehler:$TOKEN" --upload-file "$RUNNER_TEMP/punktfunk.zip" \
|
||||||
|
"$BASE/$VERSION/punktfunk.zip"
|
||||||
|
echo "published $BASE/$VERSION/punktfunk.zip"
|
||||||
|
# 2) Stable `latest/punktfunk.zip` — this is the link to paste into Decky's
|
||||||
|
# "install from URL". The generic registry rejects re-uploading an existing
|
||||||
|
# version/file (409), so delete the prior `latest` first (ignore 404 on run #1).
|
||||||
|
curl -fsS -o /dev/null --user "enricobuehler:$TOKEN" -X DELETE \
|
||||||
|
"$BASE/latest/punktfunk.zip" || true
|
||||||
|
curl -fsS --user "enricobuehler:$TOKEN" --upload-file "$RUNNER_TEMP/punktfunk.zip" \
|
||||||
|
"$BASE/latest/punktfunk.zip"
|
||||||
|
echo "install-from-URL link: $BASE/latest/punktfunk.zip"
|
||||||
|
|
||||||
|
- name: Attach zip to the Gitea release (tags only)
|
||||||
|
if: startsWith(gitea.ref, 'refs/tags/')
|
||||||
|
working-directory: ${{ gitea.workspace }}
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}"
|
||||||
|
ID=$(curl -sf -X POST "$API/releases" \
|
||||||
|
-H "Authorization: token $TOKEN" -H 'Content-Type: application/json' \
|
||||||
|
-d "{\"tag_name\":\"$GITHUB_REF_NAME\",\"name\":\"$GITHUB_REF_NAME\"}" \
|
||||||
|
| python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])' \
|
||||||
|
|| curl -sf "$API/releases/tags/$GITHUB_REF_NAME" -H "Authorization: token $TOKEN" \
|
||||||
|
| python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])')
|
||||||
|
curl -sf -X POST "$API/releases/$ID/assets?name=punktfunk-${VERSION}.zip" \
|
||||||
|
-H "Authorization: token $TOKEN" \
|
||||||
|
-F "attachment=@$RUNNER_TEMP/punktfunk.zip" >/dev/null
|
||||||
|
echo "attached punktfunk-${VERSION}.zip to release $GITHUB_REF_NAME"
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# Build the native punktfunk Linux CLIENT as a single-file Flatpak bundle and publish it to
|
||||||
|
# Gitea's GENERIC package registry, so the Steam Deck (and any flatpak distro) installs it
|
||||||
|
# the SteamOS-native, update-survivable way: `flatpak install --user <downloaded>.flatpak`.
|
||||||
|
# (The HOST stays an RPM/deb — it needs unsandboxed /dev/uinput + zero-copy NVENC; only the
|
||||||
|
# CLIENT is sandbox-friendly. See packaging/README.md and packaging/flatpak/README.md.)
|
||||||
|
#
|
||||||
|
# Gitea has NO flatpak/ostree registry, so the bundle lives in the generic registry:
|
||||||
|
# PUT https://git.unom.io/api/packages/unom/generic/punktfunk-client-flatpak/<version>/<file>
|
||||||
|
# GET https://git.unom.io/api/packages/unom/generic/punktfunk-client-flatpak/<version>/<file>
|
||||||
|
# On tags the bundle is ALSO attached to the Gitea release (mirrors release.yml's DMG).
|
||||||
|
#
|
||||||
|
# PRIVILEGED-BUILD CONSTRAINT: flatpak-builder runs bubblewrap, which needs user namespaces.
|
||||||
|
# In a Gitea/act_runner Docker executor that means the job container must be --privileged
|
||||||
|
# (the same runner already runs `docker build` in docker.yml, so its Docker daemon allows it).
|
||||||
|
# If your runner CANNOT grant --privileged, this job will fail at `flatpak-builder` with
|
||||||
|
# "Creating new namespace failed: Operation not permitted" — see the fallback in
|
||||||
|
# packaging/flatpak/README.md (build on the Deck via org.flatpak.Builder, or on a Linux box,
|
||||||
|
# then upload with the curl line below).
|
||||||
|
#
|
||||||
|
# REGISTRY_TOKEN: repo Actions secret, a PAT with write:package scope (shared with deb/rpm/docker).
|
||||||
|
name: flatpak
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
tags: ['v*']
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.unom.io
|
||||||
|
OWNER: unom
|
||||||
|
APP_ID: io.unom.Punktfunk
|
||||||
|
MANIFEST: packaging/flatpak/io.unom.Punktfunk.yml
|
||||||
|
PACKAGE: punktfunk-client-flatpak # generic-registry package name
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-publish:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 120
|
||||||
|
container:
|
||||||
|
# Fedora ships a recent flatpak + flatpak-builder + the kernel userns support.
|
||||||
|
# --privileged is required for bubblewrap inside the Docker executor (see header).
|
||||||
|
image: fedora:43
|
||||||
|
options: --privileged
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Tooling
|
||||||
|
run: |
|
||||||
|
dnf -y install flatpak flatpak-builder git python3 python3-aiohttp python3-toml curl jq
|
||||||
|
# Flathub provides the GNOME runtime/SDK + the rust-stable + ffmpeg-full extensions.
|
||||||
|
flatpak remote-add --user --if-not-exists flathub \
|
||||||
|
https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||||
|
git config --global --add safe.directory "$PWD"
|
||||||
|
|
||||||
|
- name: Version
|
||||||
|
# Tag v1.2.3 -> 1.2.3; a main push -> 0.0.1-ciN.g<sha> (sorts before a real release,
|
||||||
|
# increases by run number — newest main build always wins). The generic registry
|
||||||
|
# version string allows letters/dots/hyphens.
|
||||||
|
run: |
|
||||||
|
SHORT=$(echo "$GITHUB_SHA" | cut -c1-8)
|
||||||
|
case "$GITHUB_REF" in
|
||||||
|
refs/tags/v*) V="${GITHUB_REF_NAME#v}" ;;
|
||||||
|
*) V="0.0.1-ci${GITHUB_RUN_NUMBER}.g${SHORT}" ;;
|
||||||
|
esac
|
||||||
|
echo "VERSION=$V" >> "$GITHUB_ENV"
|
||||||
|
echo "BUNDLE=punktfunk-client-${V}.flatpak" >> "$GITHUB_ENV"
|
||||||
|
echo "flatpak version $V"
|
||||||
|
|
||||||
|
- name: Generate offline cargo sources
|
||||||
|
# flatpak builds with no network; vendor every crate from Cargo.lock into
|
||||||
|
# cargo-sources.json next to the manifest (referenced by the manifest's
|
||||||
|
# punktfunk-client module).
|
||||||
|
run: |
|
||||||
|
curl -fsSL -o /tmp/flatpak-cargo-generator.py \
|
||||||
|
https://raw.githubusercontent.com/flatpak/flatpak-builder-tools/master/cargo/flatpak-cargo-generator.py
|
||||||
|
python3 /tmp/flatpak-cargo-generator.py Cargo.lock \
|
||||||
|
-o packaging/flatpak/cargo-sources.json
|
||||||
|
|
||||||
|
- name: Build the flatpak (install deps from Flathub, offline build)
|
||||||
|
run: |
|
||||||
|
# --install-deps-from=flathub pulls everything the manifest declares: the GNOME 50
|
||||||
|
# runtime/SDK + the rust-stable (//25.08, rustc 1.96) and llvm20 SDK extensions, plus
|
||||||
|
# the runtime's auto codecs-extra (HEVC libavcodec). --disable-rofiles-fuse is the
|
||||||
|
# container-safe path (no FUSE).
|
||||||
|
flatpak-builder --user --force-clean --disable-rofiles-fuse \
|
||||||
|
--install-deps-from=flathub \
|
||||||
|
--repo="$PWD/repo" \
|
||||||
|
"$PWD/build-dir" "$MANIFEST"
|
||||||
|
|
||||||
|
- name: Export single-file bundle
|
||||||
|
run: |
|
||||||
|
flatpak build-bundle "$PWD/repo" "$BUNDLE" "$APP_ID"
|
||||||
|
ls -lh "$BUNDLE"
|
||||||
|
|
||||||
|
- name: Publish to the Gitea generic registry
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
BASE="https://$REGISTRY/api/packages/$OWNER/generic/$PACKAGE"
|
||||||
|
# 1) Immutable, versioned URL.
|
||||||
|
curl -fsS --user "enricobuehler:$TOKEN" --upload-file "$BUNDLE" \
|
||||||
|
"$BASE/$VERSION/$BUNDLE"
|
||||||
|
echo "published $BASE/$VERSION/$BUNDLE"
|
||||||
|
# 2) Stable `latest/punktfunk-client.flatpak` alias for the Decky fallback + scripts.
|
||||||
|
# The generic registry rejects re-uploading an existing version/file (409), so
|
||||||
|
# delete the prior `latest` file first (ignore 404 on the first ever run).
|
||||||
|
curl -fsS -o /dev/null --user "enricobuehler:$TOKEN" -X DELETE \
|
||||||
|
"$BASE/latest/punktfunk-client.flatpak" || true
|
||||||
|
curl -fsS --user "enricobuehler:$TOKEN" --upload-file "$BUNDLE" \
|
||||||
|
"$BASE/latest/punktfunk-client.flatpak"
|
||||||
|
echo "published $BASE/latest/punktfunk-client.flatpak"
|
||||||
|
|
||||||
|
- name: Attach bundle to the Gitea release (tags only)
|
||||||
|
if: startsWith(gitea.ref, 'refs/tags/')
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}"
|
||||||
|
ID=$(curl -sf -X POST "$API/releases" \
|
||||||
|
-H "Authorization: token $TOKEN" -H 'Content-Type: application/json' \
|
||||||
|
-d "{\"tag_name\":\"$GITHUB_REF_NAME\",\"name\":\"$GITHUB_REF_NAME\"}" \
|
||||||
|
| python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])' \
|
||||||
|
|| curl -sf "$API/releases/tags/$GITHUB_REF_NAME" -H "Authorization: token $TOKEN" \
|
||||||
|
| python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])')
|
||||||
|
curl -sf -X POST "$API/releases/$ID/assets?name=$BUNDLE" \
|
||||||
|
-H "Authorization: token $TOKEN" \
|
||||||
|
-F "attachment=@$BUNDLE" >/dev/null
|
||||||
|
echo "attached $BUNDLE to release $GITHUB_REF_NAME"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 unom
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
+49
-11
@@ -49,12 +49,14 @@ Shells out to **`avahi-browse -rpt _punktfunk._udp`** (SteamOS and Bazzite ship
|
|||||||
### Client launch (`connect()`)
|
### Client launch (`connect()`)
|
||||||
|
|
||||||
The client binary `punktfunk-client` is resolved in order: `PATH` → `/usr/bin` →
|
The client binary `punktfunk-client` is resolved in order: `PATH` → `/usr/bin` →
|
||||||
`/usr/local/bin` → `~/.local/bin` → a `flatpak run earth.buehler.punktfunk.Client`
|
`/usr/local/bin` → `~/.local/bin` → a `flatpak run io.unom.Punktfunk` fallback. The resolved
|
||||||
fallback. The resolved argv and a clear `client-not-found` error surface to the UI. The
|
argv and a clear `client-not-found` error surface to the UI. The child PID is tracked so
|
||||||
child PID is tracked so `disconnect()` (and plugin `_unload`) can terminate it.
|
`disconnect()` (and plugin `_unload`) can terminate it.
|
||||||
|
|
||||||
> **TODO:** pin the canonical SteamOS install path once a Deck packaging story for
|
> On the **Steam Deck** the client install is the flatpak `io.unom.Punktfunk`
|
||||||
> `punktfunk-client` is settled (likely a flatpak, since SteamOS `/usr` is read-only).
|
> (`packaging/flatpak/`) — SteamOS `/usr` is read-only and lacks `libadwaita`/`libSDL3`, so
|
||||||
|
> the flatpak (which bundles them) is the canonical path; the resolver's flatpak fallback
|
||||||
|
> launches exactly that.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -76,15 +78,51 @@ pnpm build # rollup → dist/index.js
|
|||||||
|
|
||||||
## Install on the Deck
|
## Install on the Deck
|
||||||
|
|
||||||
Copy the built plugin directory to the Deck and restart Decky:
|
### Option A — Decky "install from URL" (recommended; published by CI)
|
||||||
|
|
||||||
```sh
|
CI (`.gitea/workflows/decky.yml`) builds the plugin into a store-layout zip and publishes it to
|
||||||
# the dir must contain: dist/, main.py, plugin.json, package.json
|
Gitea's **generic package registry** on every push to `main` and on `v*` tags, exposing a stable
|
||||||
rsync -a --exclude node_modules clients/decky/ deck@<deck-ip>:~/homebrew/plugins/punktfunk/
|
URL. In Decky's settings → **Developer Mode** → **Install Plugin from URL**, paste:
|
||||||
# then, on the Deck, restart Decky Loader (Settings → Developer → "Restart" / reboot)
|
|
||||||
|
```
|
||||||
|
https://git.unom.io/api/packages/unom/generic/punktfunk-decky/latest/punktfunk.zip
|
||||||
```
|
```
|
||||||
|
|
||||||
The **punktfunk** panel then appears in the Quick Access Menu.
|
(or a pinned version: `.../punktfunk-decky/<version>/punktfunk.zip`). On tags the same zip is
|
||||||
|
also attached to the Gitea release. The zip's layout is the store-required one — a single
|
||||||
|
top-level `punktfunk/` dir holding `plugin.json`, `package.json`, `main.py`, `dist/index.js`,
|
||||||
|
`README.md`, and `LICENSE`.
|
||||||
|
|
||||||
|
### Option B — manual dev copy (sideload)
|
||||||
|
|
||||||
|
Decky's `~/homebrew/plugins/` is **root-owned** (PluginLoader runs as root and manages it), so a
|
||||||
|
plain `rsync` into it fails — stage to a writable temp dir, then `sudo`-install and restart the
|
||||||
|
loader. The two helper scripts do exactly this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd clients/decky
|
||||||
|
pnpm install
|
||||||
|
pnpm run package # → out/punktfunk/ + out/punktfunk-v<ver>.zip
|
||||||
|
DECK=deck@<deck-ip> pnpm run deploy # rsync → /tmp, sudo cp into plugins/, chown root, restart
|
||||||
|
```
|
||||||
|
|
||||||
|
`deploy.sh` prompts for the Deck's sudo password interactively (via `ssh -t`); set `DECKPASS=…`
|
||||||
|
to run it non-interactively. Equivalent by hand:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd clients/decky && pnpm build && bash scripts/package.sh
|
||||||
|
rsync -azp --delete out/punktfunk/ deck@<deck-ip>:/tmp/punktfunk/
|
||||||
|
ssh -t deck@<deck-ip> 'sudo sh -c "rm -rf ~deck/homebrew/plugins/punktfunk && \
|
||||||
|
cp -r /tmp/punktfunk ~deck/homebrew/plugins/punktfunk && \
|
||||||
|
chown -R root:root ~deck/homebrew/plugins/punktfunk && systemctl restart plugin_loader"'
|
||||||
|
```
|
||||||
|
|
||||||
|
A loader restart is required for an out-of-band install to appear. The **punktfunk** panel then
|
||||||
|
shows up in the Quick Access Menu.
|
||||||
|
|
||||||
|
> The plugin launches the client via the flatpak `io.unom.Punktfunk` (see
|
||||||
|
> [`../../packaging/flatpak/README.md`](../../packaging/flatpak/README.md)) — install that on
|
||||||
|
> the Deck too, or the panel's Connect surfaces a `client-not-found` error.
|
||||||
|
|
||||||
## Limitations / next steps
|
## Limitations / next steps
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ import decky
|
|||||||
# name and let the spawn fail loudly — install the client on the Deck (.deb / RPM / flatpak)
|
# name and let the spawn fail loudly — install the client on the Deck (.deb / RPM / flatpak)
|
||||||
# or symlink it into ~/.local/bin.
|
# or symlink it into ~/.local/bin.
|
||||||
#
|
#
|
||||||
# TODO: once a Steam Deck / SteamOS install path for punktfunk-client is settled (likely a
|
# On SteamOS (read-only /usr, image-based) the settled install path is the flatpak
|
||||||
# flatpak, since SteamOS is image-based and /usr is read-only), pin the canonical path here.
|
# ``io.unom.Punktfunk`` (packaging/flatpak/), launched via ``flatpak run`` — see the flatpak
|
||||||
|
# fallback in :func:`_resolve_client`.
|
||||||
CLIENT_BINARY = "punktfunk-client"
|
CLIENT_BINARY = "punktfunk-client"
|
||||||
|
|
||||||
# Service type advertised by punktfunk/1 hosts (matches NATIVE_SERVICE in the Rust host).
|
# Service type advertised by punktfunk/1 hosts (matches NATIVE_SERVICE in the Rust host).
|
||||||
@@ -59,12 +60,14 @@ def _resolve_client() -> list[str]:
|
|||||||
if Path(candidate).exists():
|
if Path(candidate).exists():
|
||||||
return [candidate]
|
return [candidate]
|
||||||
|
|
||||||
# Flatpak fallback. The app id is a guess until a flatpak is actually published;
|
# Flatpak fallback — the canonical install path on the Steam Deck (SteamOS /usr is
|
||||||
# `flatpak run <id>` is a no-op-ish failure if it is not installed, which surfaces as a
|
# read-only; the flatpak bundles the libadwaita + SDL3 the system lacks). The app id is
|
||||||
# spawn error the user can act on.
|
# the one the flatpak manifest publishes (packaging/flatpak/io.unom.Punktfunk.yml). If it
|
||||||
|
# is not installed, `flatpak run <id>` fails and surfaces as a spawn error the user can
|
||||||
|
# act on (install the bundle: `flatpak install --user punktfunk-client-*.flatpak`).
|
||||||
flatpak = shutil.which("flatpak")
|
flatpak = shutil.which("flatpak")
|
||||||
if flatpak:
|
if flatpak:
|
||||||
return [flatpak, "run", "earth.buehler.punktfunk.Client"]
|
return [flatpak, "run", "io.unom.Punktfunk"]
|
||||||
|
|
||||||
decky.logger.warning(
|
decky.logger.warning(
|
||||||
"punktfunk-client not found on PATH or in %s; falling back to bare name",
|
"punktfunk-client not found on PATH or in %s; falling back to bare name",
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"watch": "rollup -c -w",
|
"watch": "rollup -c -w",
|
||||||
|
"package": "pnpm build && bash scripts/package.sh",
|
||||||
|
"deploy": "bash scripts/deploy.sh",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -15,7 +17,7 @@
|
|||||||
"game-streaming"
|
"game-streaming"
|
||||||
],
|
],
|
||||||
"author": "enrico",
|
"author": "enrico",
|
||||||
"license": "BSD-3-Clause",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@decky/api": "^1.1.3",
|
"@decky/api": "^1.1.3",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
|
|||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Deploy the staged plugin tree (out/<name>, produced by scripts/package.sh) into a Steam
|
||||||
|
# Deck's ~/homebrew/plugins/, then restart Decky Loader.
|
||||||
|
#
|
||||||
|
# Decky's plugins dir is ROOT-OWNED (PluginLoader runs as root and manages it), so the install
|
||||||
|
# step needs sudo on the Deck. We rsync to a deck-writable /tmp first (no privilege), then
|
||||||
|
# sudo-copy into place. Out-of-band installs only appear after a loader restart.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# DECK=deck@192.168.1.235 bash scripts/deploy.sh # interactive sudo (prompts on the Deck)
|
||||||
|
# DECK=deck@192.168.1.235 DECKPASS=... bash scripts/deploy.sh # non-interactive (scripted/CI)
|
||||||
|
set -euo pipefail
|
||||||
|
HERE="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
DECK="${DECK:?set DECK=deck@<ip>}"
|
||||||
|
NAME="$(python3 -c 'import json;print(json.load(open("'"$HERE"'/plugin.json"))["name"])')"
|
||||||
|
STAGE_LOCAL="$HERE/out/$NAME"
|
||||||
|
[ -d "$STAGE_LOCAL" ] || { echo "$STAGE_LOCAL missing — run scripts/package.sh first" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 1. push to a deck-writable temp dir (deck owns its $HOME)
|
||||||
|
rsync -azp --delete -e ssh "$STAGE_LOCAL/" "$DECK:/tmp/$NAME/"
|
||||||
|
|
||||||
|
# 2. sudo-install into the root-owned plugins dir, match Decky's root:root ownership, reload
|
||||||
|
INSTALL="rm -rf /home/deck/homebrew/plugins/$NAME \
|
||||||
|
&& cp -r /tmp/$NAME /home/deck/homebrew/plugins/$NAME \
|
||||||
|
&& chown -R root:root /home/deck/homebrew/plugins/$NAME \
|
||||||
|
&& rm -rf /tmp/$NAME \
|
||||||
|
&& systemctl restart plugin_loader"
|
||||||
|
|
||||||
|
if [ -n "${DECKPASS:-}" ]; then
|
||||||
|
ssh "$DECK" "echo '$DECKPASS' | sudo -S sh -c '$INSTALL'"
|
||||||
|
else
|
||||||
|
echo "==> sudo on the Deck will prompt for ${DECK}'s password:"
|
||||||
|
ssh -t "$DECK" "sudo sh -c '$INSTALL'"
|
||||||
|
fi
|
||||||
|
echo "deployed $NAME → $DECK:~/homebrew/plugins/$NAME and restarted plugin_loader"
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Assemble the Decky plugin into the canonical store/sideload layout:
|
||||||
|
#
|
||||||
|
# out/punktfunk-v<version>.zip -> punktfunk/{dist/index.js,main.py,plugin.json,
|
||||||
|
# package.json,decky.pyi,LICENSE,README.md}
|
||||||
|
# out/punktfunk/ (the same tree, unzipped — rsync this with scripts/deploy.sh)
|
||||||
|
#
|
||||||
|
# Decky extracts the zip with --strip-components=1, so the single top-level dir MUST equal
|
||||||
|
# plugin.json "name". Run after `pnpm build` (or use `pnpm run package`). Host-agnostic: needs
|
||||||
|
# only bash, python3 and zip.
|
||||||
|
set -euo pipefail
|
||||||
|
HERE="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
cd "$HERE"
|
||||||
|
|
||||||
|
[ -f dist/index.js ] || { echo "dist/index.js missing — run 'pnpm build' first" >&2; exit 1; }
|
||||||
|
[ -f LICENSE ] || { echo "LICENSE missing (required by the Decky store)" >&2; exit 1; }
|
||||||
|
|
||||||
|
NAME="$(python3 -c 'import json;print(json.load(open("plugin.json"))["name"])')"
|
||||||
|
VER="$(python3 -c 'import json;print(json.load(open("package.json"))["version"])')"
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d)"
|
||||||
|
DEST="$STAGE/$NAME"
|
||||||
|
mkdir -p "$DEST/dist"
|
||||||
|
cp dist/index.js "$DEST/dist/index.js" # ship the bundle only, not the sourcemap
|
||||||
|
cp main.py plugin.json package.json LICENSE "$DEST/"
|
||||||
|
[ -f decky.pyi ] && cp decky.pyi "$DEST/"
|
||||||
|
[ -f README.md ] && cp README.md "$DEST/"
|
||||||
|
|
||||||
|
OUT="$HERE/out"
|
||||||
|
mkdir -p "$OUT"
|
||||||
|
ZIP="$OUT/${NAME}-v${VER}.zip"
|
||||||
|
rm -f "$ZIP"
|
||||||
|
( cd "$STAGE" && zip -r -X "$ZIP" "$NAME" >/dev/null )
|
||||||
|
# Leave an unzipped staging tree for the rsync/sudo deploy path (scripts/deploy.sh).
|
||||||
|
rm -rf "$OUT/$NAME" && cp -r "$DEST" "$OUT/$NAME"
|
||||||
|
rm -rf "$STAGE"
|
||||||
|
|
||||||
|
echo "built $ZIP"
|
||||||
|
echo "staged $OUT/$NAME (deploy with: DECK=deck@<ip> bash scripts/deploy.sh)"
|
||||||
+6
-1
@@ -95,12 +95,17 @@ systemctl --user enable --now punktfunk-host
|
|||||||
|
|
||||||
Pair a stock Moonlight client (mDNS-discovered), or connect the native punktfunk/1 client.
|
Pair a stock Moonlight client (mDNS-discovered), or connect the native punktfunk/1 client.
|
||||||
|
|
||||||
## Why not Flatpak?
|
## Why not Flatpak (for the HOST)?
|
||||||
|
|
||||||
The host needs unsandboxed access the zero-copy NVENC path, `/dev/uinput`, the PipeWire
|
The host needs unsandboxed access the zero-copy NVENC path, `/dev/uinput`, the PipeWire
|
||||||
graph and the compositor's privileged protocols — a Flatpak sandbox fights all of these.
|
graph and the compositor's privileged protocols — a Flatpak sandbox fights all of these.
|
||||||
An RPM (or the bootc layer) installs into the host system where those just work.
|
An RPM (or the bootc layer) installs into the host system where those just work.
|
||||||
|
|
||||||
|
> 👉 The **client** is a different story — it IS shipped as a Flatpak (the only viable
|
||||||
|
> Steam Deck install path: SteamOS `/usr` is read-only and lacks `libadwaita`/`libSDL3`). See
|
||||||
|
> [`flatpak/README.md`](flatpak/README.md). The client sandbox only needs the GPU render node,
|
||||||
|
> Wayland, PipeWire audio, the network and hidraw — all expressible as finish-args.
|
||||||
|
|
||||||
## Building the SRPM/RPM locally (Fedora only)
|
## Building the SRPM/RPM locally (Fedora only)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Generated by flatpak-cargo-generator.py from Cargo.lock in CI (offline crate cache for the
|
||||||
|
# manifest's punktfunk-client module). Regenerated on every build — never commit it.
|
||||||
|
cargo-sources.json
|
||||||
|
|
||||||
|
# flatpak-builder scratch / output (when building locally in this dir).
|
||||||
|
.flatpak-builder/
|
||||||
|
build-dir/
|
||||||
|
repo/
|
||||||
|
*.flatpak
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# punktfunk client — Flatpak (Steam Deck / SteamOS, and any flatpak distro)
|
||||||
|
|
||||||
|
The native Linux **client** (crate `punktfunk-client-linux`, binary `punktfunk-client`) is
|
||||||
|
published as a single-file **`.flatpak` bundle** to **Gitea's generic package registry** in
|
||||||
|
the public `unom` org. CI (`.gitea/workflows/flatpak.yml`) builds and publishes on every push
|
||||||
|
to `main` (a rolling `0.0.1-ciN.<sha>` build) and on `v*` tags (a clean `X.Y.Z`), and on tags
|
||||||
|
also attaches the bundle to the Gitea release.
|
||||||
|
|
||||||
|
> The **host** is NOT a flatpak (it needs unsandboxed `/dev/uinput` + zero-copy NVENC — see
|
||||||
|
> [`../README.md`](../README.md) "Why not Flatpak"). Only the **client** is sandbox-friendly.
|
||||||
|
|
||||||
|
## Why flatpak for the Steam Deck
|
||||||
|
|
||||||
|
SteamOS `/usr` is read-only and image-based, and the system is **missing `libadwaita` and
|
||||||
|
`libSDL3`** — so a bare `punktfunk-client` binary dropped into `~/.local/bin` won't run. Flatpak
|
||||||
|
is the Deck's native, update-survivable app path (the user already runs Moonlight and chiaki-ng
|
||||||
|
as flatpaks), and the bundle carries libadwaita (from `org.gnome.Platform//50`) + a bundled SDL3,
|
||||||
|
with HEVC-capable FFmpeg supplied automatically by the runtime's `codecs-extra` extension.
|
||||||
|
|
||||||
|
App id: **`io.unom.Punktfunk`** (matches the Apple bundle id family and the Decky plugin's
|
||||||
|
flatpak fallback).
|
||||||
|
|
||||||
|
## Install on the Deck (one-time)
|
||||||
|
|
||||||
|
The generic registry is a plain HTTP file store, so just download the bundle and install it
|
||||||
|
per-user (no root, survives SteamOS updates):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Pick a version: a tag like 1.2.3, or the newest main build's 0.0.1-ciN.gSHA.
|
||||||
|
VER=1.2.3
|
||||||
|
URL="https://git.unom.io/api/packages/unom/generic/punktfunk-client-flatpak/$VER/punktfunk-client-$VER.flatpak"
|
||||||
|
|
||||||
|
# Flathub must be enabled (it is on the Deck) so the GNOME runtime + ffmpeg-full pull in:
|
||||||
|
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||||
|
|
||||||
|
curl -fL -o /tmp/punktfunk-client.flatpak "$URL"
|
||||||
|
flatpak install --user --bundle /tmp/punktfunk-client.flatpak
|
||||||
|
```
|
||||||
|
|
||||||
|
Run it:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flatpak run io.unom.Punktfunk # GUI host list (mDNS)
|
||||||
|
flatpak run io.unom.Punktfunk --connect HOST:PORT
|
||||||
|
```
|
||||||
|
|
||||||
|
The **Decky plugin** launches exactly this (`flatpak run io.unom.Punktfunk --connect …`) once
|
||||||
|
installed — see [`../../clients/decky/README.md`](../../clients/decky/README.md).
|
||||||
|
|
||||||
|
## Updates
|
||||||
|
|
||||||
|
A bundle has no remote to track, so updates are "download the newer bundle and reinstall":
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flatpak install --user --bundle /tmp/punktfunk-client.flatpak # same command, newer file
|
||||||
|
```
|
||||||
|
|
||||||
|
(If you want `flatpak update` to track new builds automatically you'd need a hosted OSTree
|
||||||
|
repo, which Gitea cannot serve — see "Alternatives" below. The bundle is the simplest path.)
|
||||||
|
|
||||||
|
## Build locally / the CI fallback
|
||||||
|
|
||||||
|
CI builds this in a **`--privileged`** Fedora container, because `flatpak-builder` runs
|
||||||
|
`bubblewrap`, which needs user namespaces the default Docker executor denies. **If the Gitea
|
||||||
|
runner can't grant `--privileged`** (the job fails at `flatpak-builder` with
|
||||||
|
*"Creating new namespace failed: Operation not permitted"*), build it out-of-band and upload
|
||||||
|
by hand. The easiest place is **on the Deck itself** (it can run `org.flatpak.Builder`
|
||||||
|
user-scope, no root):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# On the Deck (or any flatpak box), one-time:
|
||||||
|
flatpak install --user -y flathub org.flatpak.Builder
|
||||||
|
|
||||||
|
# build-flatpak.sh auto-detects org.flatpak.Builder, generates cargo-sources.json (or reuses an
|
||||||
|
# existing one — see below), builds, and exports dist/punktfunk-client-<version>.flatpak:
|
||||||
|
bash packaging/flatpak/build-flatpak.sh
|
||||||
|
|
||||||
|
# Upload to the generic registry (PAT with write:package):
|
||||||
|
curl -fsS --user "enricobuehler:$REGISTRY_TOKEN" \
|
||||||
|
--upload-file dist/punktfunk-client-*.flatpak \
|
||||||
|
"https://git.unom.io/api/packages/unom/generic/punktfunk-client-flatpak/0.0.1-manual/punktfunk-client.flatpak"
|
||||||
|
```
|
||||||
|
|
||||||
|
> `cargo-sources.json` generation needs `python3` + `aiohttp` + `tomlkit`, which the Deck lacks.
|
||||||
|
> Generate it on a dev box (`build-flatpak.sh` does it, or run the upstream
|
||||||
|
> `flatpak-cargo-generator.py Cargo.lock -o packaging/flatpak/cargo-sources.json`), rsync it next
|
||||||
|
> to the manifest, and `build-flatpak.sh` reuses it (it only regenerates when the file is absent
|
||||||
|
> or `FORCE_GEN=1`).
|
||||||
|
|
||||||
|
> The Mac build host **cannot** build a Linux flatpak (no flatpak-builder for macOS), and
|
||||||
|
> home-worker-2 has no flatpak and no passwordless sudo to install it — so the Deck or the
|
||||||
|
> privileged CI container are the only two viable build sites.
|
||||||
|
|
||||||
|
## Manifest
|
||||||
|
|
||||||
|
[`io.unom.Punktfunk.yml`](io.unom.Punktfunk.yml). Runtime `org.gnome.Platform//50`
|
||||||
|
(GTK 4.20 + libadwaita 1.8 ≥ the crate floors of v4_16 / v1_5), built on freedesktop-sdk 25.08,
|
||||||
|
with two build-time SDK extensions: `org.freedesktop.Sdk.Extension.rust-stable` (→ //25.08,
|
||||||
|
**rustc 1.96** — the GTK4 dep chain, e.g. pango-sys 0.22, needs ≥ 1.92, which the EOL GNOME-48 /
|
||||||
|
24.08 rust-stable at 1.89 could not provide) and `org.freedesktop.Sdk.Extension.llvm20` (libclang,
|
||||||
|
needed by bindgen in ffmpeg-sys-next / sdl3-sys). HEVC-capable libavcodec (soname 61, accepted by
|
||||||
|
ffmpeg-next 8.x) is supplied **automatically at runtime** by the freedesktop `codecs-extra`
|
||||||
|
extension point (auto-downloaded with the runtime; no app-side codec declaration). A bundled
|
||||||
|
**SDL3 3.4.10** module (pinned to match `sdl3-sys 0.6.6+SDL-3.4.10`), and finish-args for Wayland +
|
||||||
|
`--device=all` (GPU/VAAPI render node + evdev + the hidraw char-devices SDL3 needs for DualSense)
|
||||||
|
+ `--socket=pulseaudio` (PipeWire-pulse: playback + mic) + `--share=network`. Alongside it:
|
||||||
|
`io.unom.Punktfunk.desktop`, `io.unom.Punktfunk.metainfo.xml`, `io.unom.Punktfunk.svg` (all
|
||||||
|
installed by the manifest). `cargo-sources.json` (the offline crate cache) is a pure function of
|
||||||
|
`Cargo.lock`; CI regenerates it each build and it is **gitignored** — generate it on any box with
|
||||||
|
network + `python3`/`aiohttp`/`tomlkit` (`build-flatpak.sh` does this automatically) and, for a
|
||||||
|
build host that lacks those (the Deck), rsync the generated file in alongside the manifest.
|
||||||
|
|
||||||
|
## Alternatives considered (and why the bundle wins)
|
||||||
|
|
||||||
|
- **Generic registry bundle (chosen):** one curl to publish, one `flatpak install --bundle` to
|
||||||
|
consume; mirrors the existing deb/rpm curl-upload pattern exactly. No auto-update.
|
||||||
|
- **Release attachment:** also done on tags (the bundle is attached to the Gitea release), good
|
||||||
|
for a human-facing download page; the generic registry gives the stable per-version URL the
|
||||||
|
Decky fallback and scripts use.
|
||||||
|
- **Self-hosted OSTree repo (rejected):** would enable `flatpak update`, but Gitea has no
|
||||||
|
flatpak/ostree registry, so it would mean serving a static OSTree tree over Gitea raw/Pages —
|
||||||
|
more moving parts than the appliance needs today.
|
||||||
Executable
+85
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the punktfunk Linux client as a single-file `.flatpak` bundle.
|
||||||
|
#
|
||||||
|
# Works on the Steam Deck (org.flatpak.Builder from Flathub, user-scope, NO root) and on any
|
||||||
|
# Linux box with flatpak + flatpak-builder. The CI does the same steps (.gitea/workflows/flatpak.yml).
|
||||||
|
#
|
||||||
|
# On the Deck (one-time):
|
||||||
|
# flatpak install --user -y flathub org.flatpak.Builder
|
||||||
|
# Then run this script from the repo root:
|
||||||
|
# bash packaging/flatpak/build-flatpak.sh
|
||||||
|
# Output: dist/punktfunk-client-<version>.flatpak (install with `flatpak install --user <file>`)
|
||||||
|
#
|
||||||
|
# Env knobs:
|
||||||
|
# VERSION=... version string for the bundle name (default: git describe / 0.0.1-dev)
|
||||||
|
# ONLINE=1 skip offline cargo-sources.json; build with --share=network (fast local
|
||||||
|
# iteration, non-reproducible). Default: offline (regenerates cargo-sources).
|
||||||
|
# BUILDER=... override the flatpak-builder invocation (default: auto-detect host
|
||||||
|
# flatpak-builder, else `flatpak run org.flatpak.Builder`).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
cd "$ROOTDIR"
|
||||||
|
|
||||||
|
APP_ID="io.unom.Punktfunk"
|
||||||
|
MANIFEST="packaging/flatpak/io.unom.Punktfunk.yml"
|
||||||
|
VERSION="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo 0.0.1-dev)}"
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
BUNDLE="dist/punktfunk-client-${VERSION}.flatpak"
|
||||||
|
|
||||||
|
# --- pick a flatpak-builder (host binary, or the org.flatpak.Builder flatpak on the Deck) ---
|
||||||
|
if [ -n "${BUILDER:-}" ]; then
|
||||||
|
FPB=($BUILDER)
|
||||||
|
elif command -v flatpak-builder >/dev/null 2>&1; then
|
||||||
|
FPB=(flatpak-builder)
|
||||||
|
elif flatpak info org.flatpak.Builder >/dev/null 2>&1; then
|
||||||
|
FPB=(flatpak run org.flatpak.Builder)
|
||||||
|
else
|
||||||
|
echo "error: need flatpak-builder. On the Deck: flatpak install --user -y flathub org.flatpak.Builder" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- ensure Flathub is available for the runtime/SDK/extensions ---
|
||||||
|
flatpak remote-add --user --if-not-exists flathub \
|
||||||
|
https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||||
|
|
||||||
|
# --- offline crate cache (skip with ONLINE=1) -------------------------------------------
|
||||||
|
EXTRA_ARGS=()
|
||||||
|
if [ "${ONLINE:-0}" = "1" ]; then
|
||||||
|
echo "==> ONLINE build (cargo fetches from crates.io; non-reproducible)"
|
||||||
|
EXTRA_ARGS+=(--build-args=--share=network)
|
||||||
|
# The manifest references cargo-sources.json; provide an empty list so it stays valid.
|
||||||
|
[ -f packaging/flatpak/cargo-sources.json ] || echo '[]' > packaging/flatpak/cargo-sources.json
|
||||||
|
elif [ -f packaging/flatpak/cargo-sources.json ] && [ "${FORCE_GEN:-0}" != "1" ]; then
|
||||||
|
# Reuse a cargo-sources.json that was generated elsewhere (e.g. on a dev box with network +
|
||||||
|
# python aiohttp/toml, then rsynced to a build host that lacks them — like the Deck). The
|
||||||
|
# offline crate cache is a pure function of Cargo.lock, so this is reproducible. FORCE_GEN=1
|
||||||
|
# to regenerate anyway.
|
||||||
|
echo "==> reusing existing packaging/flatpak/cargo-sources.json (FORCE_GEN=1 to regenerate)"
|
||||||
|
else
|
||||||
|
echo "==> generating offline cargo-sources.json from Cargo.lock"
|
||||||
|
GEN=/tmp/flatpak-cargo-generator.py
|
||||||
|
if [ ! -f "$GEN" ]; then
|
||||||
|
curl -fsSL -o "$GEN" \
|
||||||
|
https://raw.githubusercontent.com/flatpak/flatpak-builder-tools/master/cargo/flatpak-cargo-generator.py
|
||||||
|
fi
|
||||||
|
# Needs python3 + aiohttp + toml. On a host that lacks them (e.g. the Deck), generate on the
|
||||||
|
# Mac / a dev box instead and rsync the result next to the manifest (reused by the branch above).
|
||||||
|
python3 "$GEN" Cargo.lock -o packaging/flatpak/cargo-sources.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- build into a local ostree repo, then export a single-file bundle --------------------
|
||||||
|
echo "==> flatpak-builder ($APP_ID, version $VERSION)"
|
||||||
|
"${FPB[@]}" --user --force-clean --disable-rofiles-fuse \
|
||||||
|
--install-deps-from=flathub \
|
||||||
|
"${EXTRA_ARGS[@]}" \
|
||||||
|
--repo="$ROOTDIR/.flatpak-repo" \
|
||||||
|
"$ROOTDIR/.flatpak-build" "$MANIFEST"
|
||||||
|
|
||||||
|
mkdir -p dist
|
||||||
|
flatpak build-bundle "$ROOTDIR/.flatpak-repo" "$BUNDLE" "$APP_ID"
|
||||||
|
echo "built $BUNDLE"
|
||||||
|
ls -lh "$BUNDLE"
|
||||||
|
echo
|
||||||
|
echo "install: flatpak install --user -y $BUNDLE"
|
||||||
|
echo "run: flatpak run $APP_ID (or: flatpak run $APP_ID --connect host:port)"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=Punktfunk
|
||||||
|
Comment=Stream a remote punktfunk host
|
||||||
|
Exec=punktfunk-client
|
||||||
|
Icon=io.unom.Punktfunk
|
||||||
|
Terminal=false
|
||||||
|
Categories=Network;Game;
|
||||||
|
Keywords=streaming;remote;game;moonlight;
|
||||||
|
StartupNotify=true
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- AppStream metainfo for the punktfunk Linux client flatpak. Validate with:
|
||||||
|
appstreamcli validate packaging/flatpak/io.unom.Punktfunk.metainfo.xml
|
||||||
|
The component id MUST equal the flatpak app-id; the <launchable> MUST name the installed
|
||||||
|
desktop file (io.unom.Punktfunk.desktop). -->
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>io.unom.Punktfunk</id>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>MIT OR Apache-2.0</project_license>
|
||||||
|
<name>Punktfunk</name>
|
||||||
|
<summary>Low-latency desktop and game streaming client</summary>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
Punktfunk is the native Linux client for the punktfunk low-latency desktop and game
|
||||||
|
streaming stack. It discovers hosts on the LAN over mDNS, trusts them via certificate
|
||||||
|
pinning with a SPAKE2 PIN pairing ceremony, and streams HEVC video (GF(2^16) Leopard
|
||||||
|
FEC plus AES-GCM over UDP, with a QUIC control plane) at exactly the requested
|
||||||
|
resolution and refresh rate — no scaling.
|
||||||
|
</p>
|
||||||
|
<p>Features:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Hardware-accelerated HEVC decode (VAAPI zero-copy on AMD and Intel, software fallback)</li>
|
||||||
|
<li>PipeWire audio playback and microphone uplink</li>
|
||||||
|
<li>Full gamepad support including DualSense touchpad, motion, adaptive triggers and lightbar (SDL3)</li>
|
||||||
|
<li>LAN host discovery, TOFU fingerprint pinning and PIN pairing</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<launchable type="desktop-id">io.unom.Punktfunk.desktop</launchable>
|
||||||
|
|
||||||
|
<categories>
|
||||||
|
<category>Network</category>
|
||||||
|
<category>Game</category>
|
||||||
|
<category>RemoteAccess</category>
|
||||||
|
</categories>
|
||||||
|
|
||||||
|
<keywords>
|
||||||
|
<keyword>streaming</keyword>
|
||||||
|
<keyword>remote</keyword>
|
||||||
|
<keyword>game</keyword>
|
||||||
|
<keyword>moonlight</keyword>
|
||||||
|
</keywords>
|
||||||
|
|
||||||
|
<url type="homepage">https://git.unom.io/unom/punktfunk</url>
|
||||||
|
<url type="bugtracker">https://git.unom.io/unom/punktfunk/issues</url>
|
||||||
|
<url type="vcs-browser">https://git.unom.io/unom/punktfunk</url>
|
||||||
|
|
||||||
|
<developer id="io.unom">
|
||||||
|
<name>unom</name>
|
||||||
|
</developer>
|
||||||
|
|
||||||
|
<content_rating type="oars-1.1"/>
|
||||||
|
|
||||||
|
<branding>
|
||||||
|
<color type="primary" scheme_preference="light">#a79ff8</color>
|
||||||
|
<color type="primary" scheme_preference="dark">#6c5bf3</color>
|
||||||
|
</branding>
|
||||||
|
|
||||||
|
<!-- Bump on each release; the version/date should track the published bundle. -->
|
||||||
|
<releases>
|
||||||
|
<release version="0.0.1" date="2026-06-15">
|
||||||
|
<description>
|
||||||
|
<p>Initial flatpak packaging of the native Linux client for the Steam Deck and Wayland desktops.</p>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
</releases>
|
||||||
|
</component>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg width="1000" height="1000" viewBox="0 0 1000 1000" version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<!-- Brand mark: three overlapping circles (flattened from clients/apple punktfunk_Logo.icon).
|
||||||
|
Order back-to-front: large violet circle (layer 1), deep-purple circle (layer 2),
|
||||||
|
light lens highlight (layer 3). -->
|
||||||
|
<rect x="0" y="0" width="1000" height="1000" rx="180" ry="180" fill="#1c1530"/>
|
||||||
|
<path d="M403.037,791.672c107.586,0 194.41,-86.824 194.41,-194.41c0,-107.586 -86.824,-194.41 -194.41,-194.41c-107.586,0 -194.41,86.824 -194.41,194.41c0,107.586 86.824,194.41 194.41,194.41Z" fill="#a79ff8"/>
|
||||||
|
<path d="M735.276,540.321c76.075,-76.075 76.075,-198.862 0,-274.937c-76.075,-76.075 -198.862,-76.075 -274.937,0c-76.075,76.075 -76.075,198.862 0,274.937c76.075,76.075 198.862,76.075 274.937,0Z" fill="#6c5bf3"/>
|
||||||
|
<path d="M647.84,590.737c-64.853,17.403 -136.871,0.597 -187.885,-50.416c-51.013,-51.013 -67.819,-123.032 -50.416,-187.885c64.853,-17.403 136.871,-0.597 187.885,50.416c51.013,51.013 67.819,123.032 50.416,187.885Z" fill="#d2c9fb"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,159 @@
|
|||||||
|
# Flatpak manifest for the native punktfunk Linux client (crate punktfunk-client-linux,
|
||||||
|
# binary `punktfunk-client`). Built into a single-file `.flatpak` bundle and published to
|
||||||
|
# Gitea's generic package registry (see .gitea/workflows/flatpak.yml + packaging/flatpak/README.md).
|
||||||
|
#
|
||||||
|
# Why flatpak for the CLIENT (the host stays an RPM/deb — see packaging/README.md "Why not
|
||||||
|
# Flatpak"): on SteamOS the Steam Deck's /usr is read-only and image-based, so a bare
|
||||||
|
# `punktfunk-client` binary in ~/.local/bin can't bring its own libadwaita / SDL3 (both
|
||||||
|
# MISSING from the SteamOS system) — but flatpak is the Deck's native, update-survivable app
|
||||||
|
# path (the user already runs Moonlight + chiaki-ng as flatpaks). Unlike the host, the client
|
||||||
|
# is sandbox-friendly: it only needs the GPU render node, the host PipeWire socket, the
|
||||||
|
# network, Wayland, hidraw for DualSense, and its config dir — all expressible as finish-args.
|
||||||
|
#
|
||||||
|
# Runtime: GNOME 50 ships GTK 4.20 and libadwaita 1.8 — both far exceed the crate floors
|
||||||
|
# (gtk4 0.11 "v4_16", libadwaita 0.9 "v1_5"). GNOME 50 is built on freedesktop-sdk 25.08, so
|
||||||
|
# `org.freedesktop.Sdk.Extension.rust-stable` resolves to //25.08 (rustc 1.96 — the GTK4 dep
|
||||||
|
# chain, e.g. pango-sys 0.22, needs >= 1.92, which the older GNOME-48/24.08 rust-stable at 1.89
|
||||||
|
# could NOT satisfy). GNOME 50 is also a *supported* runtime (GNOME 48 went EOL in March 2026).
|
||||||
|
# libopus and the PipeWire client lib are in the freedesktop base; SDL3 is NOT, so it is built
|
||||||
|
# from source as a bundled module.
|
||||||
|
#
|
||||||
|
# HEVC decode: the base runtime's libavcodec is a stripped build (no encumbered codecs). The
|
||||||
|
# freedesktop runtime declares `org.freedesktop.Platform.codecs-extra` as a built-in extension
|
||||||
|
# point (directory lib/x86_64-linux-gnu/codecs-extra, add-ld-path lib, auto-downloaded with the
|
||||||
|
# runtime), whose full libavcodec.so.61 transparently shadows the base one at runtime. So HEVC
|
||||||
|
# (software + VAAPI) works with NO app-side codec extension to declare — we just build against
|
||||||
|
# the SDK's linkable libavcodec.so.61 and let the runtime swap in the capable build.
|
||||||
|
app-id: io.unom.Punktfunk
|
||||||
|
runtime: org.gnome.Platform
|
||||||
|
runtime-version: '50'
|
||||||
|
sdk: org.gnome.Sdk
|
||||||
|
# Build-time SDK extensions:
|
||||||
|
# - rust-stable: cargo/rustc 1.96 + the bundled mold linker (/usr/lib/sdk/rust-stable/bin).
|
||||||
|
# - llvm20: provides libclang (/usr/lib/sdk/llvm20/lib), which bindgen needs — ffmpeg-sys-next
|
||||||
|
# and sdl3-sys generate their FFI bindings via bindgen at build time. The base SDK ships no
|
||||||
|
# clang/libclang, so without this the build panics ("Unable to find libclang").
|
||||||
|
# Both are added to PATH / LIBCLANG_PATH in build-options below.
|
||||||
|
sdk-extensions:
|
||||||
|
- org.freedesktop.Sdk.Extension.rust-stable
|
||||||
|
- org.freedesktop.Sdk.Extension.llvm20
|
||||||
|
command: punktfunk-client
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
- /include
|
||||||
|
- /lib/pkgconfig
|
||||||
|
- /lib/cmake
|
||||||
|
- /share/aclocal
|
||||||
|
- /man
|
||||||
|
- /share/man
|
||||||
|
- '*.a'
|
||||||
|
- '*.la'
|
||||||
|
|
||||||
|
finish-args:
|
||||||
|
# --- display ---
|
||||||
|
- --socket=wayland # GTK4 native Wayland window (the client is Wayland-first)
|
||||||
|
- --socket=fallback-x11 # Xwayland fallback when no Wayland socket is exposed
|
||||||
|
- --share=ipc # required alongside X11 for shared-memory surfaces
|
||||||
|
# --- GPU + all input devices ---
|
||||||
|
# --device=all (not just --device=dri): covers the GPU render node (VAAPI HEVC decode + GL),
|
||||||
|
# evdev joysticks, AND the hidraw CHAR devices SDL3's HIDAPI needs for DualSense touchpad/
|
||||||
|
# motion/adaptive-triggers/lightbar. flatpak cannot bind individual /dev/hidrawN via
|
||||||
|
# --filesystem (they are char devices — "unsupported type 0o20000"), and there is no granular
|
||||||
|
# --device=hidraw; --device=all is what game/emulator flatpaks (RetroArch, Dolphin) use. We
|
||||||
|
# self-host via the Gitea generic registry — NOT Flathub — so its --device=all review rule
|
||||||
|
# does not apply.
|
||||||
|
- --device=all
|
||||||
|
- --filesystem=/run/udev:ro # SDL/HIDAPI enumerates devices via udev
|
||||||
|
# --- audio: PipeWire via its PulseAudio shim — covers playback AND mic uplink. SteamOS
|
||||||
|
# exposes PipeWire-pulse here; --socket=pulseaudio is the portable arg Moonlight/chiaki
|
||||||
|
# also use on the Deck (a bare --socket=pipewire would also need the camera/portal dance
|
||||||
|
# for capture; the pulse shim gives mic + speaker in one grant). ---
|
||||||
|
- --socket=pulseaudio
|
||||||
|
# --- network: QUIC control + UDP data plane + mDNS discovery (_punktfunk._udp) ---
|
||||||
|
- --share=network
|
||||||
|
# --- persistent client identity / pairing store (shared with punktfunk-client-rs) ---
|
||||||
|
- --filesystem=~/.config/punktfunk:create # client-{cert,key}.pem, known-hosts, settings
|
||||||
|
|
||||||
|
build-options:
|
||||||
|
append-path: /usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin
|
||||||
|
# The rust build resolves everything via pkg-config: gtk4/libadwaita/pipewire/opus AND a
|
||||||
|
# linkable libavcodec.so.61 from org.gnome.Sdk//50 (the multiarch /usr dir), plus the bundled
|
||||||
|
# SDL3's .pc from /app. (At runtime the codecs-extra extension swaps in the HEVC-capable
|
||||||
|
# libavcodec — see the header.)
|
||||||
|
env:
|
||||||
|
PKG_CONFIG_PATH: /app/lib/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig
|
||||||
|
# bindgen (ffmpeg-sys-next / sdl3-sys) loads libclang from the llvm20 extension.
|
||||||
|
LIBCLANG_PATH: /usr/lib/sdk/llvm20/lib
|
||||||
|
# mold (shipped in rust-stable) speeds the ~450-crate link on the Deck APU.
|
||||||
|
RUSTFLAGS: -C link-arg=-fuse-ld=mold
|
||||||
|
|
||||||
|
modules:
|
||||||
|
# ---------------------------------------------------------------------------------------
|
||||||
|
# SDL3 — NOT provided as a linkable libSDL3.so.0 by org.gnome.Platform/freedesktop-sdk
|
||||||
|
# 25.08, and there is no SDL3 recipe in flathub/shared-modules. Build it from source.
|
||||||
|
# Pinned to 3.4.10 to match the crate exactly: sdl3-sys is `0.6.6+SDL-3.4.10`, i.e. its
|
||||||
|
# bindings target SDL 3.4.10 — building an older SDL risks missing symbols at link time.
|
||||||
|
# HIDAPI is enabled (DualSense touchpad/motion/triggers/lightbar over hidraw).
|
||||||
|
# ---------------------------------------------------------------------------------------
|
||||||
|
- name: sdl3
|
||||||
|
buildsystem: cmake-ninja
|
||||||
|
config-opts:
|
||||||
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
|
- -DSDL_SHARED=ON
|
||||||
|
- -DSDL_STATIC=OFF
|
||||||
|
- -DSDL_HIDAPI=ON # DualSense full fidelity over hidraw
|
||||||
|
- -DSDL_TEST_LIBRARY=OFF
|
||||||
|
- -DSDL_EXAMPLES=OFF
|
||||||
|
sources:
|
||||||
|
- type: archive
|
||||||
|
url: https://github.com/libsdl-org/SDL/releases/download/release-3.4.10/SDL3-3.4.10.tar.gz
|
||||||
|
# `sha256sum SDL3-3.4.10.tar.gz` (verified 2026-06-15). Bump url + sha together.
|
||||||
|
sha256: 12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785
|
||||||
|
x-checker-data:
|
||||||
|
type: anitya
|
||||||
|
project-id: 4974
|
||||||
|
stable-only: true
|
||||||
|
url-template: https://github.com/libsdl-org/SDL/releases/download/release-$version/SDL3-$version.tar.gz
|
||||||
|
cleanup:
|
||||||
|
- /bin
|
||||||
|
- /include
|
||||||
|
- /lib/cmake
|
||||||
|
- /lib/pkgconfig
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------
|
||||||
|
# The client. cargo-sources.json is the GENERATED offline crate cache:
|
||||||
|
# python3 flatpak-cargo-generator.py Cargo.lock -o packaging/flatpak/cargo-sources.json
|
||||||
|
# (run from the repo root; the CI step does exactly this). With it present the build is fully
|
||||||
|
# offline (CARGO_NET_OFFLINE). For quick LOCAL iteration WITHOUT regenerating it, drop the
|
||||||
|
# cargo-sources.json source and pass --build-args=--share=network to flatpak-builder
|
||||||
|
# (non-reproducible; cargo fetches from crates.io during the build).
|
||||||
|
# ---------------------------------------------------------------------------------------
|
||||||
|
- name: punktfunk-client
|
||||||
|
buildsystem: simple
|
||||||
|
build-options:
|
||||||
|
env:
|
||||||
|
CARGO_HOME: /run/build/punktfunk-client/cargo
|
||||||
|
CARGO_NET_OFFLINE: 'true'
|
||||||
|
build-commands:
|
||||||
|
- cargo --offline build --release --locked -p punktfunk-client-linux
|
||||||
|
- install -Dm0755 target/release/punktfunk-client ${FLATPAK_DEST}/bin/punktfunk-client
|
||||||
|
# Desktop entry (renamed to the app id; Exec is the in-sandbox binary).
|
||||||
|
- install -Dm0644 packaging/flatpak/io.unom.Punktfunk.desktop
|
||||||
|
${FLATPAK_DEST}/share/applications/io.unom.Punktfunk.desktop
|
||||||
|
# AppStream metainfo (required for a well-formed flatpak / Software listings).
|
||||||
|
- install -Dm0644 packaging/flatpak/io.unom.Punktfunk.metainfo.xml
|
||||||
|
${FLATPAK_DEST}/share/metainfo/io.unom.Punktfunk.metainfo.xml
|
||||||
|
# Scalable icon named for the app id (GNOME runtime renders SVG via librsvg).
|
||||||
|
- install -Dm0644 packaging/flatpak/io.unom.Punktfunk.svg
|
||||||
|
${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/io.unom.Punktfunk.svg
|
||||||
|
sources:
|
||||||
|
# The repo checkout. For a Flathub/published build, replace with a pinned git source:
|
||||||
|
# - type: git
|
||||||
|
# url: https://git.unom.io/unom/punktfunk
|
||||||
|
# tag: vX.Y.Z
|
||||||
|
# commit: <sha>
|
||||||
|
# For ON-DECK / CI builds we build the checked-out working tree in place:
|
||||||
|
- type: dir
|
||||||
|
path: ../..
|
||||||
|
# Generated offline crate cache (see the comment block above). Remove for --share=network.
|
||||||
|
- cargo-sources.json
|
||||||
Reference in New Issue
Block a user