feat: Linux portable builds (CPU + NVIDIA) and release workflow by thcp · Pull Request #220 · stemdeckapp/stemdeck · GitHub
Skip to content

feat: Linux portable builds (CPU + NVIDIA) and release workflow#220

Merged
thcp merged 5 commits into
mainfrom
feat/linux-portable-build
Jun 24, 2026
Merged

feat: Linux portable builds (CPU + NVIDIA) and release workflow#220
thcp merged 5 commits into
mainfrom
feat/linux-portable-build

Conversation

@thcp

@thcp thcp commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

What

Adds Linux support as portable .tar.gz packages — Phase 1+2 of bringing StemDeck to Ubuntu/Linux. Mirrors the existing Windows (portable zip) and macOS (runtime pack) build paths. Ships two variants:

Artifact Variant
StemDeck-Linux-x64.tar.gz CPU-only (~smaller; runs anywhere)
StemDeck-Linux-x64.NVIDIA.tar.gz CUDA torch baked in; uses an NVIDIA GPU, falls back to CPU

Each bundles a self-contained Python runtime (torch + demucs) plus the Tauri binary, so users extract and run ./StemDeck with no toolchain.

Why a tarball, not .deb/apt

StemDeck's real payload is the bundled torch/demucs venv. apt/.deb is built around a host-managed dependency tree served from a repo — the exact thing a bundled runtime exists to escape. A portable tarball matches the Windows philosophy and avoids fighting apt over torch/CUDA versions. .deb and AppImage remain deferred.

Files

File Purpose
scripts/linux/make-portable.sh Builds a tarball: PBS Python + torch/demucs + Tauri binary. CPU_ONLY toggle selects CPU vs NVIDIA.
.github/workflows/linux-release.yml Builds both variants on hosted ubuntu-latest on release: published; apt deps + uv, ClamAV scan, upload.
packaging/linux/README-LINUX.txt Extract-and-run instructions + prerequisites (both variants).
packaging/linux/THIRD_PARTY_NOTICES.txt License notices.

Implementation notes

  • Python: uv fetches a python-build-standalone install copied whole into python/ (the macOS approach, not a venv). Required because the desktop shell's python_stdlib_present() looks for python/lib/pythonX.Y/os.py, which a plain venv lacks.
  • CPU variant: torch==2.6.0+cpu --force-reinstall --no-deps swap (same as the Windows script; Linux PyPI wheels bundle CUDA otherwise) + a cpu-only marker so the shell skips GPU detection.
  • NVIDIA variant: keeps the project's default torch (CUDA build on Linux x86_64) and omits the marker. No app-side changes — the CUDA detect/install path in main.rs is already cfg(not(macos)) and covers Linux; at first launch it detects the GPU and uses CUDA.
  • Build infra: both variants build on hosted ubuntu-latest (no GPU needed to build a CUDA package). One job builds CPU first, then NVIDIA reusing the same Tauri binary (SKIP_TAURI_BUILD=1); a free-disk step + dropping each uncompressed stage keeps the large CUDA bundle within the runner's disk.
  • Not bundled: FFmpeg (the Linux shell expects ffmpeg on PATH) and WebKitGTK (Tauri links the system libs). NVIDIA needs only a working driver (nvidia-smi); the CUDA runtime is bundled.

Bundled bugfix: PYTHONHOME on Linux

Includes a fix to desktop/src-tauri/src/main.rs surfaced by end-to-end testing: the backend failed to start with ModuleNotFoundError: No module named 'encodings' because PYTHONHOME was set to python/bin instead of the prefix python/. Linux uses python-build-standalone like macOS (auto-detects its prefix from bin/) and must not have PYTHONHOME set. Both sites were gated #[cfg(not(target_os = "macos"))], wrongly including Linux; now gated #[cfg(windows)].

Testing

  • bash -n on the script; workflow YAML validated; Windows cargo check clean (no new warnings) — confirms the Rust change does not regress the Windows release.
  • CPU variant validated end-to-end on WSL2 (Ubuntu): build → tarball → launch → backend healthy → stem separation produces audio. The PYTHONHOME fix was found and confirmed here.
  • Known WSL-only environment noise (not StemDeck issues): WebKitGTK GL warnings under WSLg (need software rendering) and choppy audio over the WSLg audio path — expected absent on native Linux.

