sunshine: init

This commit is contained in:
Mateusz Słodkowicz 2026-06-18 17:27:06 +02:00
parent be078046d0
commit b8eb02bb22
Signed by: materus
SSH Key Fingerprint: SHA256:rzVduzTiiszuYfLPYD0SDZV+g8lxhpcRgpbOZA1X0Uo
5 changed files with 3487 additions and 12 deletions

24
flake.lock generated
View File

@ -28,11 +28,11 @@
]
},
"locked": {
"lastModified": 1781189114,
"narHash": "sha256-5inaamLgUMWy+MOBE9ChF9QAF1o/74LFuHkI0W/9rqc=",
"lastModified": 1781788787,
"narHash": "sha256-YqlTCRRhGvNjcJejPeMuHrYQ/TVhOO2MV/nEGMWb8nk=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "486595d2cf49cfcd649b58a284fa11ac0e34da22",
"rev": "d456f483f157d4b706416005da226234b9c116ff",
"type": "github"
},
"original": {
@ -44,11 +44,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1780749050,
"narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=",
"lastModified": 1781577229,
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a799d3e3886da994fa307f817a6bc705ae538eeb",
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
"type": "github"
},
"original": {
@ -60,11 +60,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1780749050,
"narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=",
"lastModified": 1781577229,
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a799d3e3886da994fa307f817a6bc705ae538eeb",
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
"type": "github"
},
"original": {
@ -80,11 +80,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1781208515,
"narHash": "sha256-Ke76KeYDAnm/w3OGEviATXQMFw4QnRPFS/Wj6MWqM0Y=",
"lastModified": 1781794604,
"narHash": "sha256-+A52qDscDCQelWn+aSZA7iSv500HXRPmsO8KRR3mivg=",
"owner": "nix-community",
"repo": "NUR",
"rev": "d66d4c98290f2b8dc3487e43bd1eb3d038039ab2",
"rev": "8661c872ba8979434bf38bd2eb814d4f73578b38",
"type": "github"
},
"original": {

3036
pkgs/apps/sunshine/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,312 @@
{
lib,
stdenv,
fetchFromGitHub,
fetchzip,
autoPatchelfHook,
autoAddDriverRunpath,
makeWrapper,
buildNpmPackage,
nixosTests,
cmake,
avahi,
libevdev,
libpulseaudio,
libxtst,
libxrandr,
libxi,
libxfixes,
libxdmcp,
libx11,
libxcb,
openssl,
libopus,
boost,
pkg-config,
libdrm,
wayland,
wayland-scanner,
libffi,
libcap,
libgbm,
curl,
pcre,
pcre2,
python3,
libuuid,
libselinux,
libsepol,
libthai,
libdatrie,
libxkbcommon,
libepoxy,
libva,
libvdpau,
libglvnd,
numactl,
amf-headers,
svt-av1,
shaderc,
vulkan-loader,
libappindicator,
libnotify,
pipewire,
miniupnpc,
nlohmann_json,
config,
coreutils,
udevCheckHook,
cudaSupport ? config.cudaSupport,
cudaPackages ? { },
}:
let
inherit (stdenv.hostPlatform) isLinux;
stdenv' = if cudaSupport then cudaPackages.backendStdenv else stdenv;
# Upstream's cmake fetches a pre-built ffmpeg from LizardByte/build-deps at
# configure time. We can't do network I/O during the build, so fetch it via
# a fixed-output derivation and point cmake at it via FFMPEG_PREPARED_BINARIES.
# The tag must match the commit of the third-party/build-deps submodule pinned
# in the Sunshine release.
buildDepsTag = "v2026.516.30821";
ffmpegArch =
{
x86_64-linux = "Linux-x86_64";
}
.${stdenv.hostPlatform.system}
or (throw "sunshine: unsupported system ${stdenv.hostPlatform.system} for prebuilt ffmpeg");
ffmpegPrebuilt = fetchzip {
url = "https://github.com/LizardByte/build-deps/releases/download/${buildDepsTag}/${ffmpegArch}-ffmpeg.tar.gz";
# stripRoot defaults to true; the hash here matches what
# `nix-prefetch-url --unpack` produces, so the updater can refresh it
# with the built-in command (which also caches downloads by URL,
# unlike the empty-hash trick).
hash =
{
x86_64-linux = "sha256-VT+4qP2FaizCoIBBbBkzbYw4YOvGhuBUoZxWL0IYVZo=";
}
.${stdenv.hostPlatform.system};
};
in
stdenv'.mkDerivation (finalAttrs: {
pname = "sunshine";
version = "2026.516.143833";
src = fetchFromGitHub {
owner = "LizardByte";
repo = "Sunshine";
tag = "v${finalAttrs.version}";
hash = "sha256-3yuhOyW1Rqz4ddZ40z2ZzpAReZQFva0SL595XrnFB60=";
fetchSubmodules = true;
};
# build webui
ui = buildNpmPackage {
inherit (finalAttrs) src version;
pname = "sunshine-ui";
npmDepsHash = "sha256-YnNnuAdj/S5LGNytqIsmCApIec8DTWKF6VIJ7AXUctU=";
# use generated package-lock.json as upstream does not provide one
postPatch = ''
cp ${./package-lock.json} ./package-lock.json
'';
installPhase = ''
runHook preInstall
mkdir -p "$out"
cp -a . "$out"/
runHook postInstall
'';
};
postPatch = # don't look for npm since we build webui separately
''
substituteInPlace cmake/targets/common.cmake \
--replace-fail 'find_program(NPM npm REQUIRED)' ""
''
# use system boost instead of FetchContent.
# FETCH_CONTENT_BOOST_USED prevents Simple-Web-Server from re-finding boost
+ ''
sed -i -E 's/set\(BOOST_VERSION "[^"]*"\)/set(BOOST_VERSION "${boost.version}")/' \
cmake/dependencies/Boost_Sunshine.cmake
echo 'set(FETCH_CONTENT_BOOST_USED TRUE)' >> cmake/dependencies/Boost_Sunshine.cmake
''
# remove upstream dependency on systemd and udev
+ lib.optionalString isLinux ''
substituteInPlace cmake/packaging/linux.cmake \
--replace-fail 'find_package(Systemd)' "" \
--replace-fail 'find_package(Udev)' ""
# The remaining @VAR@ placeholders in the .desktop file (PROJECT_NAME,
# PROJECT_DESCRIPTION, PROJECT_FQDN, SUNSHINE_DESKTOP_ICON,
# CMAKE_INSTALL_FULL_DATAROOTDIR) are substituted by cmake's
# configure_file(... @ONLY) during the build.
substituteInPlace packaging/linux/dev.lizardbyte.app.Sunshine.desktop \
--replace-fail '/usr/bin/env systemctl start --u app-@PROJECT_FQDN@' 'sunshine'
substituteInPlace packaging/linux/app-dev.lizardbyte.app.Sunshine.service.in \
--replace-fail '/bin/sleep' '${lib.getExe' coreutils "sleep"}'
'';
nativeBuildInputs = [
cmake
pkg-config
# glad's generator needs Jinja2 + setuptools at configure time;
# GLAD_SKIP_PIP_INSTALL=ON tells cmake not to pip-install them.
(python3.withPackages (ps: [
ps.jinja2
ps.setuptools
]))
makeWrapper
]
++ lib.optionals isLinux [
wayland-scanner
shaderc # provides glslc, needed at configure time for shader compilation
# Avoid fighting upstream's usage of vendored ffmpeg libraries
autoPatchelfHook
]
++ lib.optionals cudaSupport [
autoAddDriverRunpath
cudaPackages.cuda_nvcc
(lib.getDev cudaPackages.cuda_cudart)
];
buildInputs = [
boost
curl
miniupnpc
nlohmann_json
openssl
libopus
]
++ lib.optionals isLinux [
avahi
libevdev
libpulseaudio
libx11
libxcb
libxfixes
libxrandr
libxtst
libxi
libdrm
wayland
libffi
libevdev
libcap
libdrm
pcre
pcre2
libuuid
libselinux
libsepol
libthai
libdatrie
libxdmcp
libxkbcommon
libepoxy
libva
libvdpau
numactl
libgbm
amf-headers
svt-av1
vulkan-loader
pipewire
libappindicator
libnotify
]
++ lib.optionals cudaSupport [
cudaPackages.cudatoolkit
cudaPackages.cuda_cudart
];
runtimeDependencies = lib.optionals isLinux [
avahi
libgbm
libxrandr
libxcb
libglvnd
];
cmakeFlags = [
"-Wno-dev"
(lib.cmakeBool "BOOST_USE_STATIC" false)
(lib.cmakeBool "BUILD_DOCS" false)
(lib.cmakeFeature "SUNSHINE_PUBLISHER_NAME" "nixpkgs")
(lib.cmakeFeature "SUNSHINE_PUBLISHER_WEBSITE" "https://nixos.org")
(lib.cmakeFeature "SUNSHINE_PUBLISHER_ISSUE_URL" "https://github.com/NixOS/nixpkgs/issues")
# avoid cmake's network download of the LizardByte/build-deps ffmpeg tarball
(lib.cmakeFeature "FFMPEG_PREPARED_BINARIES" "${ffmpegPrebuilt}")
# we provide Jinja2/setuptools via python3.withPackages; don't pip-install
(lib.cmakeBool "GLAD_SKIP_PIP_INSTALL" true)
]
# upstream tries to use systemd and udev packages to find these directories in FHS; set the paths explicitly instead
++ lib.optionals isLinux [
(lib.cmakeBool "UDEV_FOUND" true)
(lib.cmakeBool "SYSTEMD_FOUND" true)
(lib.cmakeFeature "UDEV_RULES_INSTALL_DIR" "lib/udev/rules.d")
(lib.cmakeFeature "SYSTEMD_USER_UNIT_INSTALL_DIR" "lib/systemd/user")
(lib.cmakeFeature "SYSTEMD_MODULES_LOAD_DIR" "lib/modules-load.d")
# used in the generated systemd unit's ExecStart= line
(lib.cmakeFeature "SUNSHINE_EXECUTABLE_PATH" "${placeholder "out"}/bin/sunshine")
]
++ lib.optionals (!cudaSupport) [
(lib.cmakeBool "SUNSHINE_ENABLE_CUDA" false)
];
env = {
# needed to trigger CMake version configuration
BUILD_VERSION = "${finalAttrs.version}";
BRANCH = "master";
COMMIT = "";
};
# copy webui where it can be picked up by build
preBuild = ''
cp -r ${finalAttrs.ui}/build ../
'';
buildFlags = [
"sunshine"
];
# redefine installPhase to avoid attempt to build webui
installPhase = ''
runHook preInstall
cmake --install .
runHook postInstall
'';
# allow Sunshine to find libvulkan
postFixup = lib.optionalString cudaSupport ''
wrapProgram $out/bin/sunshine \
--set LD_LIBRARY_PATH ${lib.makeLibraryPath [ vulkan-loader ]}
'';
doInstallCheck = isLinux;
nativeInstallCheckInputs = lib.optionals isLinux [ udevCheckHook ];
passthru = {
tests = lib.optionalAttrs isLinux {
sunshine = nixosTests.sunshine;
};
updateScript = ./updater.sh;
};
meta = {
description = "Game stream host for Moonlight";
homepage = "https://github.com/LizardByte/Sunshine";
license = lib.licenses.gpl3Only;
mainProgram = "sunshine";
maintainers = [
];
platforms = lib.platforms.linux;
};
})

