8 Commits

Author SHA1 Message Date
Tha.Les b50e221ac0 fix: pin librosa <1 and add audioread explicitly (#407) (#408)
librosa 1.0.0 dropped its audioread dependency, but audio-separator
(vocal split, #275) still imports audioread directly. uv.lock already
resolved librosa 0.11.0, so dev/CI never hit this, but the packaging
scripts install from pyproject.toml rather than the lockfile, so an
unbounded upper bound let the shipped macOS/Windows/Linux runtime
packs silently pick up librosa 1.0.0 and lose audioread, breaking only
the vocal-split subprocess in the packaged artifact.

Also add audio_separator/onnxruntime to all three packaging scripts'
import verification, platform-guarded on Intel macOS, so a broken
vocal-split dependency chain fails the build instead of shipping.

Co-authored-by: Thales <>
2026-08-21 18:55:23 +01:00
Tha.Les 6761831abc fix: point runtime pack URL at stemdeckapp, not old thcp repo (#199)
CI never sets RELEASE_BASE_URL, so every macOS release manifest baked the old thcp/stemdeck download URL. It only worked via GitHub's transfer redirect, which is outside our control and would 404 if a repo named stemdeck is ever recreated under thcp. Point the default at the repo we own. Future releases only.
2026-06-09 10:00:48 +01:00
Tha.Les ff66e15e6c fix: address open issues #169 (version), #170 (XSS), #173 (SSRF) (#176)
* fix: address open issues #169, #170, #173

#170 — Stored XSS via library folder names: folder.name was interpolated
raw into innerHTML in the folder render path. Escape it with the existing
esc() helper (catalog.js), matching the track render paths.

#173 — SoundCloud SSRF surface: drop the on.soundcloud.com share shortener
from the host allowlist (it redirects to arbitrary targets) and add a
yt-dlp extractor allowlist (allowed_extractors=[youtube, soundcloud]) so a
URL that slips past host validation can't invoke the generic extractor.

#169 — Version stuck at 0.6.0-alpha.2 for source/Docker/self-hosted: make
the version git-tag-derived via hatch-vcs (pyproject dynamic version,
app/_version.py build artifact). app_version() now reads package metadata
-> _version.py -> dev placeholder; static/version.json is removed (now a
build artifact, gitignored). Install sites pin SETUPTOOLS_SCM_PRETEND_VERSION
from the release version so shallow CI clones / Docker (no .git) don't break
(Dockerfile, make-runtime-pack.sh, make-portable.ps1). make-app.sh defaults
VERSION to `git describe`. Desktop version literals (Cargo.toml, package.json,
tauri.conf.json) are now 0.0.0 placeholders stamped from the tag at build.
The update-check no longer nags dev/source builds.

Tests: 81 passed; ruff clean; app_version derives correctly.

* build: exclude generated app/_version.py from ruff

The hatch-vcs build hook writes app/_version.py during uv sync, and CI's
`ruff format --check app/` tripped on it (it's gitignored but present on
disk during lint). Add it to ruff's exclude list.

* ci: make git-derived version resilient to CI's shallow clone (#169)

CI runs uv sync in every step, which builds the editable package and
triggers hatch-vcs/setuptools_scm. On Woodpecker's shallow, tagless clone
setuptools_scm raises ("unable to detect version"), failing the lint step
before ruff runs (and skipping the rest).

- Set SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0 in the uv-based CI steps so the
  build never invokes git for the version (CI only lints/tests, never ships).
- Add hatch-vcs fallback-version as a second safety net for shallow source
  installs outside CI.

Verified: uv sync --frozen --all-extras succeeds with the env set.

* feat: validate library folder names (reject symbols/markup)

Folder names now accept only letters (any language), digits, spaces, and a
small safe punctuation set (- _ ' & ( ) . ,). Names with markup or symbols
(e.g. the XSS probe, or ±!@£$%^&*()_+{:"|?><) are rejected on Save with an
inline message instead of being created. Complements the render-time escaping
from #170 by blocking such names at the source.

* feat: raise folder name limit to 100 chars + enforce in validator

Bump the editor input maxlength from 48 to 100 and reject over-length names
on Save with an inline message (defensive, in case the cap is bypassed).
2026-06-02 14:03:02 +01:00
Tha.Les 5628963e08 [codex] Fix portable Python runtime layout (#30)
* Fix portable Python runtime layout

* Ignore issue docs

* Organize gitignore

* ignored

* fix: improve local dev build and runtime setup reliability

- Remove redundant size check in verify_runtime_archive; SHA256 is
  sufficient and size varies across zstd builds of identical content
- Extend PATH with Homebrew dirs in extract_tar_archive so tar can
  find zstd when running inside the app bundle
- Skip runtime download in setup.js if archive is already present locally
- Make DMG Finder AppleScript non-fatal (cosmetic only)
- Poll for uvicorn startup log line instead of fixed sleep 1
- Ignore .local/ dev scripts in gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: extract runtime pack in pure Rust, no zstd system dependency

Replace the system tar subprocess with native Rust crates (tar + zstd +
flate2). macOS ships without a standalone zstd binary so tar would fail
on any machine without Homebrew installed, breaking the setup flow for
all new users. Pure Rust extraction has zero external dependencies.

* fix: re-download runtime archive if local copy fails verification

* fix: delete stale runtime archive on checksum mismatch so retry re-downloads

* fix: bundle Python stdlib in runtime pack and set PYTHONHOME at launch

UV standalone Python has /install as compiled-in base_prefix so the
stdlib is unreachable on user machines. Copy the stdlib from the UV
Python into the venv lib directory during runtime pack build, then set
PYTHONHOME to the venv root at backend launch so Python finds it.

* fix: locate Python stdlib via encodings import instead of path guessing

The previous approach (exe.parent.parent/lib/pythonX.Y) fails on UV PBS
Python builds where the actual stdlib location differs from expectations.
Finding encodings via import is reliable regardless of compiled-in prefix.

* fix: use copytree(dirs_exist_ok=True) for stdlib copy and add sanity check

The item-by-item copy with if-not-exists was silently skipping files.
Using copytree with dirs_exist_ok merges stdlib into the venv lib dir
atomically. Added post-copy check that fails the build if encodings is
missing so this can never ship silently broken again.

* fix: bundle full PBS Python installation instead of venv to include stdlib

python -m venv only creates site-packages/ — it relies on sys.base_prefix
for stdlib, which is compiled into UV PBS Python as /install (a path that
never exists on user machines). Copying the entire PBS installation to
runtime/python/ gives us lib/pythonX.Y/ with the full stdlib in place.
PYTHONHOME already set in start_backend points Python there at runtime.

* fix: remove EXTERNALLY-MANAGED marker from copied PBS Python before installing packages

* fix: validate Python stdlib in probe_runtime to detect broken installs
2026-05-14 19:36:44 +01:00
Thales Pereira f3a97bdeb9 fix(macos): arch-scope intermediate build paths to fix parallel race conditions
arm64 and x64 builds run in parallel on the same agent/workspace.
Shared staging dirs and manifest files caused rm -rf collisions and
last-writer-wins overwrites. Scope all intermediates by ARCH:
  runtime-staging      -> runtime-staging-{ARCH}
  runtime-manifest.json -> runtime-manifest-{ARCH}.json
  app-path.txt         -> app-path-{ARCH}.txt
  dmg-staging          -> dmg-staging-{ARCH}
2026-05-12 00:05:03 +01:00
Thales Pereira 03e1cec1c3 fix(macos): replace pip self-upgrade with uv pip install in runtime pack
ensurepip installs a vendored pip then pip --upgrade partially
overwrites it, leaving mixed-version files. Subprocess builds then
hit ModuleNotFoundError on pip._internal.pyproject (removed in pip 22+).
uv pip install bypasses pip subprocesses entirely.
2026-05-11 23:58:57 +01:00
Thales Pereira 3ac121438c fix(macos): force CI=true before tauri build to avoid Woodpecker CI parse error 2026-05-11 23:44:05 +01:00
Tha.Les f08b6367b8 feat: macOS native app v0.4.0-alpha.1 (#27)
* ignore build files.

* feat: macOS native app — Tauri shell, runtime pack, MPS, CI pipeline

- Tauri 2 macOS app with setup wizard that downloads and installs a
  self-contained Python/backend runtime pack on first launch
- Runtime pack download now streams via reqwest with real-time progress
  events (runtime-download-progress) replacing the blocking curl call;
  progress bar shown in setup UI with indeterminate fallback
- MPS (Apple Silicon) GPU detection and torch device selection; CUDA
  path gated to non-macOS targets
- macOS data dir uses ~/Library/Application Support/StemDeck
- macOS FFmpeg downloaded from evermeet.cx and extracted from zip
- Backend watchdog: Python process exits when parent Tauri PID disappears
- CloseRequested handler stops backend and exits cleanly
- Woodpecker CI pipeline for macOS: arm64 and x64 builds in parallel,
  DMG inspection, artifact upload to GitHub releases
- Build scripts: make-runtime-pack.sh, make-app.sh, make-dmg.sh,
  make-iconset.sh with LOCAL_DEV_TEST version default
- Version stamped from CI_COMMIT_TAG (or LOCAL_DEV_TEST) at build time
  across Cargo.toml, tauri.conf.json, package.json
- SVG logo assets, DMG packaging files, macOS README and notices
- README updated with macOS download and build instructions
- Bump version to 0.4.0-alpha.1

* fix(security): bump urllib3 to 2.7.0, ignore unresolvable torch x86 CVE

- urllib3 2.7.0 fixes CVE-2026-44431 (header forwarding) and
  CVE-2026-44432 (decompression-bomb bypass)
- CVE-2025-32434 (torch 2.2.2 RCE via torch.load) added to
  .trivyignore: no 2.6.x macOS x86_64 wheels exist; StemDeck
  has no untrusted torch.load path
- Wire .trivyignore into the trivy-fs CI step

* ci: trigger macOS and Windows builds on tag push and release

Adds event: tag so that pushing a version tag (e.g. v0.4.0-alpha.1
for a pre-release) triggers the build and inspect pipeline.
Upload steps remain gated on event: release only.

* ci: skip asset upload if already present on pre-release promotion

When a pre-release is promoted to latest, GitHub fires a second
release event. The upload step now checks if assets already exist
for the tag and exits early, preventing --clobber from deleting
and re-uploading artifacts during the promotion window.
2026-05-11 23:29:17 +01:00