ref:0be67ff8584321aff456c766830e1bf113c9ee08

build(Linux): add Alpine packages (#4987)

SHA: 0be67ff8584321aff456c766830e1bf113c9ee08
Author: Dave Lane <42013603+ReenigneArcher@users.noreply.github.com>
Date: 2026-09-10 17:37
Parents: f2c9f34
9 files changed +421 -2
Type
.github/workflows/ci-alpine.yml +173 −0
@@ -1,0 +1,173 @@
---
name: CI-Alpine
permissions: {}
on:
workflow_call:
inputs:
release_commit:
required: true
type: string
release_version:
required: true
type: string
jobs:
build_alpine:
name: ${{ matrix.arch }}
env:
ALPINE_PACKAGE_DIR: cmake-build-alpine-package
BRANCH: ${{ github.head_ref || github.ref_name }}
BUILD_VERSION: ${{ inputs.release_version }}
CLONE_URL: >-
${{
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.clone_url ||
github.event.repository.clone_url
}}
COMMIT: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.release_commit }}
permissions:
contents: read
runs-on: ${{ matrix.runner }}
container:
image: alpine:${{ matrix.alpine_version }}
strategy:
fail-fast: false
matrix:
alpine_version:
- '3.24'
arch:
- x86_64
- aarch64
include:
- arch: x86_64
runner: ubuntu-latest
- arch: aarch64
runner: ubuntu-24.04-arm
steps:
- name: Fix arm64 Alpine container
if: runner.arch == 'ARM64'
uses: laverdet/alpine-arm64@7f0f72ee2f71eb2324e5888e8b6e42b1b53e6160 # v1.0.0
- name: Install packaging tools
run: |
set -eux
apk add --no-cache \
alpine-sdk \
atools \
bash \
cmake \
file \
git \
libcap-utils \
pax-utils \
sudo
apk update
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: recursive
- name: Setup builder user
run: |
set -eux
adduser -D builder
addgroup builder abuild
mkdir -p /etc/sudoers.d
echo 'builder ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/builder
chmod 440 /etc/sudoers.d/builder
chown -R builder:abuild "${GITHUB_WORKSPACE}"
sudo -u builder abuild-keygen -a -i -n
- name: Configure APKBUILD
run: |
set -eux
sub_version=""
if [ "${BRANCH}" != "master" ]; then
# Alpine development-version suffixes accept a numeric component,
# while hexadecimal commit IDs are rejected by abuild.
sub_version="_git${GITHUB_RUN_NUMBER}"
fi
cmake -S . -B "${ALPINE_PACKAGE_DIR}" \
-DSUNSHINE_CONFIGURE_ONLY=ON \
-DSUNSHINE_CONFIGURE_APKBUILD=ON \
-DSUNSHINE_SUB_VERSION="${sub_version}"
mkdir -p apk
mv "${ALPINE_PACKAGE_DIR}/APKBUILD" apk/
mv "${ALPINE_PACKAGE_DIR}/sunshine.post-install" apk/
mv "${ALPINE_PACKAGE_DIR}/sunshine.post-upgrade" apk/
chown -R builder:abuild apk
cd apk
sh -n sunshine.post-install
sh -n sunshine.post-upgrade
sudo -u builder apkbuild-lint APKBUILD
- name: Build Alpine package
id: build
working-directory: apk
run: |
set -eux
echo "::add-matcher::.github/matchers/gcc.json"
sudo -u builder env \
_coverage_dir="${GITHUB_WORKSPACE}/coverage" \
_run_unit_tests=true \
_source_dir="${GITHUB_WORKSPACE}" \
abuild -r
echo "::remove-matcher owner=gcc::"
- name: Install and verify package
id: package
run: |
set -eux
package_file="$(find /home/builder/packages -type f -name 'sunshine-*.apk' -print -quit)"
test -n "${package_file}"
apk add --allow-untrusted "${package_file}"
file /usr/bin/sunshine | tee sunshine-file.txt
grep -Fq 'dynamically linked' sunshine-file.txt
ldd /usr/bin/sunshine | tee sunshine-ldd.txt
grep -Eq 'libc\.musl|ld-musl' sunshine-ldd.txt
scanelf -n /usr/bin/sunshine
/usr/bin/sunshine --version
rule_file="$(find /lib /usr/lib -path '*/udev/rules.d/60-sunshine.rules' -print -quit)"
test -n "${rule_file}"
grep -Fq 'IMPORT{parent}="HID_*"' "${rule_file}"
grep -Fq 'ENV{HID_PHYS}=="libvirtualhid/uhid/*"' "${rule_file}"
test -e /sys/class/mem/null
if ! udevadm test --action=add /sys/class/mem/null > udevadm-test.txt 2>&1; then
cat udevadm-test.txt
exit 1
fi
test -f /etc/modules-load.d/60-sunshine.conf
grep -Fxq 'uhid' /etc/modules-load.d/60-sunshine.conf
getcap /usr/bin/sunshine | grep -Eq 'cap_sys_admin,cap_sys_nice[=+]p'
mkdir -p artifacts
cp "${package_file}" \
"artifacts/sunshine_${BUILD_VERSION}_alpine${{ matrix.alpine_version }}_${{ matrix.arch }}.apk"
- name: Upload coverage artifact
if: >-
always() &&
(steps.build.outcome == 'success' || steps.build.outcome == 'failure')
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-Alpine-${{ matrix.arch }}
path: |
coverage/coverage.xml
coverage/tests/test_results.xml
if-no-files-found: error
- name: Upload artifacts
if: steps.package.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-Alpine-${{ matrix.arch }}
path: artifacts/
if-no-files-found: error
.github/workflows/ci-cloudsmith.yml +2 −1
@@ -6,7 +6,7 @@
workflow_call:
inputs:
release_tag:
description: GitHub release tag containing the DEB assets.
description: GitHub release tag containing the package assets.
required: true
type: string
repository:
@@ -33,5 +33,6 @@
mkdir -p artifacts/packages
gh release download "${RELEASE_TAG}" \
--dir artifacts/packages \
--pattern '*.apk' \
--pattern '*.deb' \
--pattern '*.rpm'
.github/workflows/ci.yml +16 −0
@@ -129,6 +129,16 @@
release_commit: ${{ needs.release-setup.outputs.release_commit }}
release_version: ${{ needs.release-setup.outputs.release_version }}
build-alpine:
name: Alpine
needs: release-setup
permissions:
contents: read
uses: ./.github/workflows/ci-alpine.yml
with:
release_commit: ${{ needs.release-setup.outputs.release_commit }}
release_version: ${{ needs.release-setup.outputs.release_version }}
build-linux-copr:
name: Linux Copr
if: github.event_name != 'push' # releases are handled directly in ci-release.yml
@@ -189,6 +199,7 @@
- build-freebsd
- build-linux
- build-archlinux
- build-alpine
- build-linux-flatpak
- build-macos
- build-homebrew
@@ -208,6 +219,10 @@
coverage: true
- name: Archlinux
coverage: true
- name: Alpine-x86_64
coverage: true
- name: Alpine-aarch64
coverage: true
- name: macOS-arm64
coverage: true
- name: macOS-x86_64
@@ -268,6 +283,7 @@
needs:
- release-setup
- build-archlinux
- build-alpine
- build-docker
- build-freebsd
- build-homebrew
cmake/dependencies/ffmpeg.cmake +5 −1
@@ -68,6 +68,10 @@
set(FFMPEG_PREPARED_BINARIES "${FFMPEG_EXTRACT_DIR}/ffmpeg")
# Set the archive filename based on architecture
set(FFMPEG_ARCHIVE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}-ffmpeg.tar.gz")
if(NOT DEFINED FFMPEG_RELEASE_ASSET_NAME)
set(FFMPEG_ARCHIVE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}-ffmpeg.tar.gz")
else()
set(FFMPEG_ARCHIVE_NAME "${FFMPEG_RELEASE_ASSET_NAME}")
endif()
set(FFMPEG_ARCHIVE_PATH "${FFMPEG_VERSION_DIR}/${FFMPEG_ARCHIVE_NAME}")
set(FFMPEG_DOWNLOAD_URL "${FFMPEG_RELEASE_URL}/${FFMPEG_ARCHIVE_NAME}")
cmake/prep/options.cmake +2 −0
@@ -54,5 +54,7 @@
"Enable a Flatpak build." OFF)
option(SUNSHINE_CONFIGURE_PKGBUILD
"Configure files required for AUR. Recommended to use with SUNSHINE_CONFIGURE_ONLY" OFF)
option(SUNSHINE_CONFIGURE_APKBUILD
"Configure files required for an Alpine Linux package. Recommended to use with SUNSHINE_CONFIGURE_ONLY" OFF)
option(SUNSHINE_CONFIGURE_FLATPAK_MAN
"Configure manifest file required for Flatpak build. Recommended to use with SUNSHINE_CONFIGURE_ONLY" OFF)
cmake/prep/special_package_configuration.cmake +7 −0
@@ -37,6 +37,13 @@
configure_file(packaging/linux/Arch/sunshine.install sunshine.install @ONLY)
endif()
# configure the Alpine Linux APKBUILD
if(${SUNSHINE_CONFIGURE_APKBUILD})
configure_file(packaging/linux/Alpine/APKBUILD APKBUILD @ONLY)
configure_file(packaging/linux/Alpine/sunshine.post-install sunshine.post-install COPYONLY)
configure_file(packaging/linux/Alpine/sunshine.post-install sunshine.post-upgrade COPYONLY)
endif()
# configure the flatpak manifest
if(${SUNSHINE_CONFIGURE_FLATPAK_MAN})
configure_file(packaging/linux/flatpak/${PROJECT_FQDN}.yml ${PROJECT_FQDN}.yml @ONLY)
docs/getting_started.md +21 −0
@@ -92,6 +92,27 @@
</tr>
</table>
#### Alpine Linux
> [!IMPORTANT]
> The Alpine package is dynamically linked against Alpine 3.24 libraries. CUDA and NVFBC capture are not available in
> this package, but the other capture and encoding backends supported by the system remain enabled.
##### Install
1. Download `sunshine_{version}_alpine{distro-version}_{arch}.apk` from the [latest release][latest-release].
2. Install the package as root. Release APKs use a per-build signing key, so explicitly allow the downloaded package.
```sh
apk add --allow-untrusted ./sunshine_{version}_alpine{distro-version}_{arch}.apk
```
##### Uninstall
```sh
apk del sunshine
```
#### AppImage
> [!CAUTION]
packaging/linux/Alpine/APKBUILD +166 −0
@@ -1,0 +1,166 @@
# Edit on GitHub: https://github.com/LizardByte/Sunshine/blob/master/packaging/linux/Alpine/APKBUILD
# Reference: https://wiki.alpinelinux.org/wiki/APKBUILD_Reference
: "${_run_unit_tests:=false}" # Run the test suite and generate coverage; useful in CI.
: "${_coverage_dir:=}" # Copy CI coverage artifacts to this directory when set.
: "${_source_dir:=}" # Build an existing checkout instead of cloning; useful in CI.
: "${_commit:=@GITHUB_COMMIT@}"
pkgname=sunshine
pkgver=@PROJECT_VERSION@@SUNSHINE_SUB_VERSION@
pkgrel=0
pkgdesc="@PROJECT_DESCRIPTION@"
url="@PROJECT_HOMEPAGE_URL@"
arch="x86_64 aarch64"
license="GPL-3.0-only"
install="$pkgname.post-install $pkgname.post-upgrade"
options="net"
# Shared-library dependencies are added automatically by abuild. These packages
# provide runtime tools and Qt platform plugins that ELF dependency scanning
# cannot discover.
depends="
eudev
kmod
libcap-utils
qt6-qtbase-x11
xrandr
"
makedepends="
appstream
appstream-glib
build-base
cmake
curl-dev
desktop-file-utils
eudev-dev
freetype-dev
git
glib-dev
glslang
gnutls-dev
lame-dev
libass-dev
libcap-dev
libdrm-dev
libevdev-dev
libtool
libva-dev
libvorbis-dev
libx11-dev
libxcb-dev
libxfixes-dev
libxrandr-dev
libxtst-dev
linux-headers
mesa-dev
meson
miniupnpc-dev
nasm
nodejs
npm
numactl-dev
openssl-dev
opus-dev
pipewire-dev
pkgconf
pulseaudio-dev
py3-jinja2
qt6-qtbase-dev
qt6-qtsvg-dev
samurai
sdl2-dev
shaderc-dev
vulkan-loader-dev
wayland-dev
zlib-dev
"
checkdepends="
gcovr
imagemagick
xvfb-run
"
case "$_run_unit_tests" in
[tT]*) ;;
*) options="$options !check" ;;
esac
if [ -n "$_source_dir" ]; then
builddir="$_source_dir"
else
builddir="$srcdir/$pkgname"
source="$pkgname::git+@GITHUB_CLONE_URL@#commit=$_commit"
sha512sums="SKIP"
fi
_build_dir="$srcdir/cmake-build-alpine"
prepare() {
default_prepare
if [ -z "$_source_dir" ]; then
git -C "$builddir" submodule update --recursive --init --depth 1
fi
}
build() {
export BRANCH="@GITHUB_BRANCH@"
export BUILD_VERSION="@BUILD_VERSION@"
export COMMIT="$_commit"
_build_tests=OFF
case "$_run_unit_tests" in
[tT]*) _build_tests=ON ;;
esac
cmake -S "$builddir" -B "$_build_dir" -G Ninja -Wno-dev \
-DBUILD_DOCS=OFF \
-DBUILD_TESTS="$_build_tests" \
-DBUILD_WERROR=ON \
-DCMAKE_INSTALL_LIBDIR=lib \
-DCMAKE_INSTALL_PREFIX=/usr \
-DFFMPEG_RELEASE_ASSET_NAME="Alpine-$CARCH-ffmpeg.tar.gz" \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_ENABLE_CUDA=OFF \
-DCUDA_FAIL_ON_MISSING=OFF \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_PUBLISHER_NAME=LizardByte \
-DSUNSHINE_PUBLISHER_WEBSITE=https://app.lizardbyte.dev \
-DSUNSHINE_PUBLISHER_ISSUE_URL=https://app.lizardbyte.dev/support
appstreamcli validate --no-net "$_build_dir/dev.lizardbyte.app.Sunshine.metainfo.xml"
appstream-util validate "$_build_dir/dev.lizardbyte.app.Sunshine.metainfo.xml"
desktop-file-validate "$_build_dir/dev.lizardbyte.app.Sunshine.desktop"
desktop-file-validate "$_build_dir/dev.lizardbyte.app.Sunshine.terminal.desktop"
cmake --build "$_build_dir"
}
check() {
cd "$_build_dir/tests"
GCOV_EXIT_AT_ERROR=1 xvfb-run -a ./test_sunshine \
--gtest_color=yes \
--gtest_output=xml:test_results.xml
cd "$_build_dir"
gcovr --gcov-executable gcov . -r "$builddir/src" \
--exclude-noncode-lines \
--exclude-throw-branches \
--exclude-unreachable-branches \
--xml-pretty \
-o coverage.xml
if [ -n "$_coverage_dir" ]; then
mkdir -p "$_coverage_dir/tests"
cp coverage.xml "$_coverage_dir/coverage.xml"
cp tests/test_results.xml "$_coverage_dir/tests/test_results.xml"
fi
./sunshine --version
}
package() {
DESTDIR="$pkgdir" cmake --install "$_build_dir"
install -Dm644 "$builddir/src_assets/linux/misc/60-sunshine.conf" \
"$pkgdir/etc/modules-load.d/60-sunshine.conf"
}
packaging/linux/Alpine/sunshine.post-install +29 −0
@@ -1,0 +1,29 @@
#!/bin/sh
set_sunshine_capabilities() {
if command -v setcap >/dev/null 2>&1; then
setcap cap_sys_admin,cap_sys_nice+p /usr/bin/sunshine || \
echo "Warning: unable to set capabilities on /usr/bin/sunshine" >&2
fi
}
reload_udev_rules() {
if command -v udevadm >/dev/null 2>&1; then
udevadm control --reload-rules || true
udevadm trigger --property-match=DEVNAME=/dev/uinput || true
udevadm trigger --property-match=DEVNAME=/dev/uhid || true
udevadm trigger --subsystem-match=hidraw || true
udevadm trigger --subsystem-match=input || true
fi
}
load_input_modules() {
if command -v modprobe >/dev/null 2>&1; then
modprobe uinput || true
modprobe uhid || true
fi
}
set_sunshine_capabilities
reload_udev_rules
load_input_modules