feat(linux): add an optional installer for desktop integration - #364
Merged
Conversation
Implements #342. StemDeck stays portable: extract the tarball, run ./StemDeck, and none of this is required. install.sh is there for people who would rather launch from their applications menu. It installs the package it sits in and never downloads anything, so the version and the CPU/NVIDIA variant come from the package itself (backend/static/version.json and the cpu-only marker) and cannot drift from the build being installed. That also removes any need to verify a second download. Design notes, mostly things the reference installer in #342 got wrong: - Install is atomic. The new copy goes to <target>.new and is verified before the old one is moved aside, so a failure partway leaves the working install untouched. Removing the old copy first is what made a failed upgrade in that fork leave the machine with no StemDeck, no launcher and no manifest recording where it had been. - A failed copy cleans up its own staging directory rather than leaving a package-sized partial on disk. - Exec is quoted, so an install path containing a space still launches. - Version comparison is semver-aware. sort -V ranks 0.8.0-alpha.17 above 0.8.0, which would tell every pre-release user they were current the day a stable release shipped. - Reading a missing manifest key yields empty rather than killing the script, which under set -euo pipefail is what a grep|head|cut pipeline does. - Global installs put the launcher in /usr/share/applications and the icon in /usr/share/pixmaps, so other users on the machine can see it. - Installing from inside the destination is refused rather than moving the running script out from under bash. - Non-x86_64 machines get a clear refusal instead of a binary that cannot run. User data is never touched. Stems live in ~/Documents/StemDeck and the runtime, models and logs in $XDG_DATA_HOME/stemdeck, both outside the install directory. Legacy data/ from pre-migration builds is carried across an upgrade, and uninstall refuses to delete it, leaving the folder and saying why. tests/linux/test_install_sh.sh runs the real installer against a synthetic package in a throwaway HOME: 52 checks covering install, upgrade, the failed-upgrade case, uninstall, corrupt manifests, paths with spaces, legacy data, self-install, arch refusal and the semver table. CI runs it on Linux with shellcheck and desktop-file-validate. Closes #361
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #361, and with #360 already merged this closes out #342.
Written in-house rather than adapting the fork. Portable behaviour is unchanged: extract the tarball, run
./StemDeck, and nothing here is required.What it does
It installs the package it sits in and downloads nothing. Version and variant come from
backend/static/version.jsonand thecpu-onlymarker, so the installer and the build cannot disagree, and there is no second artifact to checksum.The decisions that matter
Install is atomic. The copy goes to
<target>.newand is verified before the old install is moved aside. Removing the old copy first is what made a failed upgrade in the reference fork leave the machine with no StemDeck, no launcher, and no manifest recording where it had been. There is a test for exactly that.A failed copy cleans up after itself. Under
set -ea barecpthat dies partway aborts the script before any cleanup line, stranding a package-sized partial. Caught by the test suite, not by reading it.Version comparison is semver-aware.
sort -Vranks0.8.0-alpha.17above0.8.0, so the day a stable release ships every pre-release user would be told they were current. Ten cases pin the ordering.A missing manifest key reads as empty.
grep | head | cutunderset -euo pipefailexits 1 on a missing key, killing the script with no message and making its own corrupt-manifest handling unreachable.Also:
Execis quoted so paths with spaces launch; global installs put the launcher and icon under/usr/shareso other users see them; installing from inside the destination is refused rather than moving the running script; non-x86_64 gets a clear refusal.User data
Never touched. Stems are in
~/Documents/StemDeck, runtime and models in$XDG_DATA_HOME/stemdeck, both outside the install directory. Legacydata/from pre-migration builds is carried across an upgrade, and uninstall refuses to delete it, leaving the folder and saying why.Verification
tests/linux/test_install_sh.shruns the real installer against a synthetic package in a throwawayHOME. 52 checks, all passing:sort -Vfailure casePassing under bash 3.2 locally, which is a floor no Linux distro is below. CI adds a
linux-installerjob running it on ubuntu-latest withshellcheck -S warning(clean) anddesktop-file-validate.Full Python suite: 539 passed.
tests/test_stems_api.py::test_all_stems_zip_oggfails identically on cleanmain(local ffmpeg lackslibvorbis; the skip guard only checks that ffmpeg exists). Unrelated, untouched.