Not yet verified (reviewer/maintainer follow-up)

  • NVIDIA variant has not been built or run yet. Two things to watch on the first release run: (1) the CUDA bundle building within ubuntu-latest disk limits (the free-disk step is defensive but unproven for this payload), and (2) runtime GPU use, which can only be confirmed on a real Linux + NVIDIA machine (WSL is not a valid test).

🤖 Generated with Claude Code

Thales added 3 commits June 24, 2026 11:39
Adds a Linux .tar.gz portable package mirroring the existing Windows/macOS
build paths. Bundles a python-build-standalone runtime (CPU torch + demucs)
plus the Tauri binary so users extract and run ./StemDeck.

- scripts/linux/make-portable.sh: stages PBS Python, force-installs CPU-only
  torch, builds the Tauri binary, and produces StemDeck-Linux-x64.tar.gz with
  the backend/app + python/ layout find_repo_root resolves at runtime.
- .github/workflows/linux-release.yml: builds on hosted ubuntu-latest on
  release publish; installs Tauri v2 apt deps + uv, ClamAV-scans, uploads.
- packaging/linux/{README-LINUX,THIRD_PARTY_NOTICES}.txt: extract-and-run
  instructions noting ffmpeg + WebKitGTK are system (apt) prerequisites.

FFmpeg is not bundled: the Linux shell expects ffmpeg on PATH. NVIDIA/CUDA
and AppImage variants are intentionally deferred to later phases.
The Linux backend failed to start with 'ModuleNotFoundError: No module
named encodings'. PYTHONHOME was being set to python/bin instead of the
prefix python/, so CPython looked for its stdlib under python/bin/lib and
could not boot.

Linux bundles python-build-standalone exactly like macOS, which detects
its own prefix by walking up from bin/ and must NOT have PYTHONHOME set.
The two PYTHONHOME sites were gated #[cfg(not(target_os = "macos"))],
wrongly including Linux alongside Windows. Only Windows -- whose portable
venv keeps the stdlib under base/Lib -- needs PYTHONHOME, so gate both
sites (start_backend and python_stdlib_ok) to #[cfg(windows)].

This also fixes the latent inconsistency where probe_runtime reported
Python ready (python_stdlib_ok set PYTHONHOME=python, the correct prefix)
while start_backend set PYTHONHOME=python/bin and failed.
Adds a second Linux package, StemDeck-Linux-x64.NVIDIA.tar.gz, with
CUDA-enabled torch baked in (mirrors the Windows NVIDIA variant).

- make-portable.sh: CPU_ONLY toggle (default 1). CPU_ONLY=0 keeps the
  project's default torch wheel, which on Linux x86_64 is the CUDA build,
  and omits the cpu-only marker so the desktop shell detects the GPU and
  uses CUDA at runtime. No app-side changes needed -- the CUDA detection/
  install path in main.rs is already cfg(not(macos)) and covers Linux.
- linux-release.yml: builds both variants in one job. CPU first (full Tauri
  build), then NVIDIA with SKIP_TAURI_BUILD=1 reusing the same binary. Adds
  a free-disk-space step (CUDA bundle is several GB) and drops each
  uncompressed stage after taring to stay within the hosted runner's disk.
- README-LINUX.txt: documents both variants and the NVIDIA driver
  prerequisite (nvidia-smi must work; CUDA runtime is bundled, no toolkit
  install needed; falls back to CPU when no GPU).
@thcp thcp changed the title feat: CPU-only Linux portable build and release workflow feat: Linux portable builds (CPU + NVIDIA) and release workflow Jun 24, 2026
Thales added 2 commits June 24, 2026 15:28
Targets the org's self-hosted wsl2 runner ([self-hosted, linux, x64])
instead of hosted ubuntu-latest, matching the Windows/macOS release
jobs. Drops the free-disk-space step: it was a hosted-runner workaround
and would needlessly rm system directories on a persistent self-hosted
box (WSL2's virtual disk has ample room for the CUDA bundle).
Lets you run the full two-variant build + ClamAV scan on the self-hosted
runner without publishing a release, to validate the runner toolchain and
the CUDA build. Resolves the version from a manual input (default 0.0.0,
must be valid PEP 440) instead of the branch ref, and skips the upload
step on non-release events.
@thcp thcp merged commit 8131900 into main Jun 24, 2026
8 checks passed
@thcp thcp deleted the feat/linux-portable-build branch June 24, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant