mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
feat(nix): inline flake + psysonic derivation, wire verify into release
Supersedes PR #203's out-of-tree upstream-src layout now that the Nix build lives in the canonical repo. The flake treats `self` as the source of truth — nothing in flake.nix or psysonic.nix needs a manual version bump per release. flake.nix - `inputs.upstream-src` dropped; `src = self` throughout - Dev shell retains the full WebKitGTK / GStreamer / AppIndicator closure needed for tauri:dev on NixOS (Linux x86_64 + aarch64) nix/psysonic.nix - Version read from package.json (upstreamMeta.version removed) - `srcClean` filter excludes node_modules / dist / target / .git / result / .flatpak-builder - `cargoDeps = rustPlatform.importCargoLock` with empty `outputHashes` — sufficient for our local `[patch.crates-io]` path overrides (cpal-0.15.3, symphonia-format-isomp4) - Wraps the installed binary with LD_LIBRARY_PATH + GST_PLUGIN_PATH + GIO_EXTRA_MODULES + GDK_BACKEND=x11 + WebKit disable flags nix/upstream-sources.json - Reduced to `{ npmDepsHash }`. Placeholder for the initial commit; the verify-nix job rewrites it on every release from the actual `prefetch-npm-deps` output. .github/workflows/release.yml - New `verify-nix` job gated on `create-release` (same as the Tauri builds). Checks out `main`, installs Nix, recomputes `npmDepsHash`, refreshes `flake.lock`, runs `nix build .#psysonic --no-link` as the sanity check, then commits lock + hash updates back to `main` when they changed. - The old standalone `upstream-release-nix.yml` is intentionally not ported; its scheduled re-poll of the upstream release is redundant now that the source lives here. Tarball publishing and Cachix upload remain explicitly out of scope — those ship in a follow-up workflow tied to the cache setup.
This commit is contained in:
@@ -159,3 +159,62 @@ jobs:
|
|||||||
find src-tauri/target/release/bundle \
|
find src-tauri/target/release/bundle \
|
||||||
\( -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" \) \
|
\( -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" \) \
|
||||||
| xargs gh release upload "app-v${VERSION}" --clobber
|
| xargs gh release upload "app-v${VERSION}" --clobber
|
||||||
|
|
||||||
|
# Verifies that `nix build .#psysonic` still works against the current source
|
||||||
|
# and refreshes `nix/upstream-sources.json` (npmDepsHash) + `flake.lock`
|
||||||
|
# (nixpkgs pin). Commits the refreshed files back to `main` when they change.
|
||||||
|
#
|
||||||
|
# Tarball publishing / Cachix upload are intentionally out of scope here —
|
||||||
|
# those will live in a dedicated workflow tied to a binary cache setup.
|
||||||
|
verify-nix:
|
||||||
|
needs: create-release
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
# Full history so we can push the auto-commit back to the default branch.
|
||||||
|
fetch-depth: 0
|
||||||
|
# Checkout main, not the tag — we want to push lock/hash refreshes to
|
||||||
|
# the moving branch, not the immutable tag ref.
|
||||||
|
ref: main
|
||||||
|
|
||||||
|
- name: install Nix
|
||||||
|
uses: DeterminateSystems/nix-installer-action@v15
|
||||||
|
|
||||||
|
- name: compute npmDepsHash from package-lock.json
|
||||||
|
id: npm-hash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
HASH="$(nix run nixpkgs/nixos-unstable#prefetch-npm-deps -- package-lock.json)"
|
||||||
|
echo "hash=$HASH" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Computed npmDepsHash: $HASH"
|
||||||
|
|
||||||
|
- name: write npmDepsHash into nix/upstream-sources.json
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
HASH='${{ steps.npm-hash.outputs.hash }}'
|
||||||
|
jq --arg h "$HASH" '.npmDepsHash = $h' nix/upstream-sources.json > nix/upstream-sources.json.new
|
||||||
|
mv nix/upstream-sources.json.new nix/upstream-sources.json
|
||||||
|
cat nix/upstream-sources.json
|
||||||
|
|
||||||
|
- name: refresh flake.lock (nixpkgs pin)
|
||||||
|
run: nix flake update --accept-flake-config
|
||||||
|
|
||||||
|
- name: verify nix build
|
||||||
|
run: nix build .#psysonic --accept-flake-config --no-link --print-build-logs
|
||||||
|
|
||||||
|
- name: commit + push refreshed lock and hash (if changed)
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add flake.lock nix/upstream-sources.json
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "flake.lock / nix/upstream-sources.json unchanged — nothing to commit."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
VERSION="${{ needs.create-release.outputs.package_version }}"
|
||||||
|
git commit -m "chore(nix): refresh lock + npmDepsHash for v${VERSION}"
|
||||||
|
git push origin HEAD:main
|
||||||
|
|||||||
@@ -44,3 +44,7 @@ tmp/
|
|||||||
|
|
||||||
# Third-party clones for local research (not committed)
|
# Third-party clones for local research (not committed)
|
||||||
research/
|
research/
|
||||||
|
|
||||||
|
# Nix build output symlink
|
||||||
|
result
|
||||||
|
result-*
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
{
|
||||||
|
description = ''
|
||||||
|
Psysonic for NixOS / nixpkgs: installable app + dev shell.
|
||||||
|
|
||||||
|
Packages:
|
||||||
|
nix build .#psysonic # or .#default — desktop app (.desktop + icon)
|
||||||
|
nix profile install .#psysonic
|
||||||
|
|
||||||
|
Run (after build, or from any clone with flake):
|
||||||
|
nix run .#psysonic
|
||||||
|
nix run github:Psychotoxical/psysonic
|
||||||
|
|
||||||
|
Development:
|
||||||
|
nix develop # mkShell (Rust/Node/WebKit deps + hooks)
|
||||||
|
nix shell .#devShells.default # same environment without entering subshell semantics
|
||||||
|
|
||||||
|
Release pipeline updates `flake.lock` (nixpkgs pin refresh) and
|
||||||
|
`nix/upstream-sources.json` (npmDepsHash) on every `v*` tag push —
|
||||||
|
see `.github/workflows/release.yml` (verify-nix job). Package version
|
||||||
|
is read from `package.json`; nothing in this file needs manual bumping
|
||||||
|
per release.
|
||||||
|
'';
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{ self, nixpkgs }:
|
||||||
|
let
|
||||||
|
inherit (nixpkgs) lib;
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
];
|
||||||
|
forSystem = f: lib.genAttrs systems f;
|
||||||
|
|
||||||
|
mkShellFor =
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
gstPlugins = with pkgs.gst_all_1; [
|
||||||
|
gstreamer
|
||||||
|
gst-plugins-base
|
||||||
|
gst-plugins-good
|
||||||
|
gst-plugins-bad
|
||||||
|
];
|
||||||
|
gstPluginPath = pkgs.lib.makeSearchPath "lib/gstreamer-1.0" gstPlugins;
|
||||||
|
in
|
||||||
|
pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
nodejs_22
|
||||||
|
rustc
|
||||||
|
cargo
|
||||||
|
cmake
|
||||||
|
pkg-config
|
||||||
|
openssl
|
||||||
|
gtk3
|
||||||
|
webkitgtk_4_1
|
||||||
|
libsoup_3
|
||||||
|
glib-networking
|
||||||
|
atk
|
||||||
|
cairo
|
||||||
|
gdk-pixbuf
|
||||||
|
glib
|
||||||
|
pango
|
||||||
|
librsvg
|
||||||
|
alsa-lib
|
||||||
|
libayatana-appindicator
|
||||||
|
]
|
||||||
|
++ gstPlugins;
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
export LD_LIBRARY_PATH="${pkgs.libayatana-appindicator}/lib''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||||
|
export GST_PLUGIN_PATH="${gstPluginPath}''${GST_PLUGIN_PATH:+:$GST_PLUGIN_PATH}"
|
||||||
|
export GIO_EXTRA_MODULES="${pkgs.glib-networking}/lib/gio/modules''${GIO_EXTRA_MODULES:+:$GIO_EXTRA_MODULES}"
|
||||||
|
export GDK_BACKEND=x11
|
||||||
|
export WEBKIT_DISABLE_COMPOSITING_MODE=1
|
||||||
|
export WEBKIT_DISABLE_DMABUF_RENDERER=1
|
||||||
|
unset CI
|
||||||
|
'';
|
||||||
|
|
||||||
|
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
|
||||||
|
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include";
|
||||||
|
};
|
||||||
|
|
||||||
|
upstreamMeta = lib.importJSON ./nix/upstream-sources.json;
|
||||||
|
|
||||||
|
psysonicFor =
|
||||||
|
system:
|
||||||
|
nixpkgs.legacyPackages.${system}.callPackage ./nix/psysonic.nix {
|
||||||
|
src = self;
|
||||||
|
inherit upstreamMeta;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells = forSystem (system: { default = mkShellFor system; });
|
||||||
|
|
||||||
|
packages = forSystem (system: {
|
||||||
|
psysonic = psysonicFor system;
|
||||||
|
default = psysonicFor system;
|
||||||
|
});
|
||||||
|
|
||||||
|
apps = forSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
p = psysonicFor system;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = {
|
||||||
|
type = "app";
|
||||||
|
program = lib.getExe p;
|
||||||
|
meta = {
|
||||||
|
inherit (p.meta) description homepage license;
|
||||||
|
mainProgram = "psysonic";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
# Installable Psysonic (Tauri): npm build → cargo tauri build --no-bundle.
|
||||||
|
# Source: `self` (this repo). Package version is read from package.json.
|
||||||
|
# `npmDepsHash` in nix/upstream-sources.json is refreshed by the release
|
||||||
|
# workflow (see .github/workflows/release.yml, verify-nix job).
|
||||||
|
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
fetchNpmDeps,
|
||||||
|
npmHooks,
|
||||||
|
rustPlatform,
|
||||||
|
cargo,
|
||||||
|
rustc,
|
||||||
|
pkg-config,
|
||||||
|
cmake,
|
||||||
|
openssl,
|
||||||
|
gtk3,
|
||||||
|
webkitgtk_4_1,
|
||||||
|
libsoup_3,
|
||||||
|
glib-networking,
|
||||||
|
alsa-lib,
|
||||||
|
libayatana-appindicator,
|
||||||
|
atk,
|
||||||
|
cairo,
|
||||||
|
gdk-pixbuf,
|
||||||
|
glib,
|
||||||
|
pango,
|
||||||
|
librsvg,
|
||||||
|
cargo-tauri,
|
||||||
|
nodejs,
|
||||||
|
makeWrapper,
|
||||||
|
wrapGAppsHook4,
|
||||||
|
copyDesktopItems,
|
||||||
|
makeDesktopItem,
|
||||||
|
gst_all_1,
|
||||||
|
src,
|
||||||
|
upstreamMeta,
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
version = (lib.importJSON (src + "/package.json")).version;
|
||||||
|
# WebKit media stack needs discoverable GStreamer plugins (e.g. appsink in gst-plugins-base).
|
||||||
|
gstPlugins = with gst_all_1; [
|
||||||
|
gstreamer
|
||||||
|
gst-plugins-base
|
||||||
|
gst-plugins-good
|
||||||
|
gst-plugins-bad
|
||||||
|
];
|
||||||
|
gstPluginPath = lib.makeSearchPath "lib/gstreamer-1.0" gstPlugins;
|
||||||
|
srcClean = lib.cleanSourceWith {
|
||||||
|
inherit src;
|
||||||
|
filter =
|
||||||
|
path: _:
|
||||||
|
let
|
||||||
|
f = toString path;
|
||||||
|
in
|
||||||
|
!(lib.hasInfix "/node_modules/" f)
|
||||||
|
&& !(lib.hasInfix "/dist/" f)
|
||||||
|
&& !(lib.hasInfix "/target/" f)
|
||||||
|
&& !(lib.hasInfix "/.git/" f)
|
||||||
|
&& !(lib.hasInfix "/result/" f)
|
||||||
|
&& !(lib.hasInfix "/.flatpak-builder/" f);
|
||||||
|
};
|
||||||
|
npmDeps = fetchNpmDeps {
|
||||||
|
src = srcClean;
|
||||||
|
hash = upstreamMeta.npmDepsHash;
|
||||||
|
};
|
||||||
|
cargoLockFile = src + "/src-tauri/Cargo.lock";
|
||||||
|
in
|
||||||
|
|
||||||
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
|
pname = "psysonic";
|
||||||
|
inherit version;
|
||||||
|
src = srcClean;
|
||||||
|
inherit npmDeps;
|
||||||
|
|
||||||
|
strictDeps = true;
|
||||||
|
|
||||||
|
# cmake is only for Rust deps (e.g. libopus); no top-level CMakeLists.txt in repo root
|
||||||
|
dontUseCmakeConfigure = true;
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
npmHooks.npmConfigHook
|
||||||
|
cargo
|
||||||
|
rustc
|
||||||
|
rustPlatform.cargoSetupHook
|
||||||
|
pkg-config
|
||||||
|
cmake
|
||||||
|
makeWrapper
|
||||||
|
wrapGAppsHook4
|
||||||
|
copyDesktopItems
|
||||||
|
cargo-tauri
|
||||||
|
nodejs
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = [
|
||||||
|
gtk3
|
||||||
|
webkitgtk_4_1
|
||||||
|
libsoup_3
|
||||||
|
glib-networking
|
||||||
|
openssl
|
||||||
|
alsa-lib
|
||||||
|
libayatana-appindicator
|
||||||
|
atk
|
||||||
|
cairo
|
||||||
|
gdk-pixbuf
|
||||||
|
glib
|
||||||
|
pango
|
||||||
|
librsvg
|
||||||
|
]
|
||||||
|
++ gstPlugins;
|
||||||
|
|
||||||
|
cargoRoot = "src-tauri";
|
||||||
|
cargoDeps = rustPlatform.importCargoLock {
|
||||||
|
lockFile = cargoLockFile;
|
||||||
|
# Local path overrides for `[patch.crates-io]` entries in src-tauri/Cargo.toml.
|
||||||
|
# Keep in sync with that block — importCargoLock needs the source to match
|
||||||
|
# the lockfile entries for patched crates (otherwise it tries to fetch from
|
||||||
|
# crates.io and the hash mismatches).
|
||||||
|
outputHashes = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
dontUseCargoParallelJobs = true;
|
||||||
|
|
||||||
|
env = {
|
||||||
|
OPENSSL_DIR = "${openssl.dev}";
|
||||||
|
OPENSSL_LIB_DIR = "${openssl.out}/lib";
|
||||||
|
OPENSSL_INCLUDE_DIR = "${openssl.dev}/include";
|
||||||
|
VITE_LASTFM_API_KEY = "";
|
||||||
|
VITE_LASTFM_API_SECRET = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
# beforeBuildCommand runs npm run build; npmConfigHook supplies offline node_modules
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
export HOME=$(mktemp -d)
|
||||||
|
(cd src-tauri && cargo tauri build --no-bundle -v)
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
install -Dm755 src-tauri/target/release/psysonic -t $out/bin
|
||||||
|
install -Dm644 src-tauri/icons/128x128.png $out/share/icons/hicolor/128x128/apps/psysonic.png
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
desktopItems = [
|
||||||
|
(makeDesktopItem {
|
||||||
|
name = "psysonic";
|
||||||
|
desktopName = "Psysonic";
|
||||||
|
comment = "Subsonic-compatible music player";
|
||||||
|
icon = "psysonic";
|
||||||
|
exec = "psysonic";
|
||||||
|
categories = [ "AudioVideo" "Audio" "Player" ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
postFixup = ''
|
||||||
|
wrapProgram $out/bin/psysonic \
|
||||||
|
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ libayatana-appindicator ]}" \
|
||||||
|
--prefix GST_PLUGIN_PATH : "${gstPluginPath}" \
|
||||||
|
--prefix GIO_EXTRA_MODULES : "${glib-networking}/lib/gio/modules" \
|
||||||
|
--set GDK_BACKEND x11 \
|
||||||
|
--set WEBKIT_DISABLE_COMPOSITING_MODE 1 \
|
||||||
|
--set WEBKIT_DISABLE_DMABUF_RENDERER 1
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Desktop music player for Subsonic-compatible servers";
|
||||||
|
homepage = "https://github.com/Psychotoxical/psysonic";
|
||||||
|
license = lib.licenses.gpl3Only;
|
||||||
|
mainProgram = "psysonic";
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
};
|
||||||
|
})
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"npmDepsHash": "sha256-0000000000000000000000000000000000000000000="
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user