v2.4.0 - Portable Distribution & External Updater
Changed — breaking
- In-app auto-update is removed. Sky Player now notifies you when a new version is available; applying it is done by running the new
updater.batin the install folder, then reopeningSky-Player.exe. This moves Sky Player to a portable-distribution model and removes in-place file-replacement logic from the running app. The previous "Auto-apply without asking" toggle is removed from Update Settings. - "Check for Update" in the picker now surfaces a banner modal with three actions: Open Releases page, Skip this version, Dismiss. The Download-and-apply progress modal is removed.
- The
update.auto_applyandupdate.pending_update_versionfields inconfig.jsonare no longer read or written. Existing entries in olderconfig.jsonfiles are ignored silently and stripped on next save.
Added
- External updater —
updater.bat(repo root) +installer/updater.ps1. Verifies SHA256 before any install mutation; stages in TEMP; backs up and copies binaries transactionally with fallback rollback on failure. Preservesconfig.jsonand skipssongs/entirely. Log:%LOCALAPPDATA%\Sky-Player\updater.log. Supports-Channel stable|beta,-DryRun,-ForceClose,-Restart. update.channel(defaultstable),update.last_notified_version, and (until 2.4.1)update.legacy_old_dir_sweep_pendinginconfig.json. Channel is wired to the in-app check (include_prerelease) and to the external updater.- One-time sweep of legacy
.old.{guid}install siblings left from pre-2.4.0 atomic swaps. - Release pipeline (
.github/workflows/release.yml) — tag-driven build with tag↔pyproject.tomlversion lock, free-threaded audit, build-provenance attestation, three-artifact triple (zip+sha256+MANIFEST.json). - CI workflow (
.github/workflows/ci.yml) executes the full altitude table onwindows-latestagainst the free-threaded interpreter. - Pre-commit config (
.pre-commit-config.yaml) mirrors the same gates locally and addscheck-yaml/check-toml/check-json/eol/trailing-whitespace. - Pytest markers (
scheduler,windows,golden,slow) andnorecursedirsdeclared via[tool.pytest.ini_options]. .editorconfigpins UTF-8, LF, 4-space indent for Python and 2-space for YAML/TOML/JSON; CRLF preserved on*.bat.PULL_REQUEST_TEMPLATE.md+ issue templates (bug_report.md,feature_request.md,security_p0.md,config.yml).docs/distribution-and-update.md— contributor documentation.
Removed
apply_update_and_restart,write_apply_batch,apply_staged_update,download_and_verify_update,download_and_apply_update_worker,_apply_stagedfrom the app and service layers.UpdateProgressModalfromsrc/sky_music/ui/textual_app/modals.py.find_old_backups,post_update_flag_path,write_apply_batch,apply_update_and_restartfromsrc/sky_music/infrastructure/update_installer.py.simulate_update.pyscenariosdownload-okanddownload-bad-sha.use_ll_hookmachinery — opt-in globalWH_KEYBOARD_LLhook (SetWindowsHookExW), the dormanthotkey_hook.pymodule, theAppConfig.use_ll_hookfield, and thePlaybackControls._hookslot. The hotkey mechanism now relies exclusively on the focus-gated poll path (is_virtual_key_down), aligning the runtime withAGENTS.mdP0.1 ("NO GAME TAMPERING — no hooks") and clearing the only outstanding entry in.config/security_audit_baseline.json.
Security
- AGENTS.md P0 audit enforced in CI. New
scripts/audit_security_mandates.py(AST scanner) runs on every push and PR alongside the existingaudit_free_threaded_wheels.pyprecheck. It forbidsReadProcessMemory,WriteProcessMemory,SetWindowsHookEx*,CreateRemoteThread,DebugActiveProcess,NtQueryInformationProcess, imports ofpymem/pyinject/win32api, andWinDLL("ntdll.dll"), while explicitly allowing only theSendInputfamily. - Public
SECURITY.mdrestates the P0 mandates and the disclosure channel (security@pumni.dev) for vulnerability-grade findings.
Full changelog: see CHANGELOG.md.