View File

@ -0,0 +1,125 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p gnugrep gnused coreutils curl jq nix-update nix-prefetch-git
# shellcheck shell=bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
package_nix="$script_dir/sunshine.nix"
nixerus_root="$(cd "$script_dir/../../.." && pwd)"
cd "$nixerus_root"
log() {
printf '\n==> %s\n' "$*" >&2
}
api() {
curl ${GITHUB_TOKEN:+-u ":$GITHUB_TOKEN"} --silent --location "$@"
}
log "Querying latest Sunshine release from GitHub"
version=$(api https://api.github.com/repos/LizardByte/Sunshine/releases/latest | jq --raw-output .tag_name | grep -oP "^v\K.*")
log "Latest version: $version"
if [[ "${UPDATE_NIX_OLD_VERSION:-}" == "$version" ]]; then
log "Already up to date!"
exit 0
fi
log "Updating sunshine version (src hash refreshed separately, see below)"
# --no-src: nix-update can't fetch submodules, so we refresh the src hash
# ourselves via nix-prefetch-git below.
nix-update sunshine --version "$version" --no-src
log "Prefetching sunshine src with submodules (this can take several minutes)"
# nix-prefetch-git outputs SRI under .hash on recent versions. The submodule
# tree NAR-hashes identically on Linux and Darwin, so one hash serves both.
src_hash=$(nix-prefetch-git \
--quiet \
--fetch-submodules \
--url "https://github.com/LizardByte/Sunshine" \
--rev "v$version" \
| jq --raw-output .hash)
if [[ -z "$src_hash" || "$src_hash" == "null" ]]; then
echo "ERROR: failed to prefetch sunshine src hash" >&2
exit 1
fi
log "src hash: $src_hash"
log "Patching src hash in $package_nix"
# The src fetchFromGitHub is the only one with fetchSubmodules; anchor on it so
# we don't touch the ffmpeg or npmDepsHash entries. sed -z spans newlines.
sed -i -zE "s#hash = \"sha256-[A-Za-z0-9+/=]+\"(\\s*fetchSubmodules = true;)#hash = \"$src_hash\"\\1#" "$package_nix"
if ! grep -q "$src_hash" "$package_nix"; then
echo "ERROR: failed to write src hash into $package_nix" >&2
exit 1
fi
log "Regenerating sunshine.ui package-lock.json"
# `--generate-lockfile` only regenerates package-lock.json; it skips the npmDepsHash
# refresh (see nix-update's dependency_hashes.py). Run a second pass to update the hash.
# `--no-src` avoids re-fetching the (already-pinned) sunshine src on each pass.
nix-update sunshine --version=skip --no-src --generate-lockfile --subpackage ui
log "Refreshing sunshine.ui npmDepsHash"
nix-update sunshine --version=skip --no-src --subpackage ui
# Update the LizardByte/build-deps tag and pinned ffmpeg tarball hashes.
# Sunshine pins build-deps via a git submodule at third-party/build-deps; the
# tag we need is whichever build-deps release tag points at that submodule's
# commit.
log "Resolving LizardByte/build-deps submodule commit and tag"
build_deps_sha=$(api "https://api.github.com/repos/LizardByte/Sunshine/contents/third-party?ref=v$version" \
| jq --raw-output '.[] | select(.name=="build-deps") | .sha')
log "build-deps submodule commit: $build_deps_sha"
build_deps_tag=$(api "https://api.github.com/repos/LizardByte/build-deps/tags?per_page=100" \
| jq --raw-output --arg sha "$build_deps_sha" '.[] | select(.commit.sha==$sha) | .name' \
| head -n1)
if [[ -z "$build_deps_tag" ]]; then
echo "ERROR: no LizardByte/build-deps tag points at submodule commit $build_deps_sha" >&2
exit 1
fi
log "build-deps tag: $build_deps_tag"
# Compute the SRI hash of a `fetchzip` (default stripRoot=true) for a URL.
# `nix-prefetch-url --unpack` produces the same NAR hash AND caches by URL
# (whereas the empty-hash + fetchzip trick re-downloads every invocation).
prefetch_unpacked_sri() {
local raw
raw=$(nix-prefetch-url --unpack --type sha256 "$1")
nix --extra-experimental-features nix-command hash convert --hash-algo sha256 --to sri "$raw"
}
ffmpeg_url() {
echo "https://github.com/LizardByte/build-deps/releases/download/$build_deps_tag/$1-ffmpeg.tar.gz"
}
# Map nix system → upstream tarball arch token. Keep this in sync with the
# `ffmpegArch` attrset in package.nix.
declare -A ffmpeg_arch=(
[x86_64-linux]=Linux-x86_64
)
declare -A ffmpeg_hash
for system in "${!ffmpeg_arch[@]}"; do
log "Prefetching ffmpeg tarball for $system (${ffmpeg_arch[$system]})"
h=$(prefetch_unpacked_sri "$(ffmpeg_url "${ffmpeg_arch[$system]}")")
if [[ -z "$h" ]]; then
echo "ERROR: failed to prefetch ffmpeg tarball for $system" >&2
exit 1
fi
log " $system -> $h"
ffmpeg_hash[$system]=$h
done
log "Patching $package_nix"
sed_args=(-E -e "s#buildDepsTag = \"v[^\"]*\";#buildDepsTag = \"$build_deps_tag\";#")
for system in "${!ffmpeg_hash[@]}"; do
sed_args+=(-e "s#$system = (lib\\.fakeHash|\"sha256-[A-Za-z0-9+/=]+\");#$system = \"${ffmpeg_hash[$system]}\";#")
done
sed -i "${sed_args[@]}" "$package_nix"
log "Done."

View File

@ -29,4 +29,6 @@
lh2ctrl = callPackage ./apps/lh2ctrl.nix { };
idea-oss-bin = callPackage ./apps/idea-community.nix { };
sunshine = callPackage ./apps/sunshine/sunshine.nix { };
}