name: linux-packages
on:
workflow_dispatch:
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
APP_ID: io.github.shakfu.playr
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- { target: x86_64-unknown-linux-gnu, arch: amd64, os: ubuntu-22.04 }
- { target: aarch64-unknown-linux-gnu, arch: arm64, os: ubuntu-22.04-arm }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- name: Install ALSA headers and window libraries
run: sudo apt-get update && sudo apt-get install -y libasound2-dev libxkbcommon-dev libwayland-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
- uses: dtolnay/rust-toolchain@stable
- name: Build
run: cargo build --profile dist --locked -p playr -p playr-gui -p playr-server --features playr/opus,playr-gui/opus,playr-server/opus
- uses: actions/upload-artifact@v7
with:
name: bin-${{ matrix.arch }}
path: |
target/dist/playr
target/dist/playr-gui
target/dist/playr-server
native:
needs: build
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
pattern: bin-*
path: bin
- name: Install nfpm
run: |
curl -sSfLo nfpm.deb https://github.com/goreleaser/nfpm/releases/download/v2.47.0/nfpm_2.47.0_amd64.deb
sudo dpkg -i nfpm.deb
- name: Package
run: |
# Artifacts do not keep the executable bit.
chmod 755 bin/*/*
export VERSION=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -1)
export MAINTAINER="$(git log -1 --format='%an <%ae>')"
mkdir -p staged out
# The packaged unit runs the packaged binary, not ~/.local/bin's.
sed 's|^ExecStart=%h/.local/bin/|ExecStart=/usr/bin/|' packaging/linux/playr-server.service > staged/playr-server.service
# Fields every package shares; each package's file adds the rest.
cat > common.yaml <<'EOF'
arch: ${ARCH}
platform: linux
version: ${VERSION}
release: 1
section: sound
maintainer: ${MAINTAINER}
homepage: https://github.com/shakfu/playr
license: MIT
archlinux:
packager: ${MAINTAINER}
EOF
cat > playr.yaml <<'EOF'
name: playr
description: playr's terminal music player, which plays local files and contacts nothing
contents:
- src: staged/bin/playr
dst: /usr/bin/playr
file_info: { mode: 0755 }
- src: README.md
dst: /usr/share/doc/playr/README.md
- src: CHANGELOG.md
dst: /usr/share/doc/playr/CHANGELOG.md
- src: LICENSE
dst: /usr/share/licenses/playr/LICENSE
overrides:
deb: { depends: [libasound2] }
rpm: { depends: [alsa-lib] }
archlinux: { depends: [alsa-lib] }
EOF
# playr-gui loads its window libraries at run time, so ldd does not
# list them; the packages name them.
cat > playr-gui.yaml <<'EOF'
name: playr-gui
description: playr's desktop window, which plays local files and contacts nothing
contents:
- src: staged/bin/playr-gui
dst: /usr/bin/playr-gui
file_info: { mode: 0755 }
- src: packaging/linux/playr.desktop
dst: /usr/share/applications/playr.desktop
- src: crates/playr-gui/assets/playr.png
dst: /usr/share/icons/hicolor/256x256/apps/playr.png
- src: LICENSE
dst: /usr/share/licenses/playr-gui/LICENSE
overrides:
deb: { depends: [libasound2, libxkbcommon0, libwayland-client0, libegl1] }
rpm: { depends: [alsa-lib, libxkbcommon, libwayland-client, mesa-libEGL] }
archlinux: { depends: [alsa-lib, libxkbcommon, wayland, libglvnd] }
EOF
cat > playr-server.yaml <<'EOF'
name: playr-server
description: playr's server, controlled from a web page and OSC
contents:
- src: staged/bin/playr-server
dst: /usr/bin/playr-server
file_info: { mode: 0755 }
- src: staged/playr-server.service
dst: /usr/lib/systemd/user/playr-server.service
- src: docs/server-guide.md
dst: /usr/share/doc/playr-server/server-guide.md
- src: LICENSE
dst: /usr/share/licenses/playr-server/LICENSE
overrides:
deb: { depends: [libasound2] }
rpm: { depends: [alsa-lib] }
archlinux: { depends: [alsa-lib] }
EOF
for pkg in playr playr-gui playr-server; do
cat common.yaml $pkg.yaml > nfpm-$pkg.yaml
# nfpm does not expand archlinux.packager.
sed -i "s|\${MAINTAINER}|$MAINTAINER|" nfpm-$pkg.yaml
done
for arch in amd64 arm64; do
# nfpm expands variables in fields such as arch, not in paths.
rm -rf staged/bin && cp -r bin/bin-$arch staged/bin
for pkg in playr playr-gui playr-server; do
for fmt in deb rpm archlinux; do
ARCH=$arch nfpm package -f nfpm-$pkg.yaml -p $fmt -t out/
done
done
done
ls -l out
- uses: actions/upload-artifact@v7
with:
name: native-packages
path: out/*
flatpak:
needs: build
runs-on: ubuntu-latest
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08
options: --privileged
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
name: bin-amd64
path: flatpak
- name: Manifest
run: |
cp packaging/linux/playr.desktop crates/playr-gui/assets/playr.png flatpak/
# Flatpak exports only files named by the app ID.
sed -i "s/^Icon=playr$/Icon=$APP_ID/" flatpak/playr.desktop
cat > flatpak/$APP_ID.yaml <<EOF
app-id: $APP_ID
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
command: playr-gui
finish-args:
- --share=ipc
- --socket=wayland
- --socket=fallback-x11
- --device=dri
# cpal's ALSA output reaches the host through the runtime's Pulse plugin.
- --socket=pulseaudio
# Music outside ~/Music needs: flatpak override --user --filesystem=PATH $APP_ID
- --filesystem=xdg-music
- --own-name=org.mpris.MediaPlayer2.playr
modules:
- name: playr
buildsystem: simple
build-commands:
- install -Dm755 playr-gui /app/bin/playr-gui
- install -Dm644 playr.desktop /app/share/applications/$APP_ID.desktop
- install -Dm644 playr.png /app/share/icons/hicolor/256x256/apps/$APP_ID.png
sources:
- { type: file, path: playr-gui }
- { type: file, path: playr.desktop }
- { type: file, path: playr.png }
EOF
cat flatpak/$APP_ID.yaml
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with:
manifest-path: flatpak/${{ env.APP_ID }}.yaml
bundle: playr.flatpak
cache: false
snap:
needs: build
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
name: bin-amd64
path: snap-build/payload/bin
- name: snapcraft.yaml
run: |
version=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -1)
mkdir -p snap-build/snap
chmod 755 snap-build/payload/bin/*
cp crates/playr-gui/assets/playr.png snap-build/payload/
# snapcraft rewrites Exec= to the snap's own command; the icon needs
# a path inside the snap.
sed 's|^Icon=playr$|Icon=${SNAP}/playr.png|' packaging/linux/playr.desktop > snap-build/payload/playr.desktop
# ALSA's default device, sent to the host's sound server.
printf 'pcm.!default pulse\nctl.!default pulse\n' > snap-build/payload/asound.conf
cat > snap-build/snap/snapcraft.yaml <<EOF
name: playr
base: core22
version: '$version'
summary: A minimal music player that plays local files
description: |
playr plays and samples a local music library, and contacts nothing.
It has a terminal program, a desktop window and a server
controlled from a web page and OSC.
license: MIT
grade: devel
confinement: strict
layout:
/etc/asound.conf:
bind-file: \$SNAP/asound.conf
/usr/lib/x86_64-linux-gnu/alsa-lib:
bind: \$SNAP/usr/lib/x86_64-linux-gnu/alsa-lib
/usr/share/alsa:
bind: \$SNAP/usr/share/alsa
slots:
mpris:
interface: mpris
name: playr
parts:
playr:
plugin: dump
source: payload
stage-packages:
- libasound2
- libasound2-plugins
apps:
playr:
command: bin/playr
plugs: [audio-playback, home, removable-media]
slots: [mpris]
playr-gui:
command: bin/playr-gui
desktop: playr.desktop
extensions: [gnome]
plugs: [audio-playback, home, removable-media]
slots: [mpris]
playr-server:
command: bin/playr-server
plugs: [audio-playback, home, removable-media, network, network-bind]
EOF
cat snap-build/snap/snapcraft.yaml
- uses: snapcore/action-build@v1
id: snapcraft
with:
path: snap-build
- uses: actions/upload-artifact@v7
with:
name: snap
path: ${{ steps.snapcraft.outputs.snap }}
collect:
needs: [native, flatpak, snap]
runs-on: ubuntu-22.04
steps:
- uses: actions/download-artifact@v8
with:
path: dist
merge-multiple: true
- name: Checksums
working-directory: dist
run: sha256sum *.deb *.rpm *.pkg.tar.zst *.flatpak *.snap > SHA256SUMS
- uses: actions/upload-artifact@v7
with:
name: playr-linux-packages
path: |
dist/*.deb
dist/*.rpm
dist/*.pkg.tar.zst
dist/*.flatpak
dist/*.snap
dist/SHA256SUMS