Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
439 commits
Select commit Hold shift + click to select a range
dd1b356
fix(transpose): open a song at the key its saved record holds (#830)
thcp Oct 22, 2026
581f7dc
feat(click): click sound presets, defined once for playback and export
thcp Oct 22, 2026
b872022
feat(export): click sound on every export, and the click as its own stem
thcp Oct 22, 2026
9ff0271
feat(click): choose the click sound in the click options
thcp Oct 22, 2026
9db1e10
feat(click): click as its own stem in the stems zip, and a click soun…
thcp Oct 22, 2026
0531f5b
fix(vocal-split): record why a Lead + Backing split failed
thcp Oct 22, 2026
77ebeff
fix(ui): say when Lead + Backing could not be made, and offer Try again
thcp Oct 22, 2026
f8b028d
fix(vocal-split): say when Lead + Backing could not be made (#834)
thcp Oct 22, 2026
a06b85a
ci(e2e): a hub change adds a core set instead of the whole suite
thcp Oct 22, 2026
f27ed64
ci(e2e): one reusable e2e workflow, and the full suite on every push …
thcp Oct 22, 2026
6c3cf58
ci(e2e): scoped e2e on staging PRs, full suite after each merge to bi…
thcp Oct 22, 2026
fbb88f2
fix(export): put the exported click on both channels at unity, as the…
thcp Oct 22, 2026
01be63b
fix(export): exported click at the level the app plays it (#837)
thcp Oct 22, 2026
3bb1874
ci(e2e): bound, retry and cache the e2e install steps
thcp Oct 22, 2026
ca231fb
ci(e2e): retry the system packages off the Azure mirror, count zombie…
thcp Oct 22, 2026
bcce70e
ci(e2e): bound, retry and cache the e2e install steps (#839)
thcp Oct 22, 2026
1cc3b8a
fix(footer): wrap the clusters instead of scrolling them out of sight
thcp Oct 22, 2026
21ea81e
test(footer): model the controls row and the rows level in footer-fit
thcp Oct 23, 2026
ac72e17
fix(footer): wrap the clusters instead of scrolling them out of sight…
thcp Oct 23, 2026
4eae117
feat(versions): keep versions of a song over one set of stems
thcp Oct 24, 2026
579ba3b
feat(versions): versions in the library and the Stems popover
thcp Oct 24, 2026
05c91b6
test(e2e): cover versions of a song
thcp Oct 24, 2026
7caac9f
feat(versions): delete a version from its row in the library
thcp Oct 24, 2026
9bdad63
fix(deps): update filelock to 4.2.4 for CVE-2026-112940
thcp Oct 24, 2026
6a529cd
fix(deps): update filelock to 4.2.4 for CVE-2026-112940 (#846)
thcp Oct 24, 2026
a9532f1
Merge remote-tracking branch 'origin/big-release' into feat/song-vers…
thcp Oct 24, 2026
5530a0b
feat(versions): keep versions of a song over one set of stems (#844)
thcp Oct 24, 2026
b81b34f
chore(py): remove dead helpers and an unread setting
thcp Oct 26, 2026
dea5bfb
chore(tauri): drop the uncalled local_ip command
thcp Oct 26, 2026
4c7c7c0
chore(i18n): remove 38 unused translation keys from every table
thcp Oct 26, 2026
756037b
chore(js): remove dead frontend code
thcp Oct 26, 2026
b0805b5
chore(css): remove the eight stylesheets nothing loads
thcp Oct 26, 2026
49482ff
chore(css): remove selectors no markup or script produces
thcp Oct 26, 2026
3e1d23d
fix(tauri): gate platform-only helpers so macOS clippy passes
thcp Oct 28, 2026
b0f8070
chore: remove dead code across Python, Tauri, JS, CSS and i18n
thcp Oct 28, 2026
210673f
fix(deps): update hydra-core to 1.3.8 and msgpack to 1.2.4
thcp Oct 30, 2026
97e8f21
fix(deps): update zip to 9.x
thcp Oct 31, 2026
60155ef
fix(export): write WAVs with a plain 44-byte header
sam1am Oct 29, 2026
f9b63e0
fix(export): write take audio.wav with a plain header too
thcp Oct 31, 2026
c1e1c75
test(e2e): fix macOS-only e2e failures (ControlOrMeta, engine-ready a…
thcp Oct 31, 2026
7a3c4e3
fix(export): include every stem in the All stems zip
thcp Oct 31, 2026
56eff24
feat(export): name the all-stems export "Export Extracted Stems"
thcp Oct 31, 2026
cd658e1
fix(export): zip only the stems the user extracted
thcp Oct 31, 2026
1b7fc29
ci: run the macOS and Windows Rust checks on GitHub-hosted runners
thcp Oct 31, 2026
a4a804b
ci: cache cargo dependencies in the macOS and Windows Rust checks
thcp Oct 31, 2026
5e62e3a
fix(desktop): pin the Windows ffmpeg download to a dated build with a…
Nov 1, 2026
2d93f1e
feat(performance): stacked take rows in Performance, hidden elsewhere
Nov 1, 2026
4dad467
fix(macos): sign and notarize the app after its last change (#857)
thcp Nov 1, 2026
42b7e70
fix(desktop): replace a known-bad managed Windows ffmpeg with the pin…
Nov 1, 2026
be60cb1
fix(job): a failed restore import always says why
Nov 1, 2026
c26972a
fix(performance): say why video recording cannot start, naming Linux …
Nov 1, 2026
4a0d134
feat(ui): the Mode menu opens when the mouse is over the button
Nov 1, 2026
ddf18f6
feat(donate): keep the 5, 10, 15 and 30 presets, without captions
Nov 1, 2026
28ba825
test(network): prove the gate refuses other devices while network acc…
Nov 1, 2026
adb992b
docs(network): say who can reach StemDeck, and fix the run.sh defaults
Nov 1, 2026
8dfcad2
fix(desktop): no cleanup warning for a runtime.old that never existed
Nov 1, 2026
78e8cd5
fix(desktop): record the FFmpeg source and architecture actually used
Nov 1, 2026
4ccd8f4
test(e2e): drag a loop, I and O, and the transport shortcuts by hand
Nov 1, 2026
2190413
test(acceptance): open a 0.20.0 library with the new build
Nov 1, 2026
67de258
fix(player): Play pressed while the stems load no longer ends the song
Nov 1, 2026
6fc0326
test(e2e): Play while the stems load starts the song, and Karaoke waits
Nov 1, 2026
c5aeaf1
feat(health): report whether ffmpeg can export a transposed mix
Nov 2, 2026
cb5a478
fix(export): hold back a transposed mix when ffmpeg cannot transpose
Nov 2, 2026
c7ec1da
fix(export): scale the render timeout with the length of the export
Nov 2, 2026
4705bf6
fix(export): scale the render timeout with the length of the export (…
thcp Nov 2, 2026
02d641a
fix(sweep): never auto-delete a stopped song
Nov 2, 2026
20041a9
fix(sweep): never auto-delete a stopped song (#862)
thcp Nov 2, 2026
821a9c8
test(settings): pin what a 0.20 upgrade gets for stem format and lyrics
Nov 2, 2026
15778d0
fix(pipeline): keep an upload when a shutdown interrupts its separation
Nov 2, 2026
8e9d5bf
fix(shortcuts): leave Ctrl, Cmd and Alt chords and focused selects alone
Nov 2, 2026
765a02c
fix(settings): refuse library writes between a stems folder move and …
Nov 2, 2026
203a560
fix(export): Auto uses only the GPU encoder measured in real renders
Nov 2, 2026
2b328c4
test(export): an explicit encoder pick is tested once per encoder
Nov 2, 2026
7460e45
fix(pipeline): keep an upload's source when its job fails
Nov 2, 2026
b886677
fix(vocal-split): keep the Lead + Backing request on the job
Nov 2, 2026
db71c65
fix(vocal-split): share one split between both completion paths
Nov 2, 2026
a631c0a
fix(ffmpeg): refuse an FFmpeg older than 5.1
Nov 2, 2026
5c18c00
fix(player): drop the previous song's engine before a song loads
Nov 2, 2026
07eac11
fix(settings): refuse library writes between a stems folder move and …
thcp Nov 2, 2026
d8bfd72
fix(vocal-split): keep the Lead + Backing request on the job (#869)
thcp Nov 2, 2026
491bf09
fix(shortcuts): leave Ctrl, Cmd and Alt chords and focused selects al…
thcp Nov 2, 2026
bf22f4b
test(settings): pin what a 0.20 upgrade gets for stem format and lyri…
thcp Nov 2, 2026
8a156bf
fix(pipeline): keep an upload when a shutdown interrupts its separati…
thcp Nov 2, 2026
4964ec0
fix(pipeline): keep an upload's source when its job fails (#868)
thcp Nov 2, 2026
716d774
fix(ffmpeg): refuse an FFmpeg older than 5.1 (#870)
thcp Nov 2, 2026
1fec020
fix(player): drop the previous song's engine before a song loads (#871)
thcp Nov 2, 2026
7132fb2
fix(export): Auto uses only the GPU encoder measured in real exports …
thcp Nov 2, 2026
19d7502
fix(job): say why a submit failed, in the chosen language
Nov 2, 2026
79bce15
fix(job): say why a submit failed, in the chosen language (#867)
thcp Nov 2, 2026
4ffcbd4
docs(release): say 0.21.0 is a full download and where a Windows libr…
Nov 2, 2026
be9412f
docs(release): say 0.21.0 is a full download and where a Windows libr…
thcp Nov 2, 2026
0a4a1e8
fix(vocal-split): reload the open song when its split ends
Nov 2, 2026
7285c9c
fix(backing): keep backing and rest sums in the data cache under a bu…
Nov 2, 2026
c21f943
fix(backing): keep backing and rest sums in the data cache under a bu…
thcp Nov 2, 2026
7e4b34f
fix(logs): redact paths, names and addresses in the Export logs zip
Nov 2, 2026
8850e21
fix(logs): take the home folder's name as the user name only in a use…
Nov 2, 2026
ce392b0
fix(vocal-split): reload the open song when its split ends (#877)
thcp Nov 2, 2026
caaba30
chore(guide): explain every control in Performance, Karaoke, lyrics t…
Nov 2, 2026
1c7797d
chore(guide): true sentences for the running queue row and a nudged l…
Nov 2, 2026
1876270
fix(jobs): refuse delete, move and reset while a song is still separa…
Nov 2, 2026
2cc39e5
chore(guide): explain every control in Performance, Karaoke, lyrics t…
thcp Nov 2, 2026
8e8f673
fix(logs): redact paths, names and addresses in the Export logs zip (…
thcp Nov 2, 2026
5a8f666
fix(jobs): refuse delete, move and reset while a song is still separa…
thcp Nov 2, 2026
6d1c89b
fix(vocal-split): read a damaged model as the model, not a missing de…
Nov 2, 2026
30a4f02
fix(vocal-split): tell an Intel Mac it has no Lead + Backing, not to …
Nov 2, 2026
07653aa
fix(vocal-split): clear a late cancel when the split succeeds
Nov 2, 2026
13d6449
fix(vocal-split): stop a restarted song saying it is still splitting
Nov 2, 2026
5f20c48
fix(vocal-split): offer no Try again on an Intel Mac
Nov 2, 2026
0141b4d
fix(drag-out): name each region drag after the mix it carries
Nov 2, 2026
e61dd7e
fix(drag-out): name each lane nugget drag after the slice it carries
Nov 2, 2026
ef27030
fix(vocal-split): correct the split's status and outcome codes (#880)
thcp Nov 3, 2026
a35aa8b
fix(import): say why an import failed and retry it from the message
Nov 2, 2026
3f4deca
fix(import): give Resync the reason a refused restore had
Nov 3, 2026
2fa5258
fix(drag-out): name each region drag after the mix it carries (#881)
thcp Nov 3, 2026
0cf0fb5
fix(registry): keep favourites, Trash and versions when the registry …
Nov 2, 2026
39f8f7d
fix(versions): open a version with the same lanes without reloading t…
Nov 3, 2026
437d24f
fix(registry): keep favourites, Trash and versions when the registry …
thcp Nov 3, 2026
a35d786
fix(import): say why an import failed, retry it from the message, and…
thcp Nov 3, 2026
53fa826
fix(versions): open a version with the same lanes without reloading t…
thcp Nov 3, 2026
a718b98
fix(jobs): stop only the operation a cancel was sent to, and finish a…
Nov 3, 2026
ec0c2a3
fix(registry): survive a downgrade to 0.20.0 and back, and one bad re…
Nov 3, 2026
15ccdb7
fix(settings): put settings.json on the disk before it replaces the o…
Nov 3, 2026
f5c48de
fix(stems-location): accept a folder holding only OS or StemDeck left…
Nov 3, 2026
a1cd1be
fix(stems-location): point at a library from a folder that holds no s…
Nov 3, 2026
16e522b
fix(stems-location): never keep a song cut short by a crash during a …
Nov 3, 2026
1f92a5f
fix(jobs): stop only the operation a cancel was sent to, and finish a…
thcp Nov 3, 2026
1526825
fix(reset): remove only StemDeck's own entries in a shared jobs folder
Nov 3, 2026
c699635
fix(stems-location): check for junctions only on Windows
Nov 3, 2026
996a973
Merge remote-tracking branch 'origin/big-release' into fix/b21-librar…
Nov 3, 2026
1a4ded1
fix(stems-location): make moving the library durable and safe to retr…
thcp Nov 3, 2026
545de92
fix(reset): remove only StemDeck's own entries in a shared jobs folde…
thcp Nov 3, 2026
d3c8041
Merge origin/big-release into fix/b18-downgrade-bad-records
Nov 3, 2026
1e3c091
fix(registry): end the version id pattern in \Z and load a registry w…
Nov 3, 2026
62ed235
fix(registry): survive a downgrade to 0.20.0 and back, and one bad re…
thcp Nov 3, 2026
2ab5f02
fix(registry): keep a song that was never separated when the registry…
Nov 3, 2026
4b74256
fix(registry): keep a song that was never separated when the registry…
thcp Nov 3, 2026
ed12a44
fix(queue): count a resume when the job starts, not at each restart
Nov 3, 2026
4699553
fix(queue): count a resume when the job starts, not at each restart (…
thcp Nov 3, 2026
5a053c6
fix(desktop): roll back a failed update swap and accept only this rep…
Nov 3, 2026
1c8f39b
fix(desktop): roll back a failed update swap and accept only this rep…
thcp Nov 3, 2026
bbf9d3d
fix: keep paths and cookie lines out of errors and logs, decode take …
Nov 3, 2026
4b50562
fix(pipeline): run failure and cancel cleanup off the event loop
Nov 3, 2026
f79a2b5
fix(library): Try again for a failed upload, from the file it kept
Nov 3, 2026
6334986
fix(desktop): keep a verified CUDA torch, and repair a missing torchv…
Nov 3, 2026
b572d4a
fix(pipeline): run failure and cancel cleanup off the event loop (#892)
thcp Nov 3, 2026
f0069dd
fix: keep paths and cookie lines out of errors and logs, decode take …
thcp Nov 3, 2026
8609fdf
fix(library): Try again for a failed upload, from the file it kept (#…
thcp Nov 3, 2026
70265f2
fix(desktop): keep a verified CUDA torch, and repair a missing torchv…
thcp Nov 3, 2026
1a10248
fix(registry): save the registry off the event loop and outside its lock
Nov 3, 2026
3f6f4bd
fix(desktop): keep the portable WebView2 profile in data, end setup c…
Nov 3, 2026
3325b4f
fix(registry): save the registry off the event loop and outside its l…
thcp Nov 3, 2026
2118511
fix(desktop): keep the portable WebView2 profile in data, end setup c…
thcp Nov 3, 2026
c1eebb1
fix(library): open an old song at once and measure its loudness in th…
Nov 3, 2026
67ce75f
fix(pipeline): write stem audio whole, and keep WAVs until MP3s are o…
Nov 3, 2026
0de57a3
fix: sweep crash leftovers at startup, free a cancelled zip, give eac…
Nov 3, 2026
59cddcb
fix(pipeline): write stem audio whole, and keep WAVs until MP3s are o…
thcp Nov 3, 2026
2bcf3e3
fix(library): open an old song at once and measure its loudness in th…
thcp Nov 3, 2026
c46a028
fix: sweep crash leftovers at startup, free a cancelled zip, give eac…
thcp Nov 3, 2026
ecdc03a
fix(registry): rebuild a song as done only when its separation finished
Nov 3, 2026
8bac425
fix(registry): rebuild a song as done only when its separation finish…
thcp Nov 3, 2026
f692f65
fix(donate): hide every Donate control whose link is not set
Nov 3, 2026
d70cf8e
fix(donate): hide every Donate control whose link is not set (#902)
thcp Nov 3, 2026
7abc4f0
feat(sections): give the sections bar its own Hide button
Nov 3, 2026
7180a77
fix(desktop): keep a portable run's temp files, tool caches and setti…
Nov 3, 2026
a27ac99
feat(sections): give the sections bar its own Hide button (#903)
thcp Nov 3, 2026
9c35a64
fix(desktop): keep a portable run's temp files, tool caches and setti…
thcp Nov 3, 2026
29f8cc9
fix(separate): bound the separation model download and say why it failed
Nov 3, 2026
3430ae7
fix(separate): bound the separation model download and say why it fai…
thcp Nov 3, 2026
a5179f5
fix(desktop): listen on 127.0.0.1 until network access is on
Nov 3, 2026
985b87c
fix(setup): make the model step honest and interruptible
Nov 3, 2026
643b503
fix(desktop): make the network restart safe to cancel, retry and upda…
Nov 3, 2026
1ac44b5
fix(setup): close the gaps review found in the model step
Nov 3, 2026
eff949a
fix(desktop): listen on 127.0.0.1 until network access is on (#906)
thcp Nov 3, 2026
a716a05
fix(setup): make the model step honest and interruptible (#907)
thcp Nov 3, 2026
c4931b8
fix(desktop): run the long setup and update commands off the main thread
Nov 3, 2026
4ee9d9e
fix(desktop): run the long setup and update commands off the main thr…
thcp Nov 3, 2026
4dded52
fix(desktop): a portable folder never reads an installed app's settin…
Nov 3, 2026
55a0d97
fix(desktop): a portable folder never reads an installed app's settin…
thcp Nov 3, 2026
7a582b3
fix(desktop): save exported logs through the save dialog, not the window
Nov 3, 2026
3e3afa0
test(desktop): cover a failed and a cancelled log save; harden the le…
Nov 3, 2026
05baeb1
fix(library): auto-delete keeps a song while work runs on it, and one…
Nov 3, 2026
8ea25a9
fix(scripts): send the leak check's HTTP import with HttpClient
Nov 3, 2026
1854f9c
perf(recording): the armed-track meter loop keeps its meters instead …
Nov 3, 2026
d496508
ci(trivy): install Trivy from the pinned release asset, not get.trivy…
Nov 3, 2026
b71c1e5
fix(library): order songs by the server's time, not the browser's clock
Nov 3, 2026
c815e19
fix(scripts): count only StemDeck folders and Downloads files as the …
Nov 3, 2026
9bc9b20
fix(guide): explain the footer seek bar and Performance section markers
Nov 3, 2026
0122710
ci(trivy): install Trivy from the pinned release asset, not get.trivy…
thcp Nov 4, 2026
d14fc51
Merge branch 'big-release' into fix/b22-autodelete-keep
thcp Nov 4, 2026
ea177cc
Merge branch 'big-release' into fix/b42-library-hot-paths
thcp Nov 4, 2026
ee0c0eb
Merge branch 'big-release' into fix/o6-frame-dom-query
thcp Nov 4, 2026
c5dc44f
Merge branch 'big-release' into fix/b25d-portable-no-outside-writes
thcp Nov 4, 2026
9b4142d
Merge branch 'big-release' into fix/b14-footer-scrub-guide
thcp Nov 4, 2026
e84975a
fix(library): auto-delete keeps a song while work runs on it, and one…
thcp Nov 4, 2026
1053f42
fix(library): order songs by the server's time, not the browser's clo…
thcp Nov 4, 2026
c93c50c
perf(recording): the armed-track meter loop keeps its meters instead …
thcp Nov 4, 2026
41fd025
fix(guide): explain the footer seek bar and Performance section marke…
thcp Nov 4, 2026
caae319
fix(desktop): Export logs saves through the save dialog, so a portabl…
thcp Nov 4, 2026
5fb6c7c
refactor(footer): delete the hidden footer seek bar and its Guide entry
Nov 4, 2026
85f33a0
refactor(footer): drop the duration import the seek handler left unused
Nov 4, 2026
0b034e8
fix(registry): a song rebuilt from its folder keeps the time it was a…
Nov 4, 2026
dac9ea2
refactor(footer): delete the hidden footer seek bar and its Guide ent…
thcp Nov 4, 2026
19af9f4
refactor(stems-location): drop the unread same_filesystem field
Nov 2, 2026
131594f
refactor(api): drop the click params get_stem never reads
Nov 2, 2026
769a00b
chore(frontend): remove the unused TAKE_ID_RE export
Nov 2, 2026
53ea98b
chore(css): remove the footer-art rules that match no markup
Nov 2, 2026
d7bab06
chore(html): remove the hidden track-artist span nothing writes
Nov 2, 2026
195d8cc
refactor(pipeline): drop four parameters their functions never use
Nov 2, 2026
738de3c
refactor(eq): remove fit_master_eq, which nothing calls
Nov 4, 2026
6454870
chore(frontend): remove the unused stemsControlState export
Nov 4, 2026
0a4c78e
docs: correct stale wording about the build tooling and Python range
Nov 4, 2026
0564356
perf(export): resolve the jobs folder once per rebuilt Original
Nov 4, 2026
1e8eb1e
fix(registry): a song rebuilt from its folder keeps the time it was a…
thcp Nov 4, 2026
25b7989
chore: remove dead code and correct stale build wording (#1006)
thcp Nov 4, 2026
f91f27e
feat(gpu): AMD GPUs on Linux through the ROCm build of torch
Nov 4, 2026
8c0a4c2
feat(gpu): AMD GPUs on Linux through the ROCm build of torch (#1007)
thcp Nov 4, 2026
88f203d
refactor(footer): delete the hidden footer waveform strip and its redraw
Nov 4, 2026
dc33636
fix(settings): say why separation runs on the CPU on a machine with a…
Nov 4, 2026
3d250d4
fix(desktop): log when setup assumes CUDA 12.4 because the nvidia-smi…
Nov 4, 2026
8999225
fix(settings): keep the GPU probe off the event loop and run it once …
Nov 4, 2026
e4a09a5
fix(settings): ask nvidia-smi at server start so opening Settings nev…
Nov 4, 2026
1d2a39f
fix(settings): give no NVIDIA reason for the AMD (ROCm) build of torch
Nov 4, 2026
d471a20
refactor(footer): delete the hidden footer waveform strip and its red…
thcp Nov 4, 2026
9e641e5
fix(settings): say why separation runs on the CPU on a machine with a…
thcp Nov 4, 2026
35fd1f3
fix(ci): ignore CVE-2026-24747, StemDeck never calls torch.load (#101…
thcp Nov 4, 2026
e952729
fix(setup): say the CPU download is the CPU download, not that there …
thcp Nov 4, 2026
22366fb
fix(desktop): read the CUDA UMD Version header that driver 616 prints…
thcp Nov 4, 2026
22b38ee
fix(library): answer a press on Settings in the first moments after s…
thcp Nov 4, 2026
19fc716
chore(ci): drop the dead pyproject version rewrite, target py311 in r…
thcp Nov 4, 2026
2511276
ci(e2e): run the Playwright job only on pull requests into main (#102…
thcp Nov 4, 2026
a17d7a4
perf(versions): write a version's store records in one save (#1020)
thcp Nov 4, 2026
eb59ddf
fix(loudness): say why an older song's loudness numbers changed (#1019)
thcp Nov 4, 2026
1b79a05
perf(library): keep each song's files-present answer until its folder…
thcp Nov 4, 2026
071bb02
perf(versions): freeze the versions' voice before a split in one stor…
thcp Nov 4, 2026
a807f61
fix(library): hold the list rebuild while a song or folder is dragged…
thcp Nov 4, 2026
9c273fb
ci(release): build release candidates into a draft pre-release by han…
thcp Nov 5, 2026
f82368a
fix(video): try every VAAPI render node, not only the first (#1032)
thcp Nov 5, 2026
c786487
fix(desktop): one early exit no longer turns the DMA-BUF renderer off…
thcp Nov 5, 2026
be55238
fix(docker): re-own only the library entries whose owner is wrong (#1…
thcp Nov 5, 2026
71be634
Free the separation models' memory after 10 idle minutes (#975) (#1034)
thcp Nov 5, 2026
bf50807
fix(ffmpeg): choose the Mac FFmpeg for the machine's CPU, not the she…
thcp Nov 7, 2026
f04459e
feat(donate): add the Liberapay link, so the Donate button shows (Ref…
thcp Nov 7, 2026
f9766bf
fix(donate): a way that takes no amount opens its page from its tile …
thcp Nov 7, 2026
7c7e4b1
i18n(donate): QR caption, follow-link names and Guide text; drop Copy…
Nov 7, 2026
c27c4ae
feat(donate): Stripe link, QR cards, follow links and a lighter hero …
Nov 7, 2026
ffe82b6
fix(desktop): a middle-click on an external link opens the user's bro…
thcp Nov 8, 2026
6a40510
fix(desktop): an explicit STEMDECK_FFMPEG wins over the pair in confi…
thcp Nov 8, 2026
5e4e884
fix(video): set a failing VAAPI node aside and key its pass on the GP…
thcp Nov 8, 2026
21b0054
fix(update): offer a release only when it is newer than the running b…
thcp Nov 8, 2026
226694b
chore(deps): bump docker/login-action from 4.6.0 to 4.7.0 (#1048)
thcp Nov 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
104 changes: 59 additions & 45 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ on:
release:
types: [published]

# No job writes to the repository, so the token gets nothing. Checkout of this
# public repository and the artifact upload work without it, as in the other
# workflows.
permissions: {}

env:
UV_LINK_MODE: copy
# Version is git-derived (hatch-vcs). CI's clone is shallow/tagless, which
Expand All @@ -25,7 +30,7 @@ jobs:
container:
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# diffq (a mandatory transitive dependency of audio-separator, #275) has
# no prebuilt wheel for Python 3.11+ on Linux -- its last release only
# ever shipped cp310 wheels -- so uv sync must compile it from source.
Expand All @@ -37,13 +42,20 @@ jobs:
- run: uv run ruff check app/ tests/
- run: uv run ruff format --check app/ tests/
- run: bash -n run.sh
# The retry wrapper the e2e installs depend on (#838). Here because this
# job runs on every pull request, whatever it changes.
- run: bash tests/ci/test_retry_sh.sh
# The Docker entrypoint's ownership pass (#994). This container runs as
# root, which the mixed-owner and symlink cases need, and sh here is
# dash, the same shell as the image.
- run: bash tests/ci/test_docker_reown_sh.sh

test:
runs-on: ubuntu-latest
container:
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# build-essential: see the matching comment in the lint job above.
- run: apt-get update && apt-get install -y --no-install-recommends ffmpeg build-essential
- run: uv sync --frozen --all-extras
Expand All @@ -54,57 +66,38 @@ jobs:
container:
image: node:20-alpine
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: for f in static/js/*.js; do node --check "$f"; done
- run: for f in tests/js/*.test.mjs; do node "$f"; done

# Browser tests for static/js (#339). node --check above is a syntax parse;
# this drives the real UI against the real backend, including the Tauri code
# path, which is where #335 hid because it was invisible in a browser.
# Split three ways, each shard on its own runner (#686). The suite runs
# serially inside a shard -- workers: 1 in playwright.config -- because it
# shares one backend and one jobs directory, and several specs change server
# state. A shard is a separate machine with its own backend, so splitting
# here adds parallelism without adding sharing. Files stay whole, since
# fullyParallel is off, so tests that rely on order within a file still get
# it. Free on a public repository; each shard pays its own setup.
# Browser tests for static/js: the steps live in e2e.yml, shared with
# e2e-full.yml. Runs the whole suite, and only for a pull request into main,
# a push to main or a release. A pull request into any other base (such as
# big-release) skips it (#1024): the full run on every push to big-release
# and the nightly run (e2e-full.yml) catch a broken spec on the merge commit.
# Checks show as "frontend-e2e / frontend-e2e (1)" to "(3)" on those events.
frontend-e2e:
runs-on: ubuntu-latest
strategy:
# One failing shard must not cancel the others: the report is only
# useful whole.
fail-fast: false
matrix:
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/setup-node@v7.0.0
with:
node-version: "20"
cache: npm
- run: curl -LsSf https://astral.sh/uv/install.sh | sh
- run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- run: uv sync --frozen --all-extras
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test --shard=${{ matrix.shard }}/3
- if: failure()
uses: actions/upload-artifact@v7.0.1
with:
# Per shard: a second upload under the same name fails the job.
name: playwright-report-${{ matrix.shard }}
path: |
playwright-report/
test-results/
retention-days: 7
if: github.event_name != 'pull_request' || github.base_ref == 'main'
# A new push to a pull request cancels that pull request's older e2e run.
# Anything else gets a group of its own (the run id), so a push to main or
# a release is never cancelled and never waits behind another run. This
# does not help a runner GitHub never assigns: re-run that job, or
# gh run cancel <id> then gh run rerun <id> --failed.
concurrency:
group: e2e-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
uses: ./.github/workflows/e2e.yml
with:
mode: full
base-ref: ${{ github.base_ref }}

# The Linux installer only ever runs on a user's machine, so nothing else
# would catch a regression in it. Runs on a real Linux image rather than the
# macOS bash used during development.
linux-installer:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: bash -n packaging/linux/install.sh
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends shellcheck desktop-file-utils
- run: shellcheck -S warning packaging/linux/install.sh
Expand All @@ -115,7 +108,7 @@ jobs:
container:
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: uv tool install bandit
- run: uv tool run bandit -r app/ -ll # fail on medium+ severity

Expand All @@ -124,7 +117,7 @@ jobs:
container:
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: uv tool install pip-audit
- run: uv pip compile pyproject.toml -o /tmp/requirements.txt
# Ignored CVEs (review when upgrading torch or demucs):
Expand Down Expand Up @@ -185,7 +178,26 @@ jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# Installs Trivy from its GitHub release asset, checked against a pinned
# sha256, instead of letting trivy-action call setup-trivy. setup-trivy's
# install.sh downloads the binary from get.trivy.dev, so the scan failed
# (curl exit 6) as soon as that hostname stopped resolving. Bump the
# version and the sha256 together: the sha256 is the Linux-64bit line of
# trivy_<version>_checksums.txt in the same release.
- name: Install Trivy
env:
TRIVY_VERSION: 0.70.0
TRIVY_SHA256: 8b4376d5d6befe5c24d503f10ff136d9e0c49f9127a4279fd110b727929a5aa9
run: |
tarball="trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
curl -fsSL -o "$RUNNER_TEMP/$tarball" \
"https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/$tarball"
echo "$TRIVY_SHA256 $RUNNER_TEMP/$tarball" | sha256sum --check --strict
mkdir -p "$RUNNER_TEMP/trivy-bin"
tar -xzf "$RUNNER_TEMP/$tarball" -C "$RUNNER_TEMP/trivy-bin" trivy
echo "$RUNNER_TEMP/trivy-bin" >> "$GITHUB_PATH"
"$RUNNER_TEMP/trivy-bin/trivy" --version
# Scans the source tree for: known CVEs in deps, leaked secrets,
# and Dockerfile / compose misconfigurations. Skips .venv (it can
# be left over from earlier steps in the shared workspace; trivy
Expand All @@ -202,6 +214,7 @@ jobs:
ignore-unfixed: true
trivyignores: .trivyignore
skip-dirs: .venv,jobs
skip-setup-trivy: true
# Dedicated Dockerfile + compose static analysis (Trivy's IaC linter).
- name: trivy config
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
Expand All @@ -210,3 +223,4 @@ jobs:
scan-ref: build/
severity: HIGH,CRITICAL
exit-code: '1'
skip-setup-trivy: true
52 changes: 45 additions & 7 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ name: Docker Publish
# (ghcr.io/stemdeckapp/stemdeck). The image keeps the default Linux x86_64 torch
# wheel, which is the CUDA build -- so the same image runs on CPU by default and
# uses the GPU automatically when started with `--runtime=nvidia` (see the
# Unraid template in templates/unraid/).
# Unraid template in templates/stemdeck.xml).
#
# A second image carries the ROCm build of torch for AMD GPUs (#825), with the
# same tags plus a -rocm suffix (:rocm where the default image gets :latest).
# It is a separate image rather than the default because the ROCm runtime is
# several GB more, which every CPU and NVIDIA user would otherwise pull for
# nothing. Each image is its own matrix leg: its own runner, disk and timeout,
# built in parallel, and fail-fast is off, so a ROCm failure can never stop or
# delay the default image.

on:
# Every merge to main publishes a rolling :edge image. The version is derived
Expand Down Expand Up @@ -41,8 +49,28 @@ env:

jobs:
build-and-push:
name: build-and-push (${{ matrix.image }})
runs-on: ubuntu-latest
timeout-minutes: 60
timeout-minutes: ${{ matrix.timeout }}
strategy:
fail-fast: false
matrix:
include:
- image: default
edge: edge
suffix: ""
latest: latest
torch_index: ""
timeout: 60
# Not measured yet: the ROCm wheels are about 3.6 GB on top of the
# CUDA stack the project install pulls first (torch<2.7 in
# pyproject.toml), so the leg gets twice the default's time.
- image: rocm
edge: edge-rocm
suffix: "-rocm"
latest: rocm
torch_index: https://download.pytorch.org/whl/rocm6.4
timeout: 120
permissions:
contents: read
packages: write
Expand Down Expand Up @@ -76,14 +104,22 @@ jobs:
with:
images: ${{ env.IMAGE }}
tags: |
type=raw,value=edge,enable=${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.version == '') }}
type=raw,value=${{ steps.ver.outputs.value }},enable=${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.version != '') }}
type=raw,value=latest,enable=${{ github.event_name == 'release' && (github.event.action == 'released' || (github.event.action == 'published' && !github.event.release.prerelease)) }}
type=raw,value=${{ matrix.edge }},enable=${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.version == '') }}
type=raw,value=${{ steps.ver.outputs.value }}${{ matrix.suffix }},enable=${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.version != '') }}
type=raw,value=${{ matrix.latest }},enable=${{ github.event_name == 'release' && (github.event.action == 'released' || (github.event.action == 'published' && !github.event.release.prerelease)) }}

# The ROCm wheels unpack to several GB on top of the CUDA stack the
# project install pulls first, more than a hosted runner has free.
- name: free disk space for the ROCm build
if: matrix.image == 'rocm'
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
df -h /

- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

- name: log in to GHCR
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand All @@ -94,11 +130,13 @@ jobs:
with:
context: .
file: build/Dockerfile
# Unraid is x86_64; arm64 has no CUDA torch and is not a target.
# Unraid is x86_64; arm64 has no CUDA or ROCm torch and is not a target.
platforms: linux/amd64
push: true
# Empty for the default image, which keeps the CUDA build.
build-args: |
VERSION=${{ steps.ver.outputs.value }}
TORCH_INDEX_URL=${{ matrix.torch_index }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
provenance: false
46 changes: 46 additions & 0 deletions .github/workflows/e2e-full.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# The whole Playwright suite on big-release, the integration branch. A pull
# request into big-release does not run Playwright (ci.yml), so this is where a
# broken spec shows up: within minutes of the merge that broke it, on that
# merge's commit. It never blocks a pull request.
#
# Triggers:
# - every push to big-release (each merged pull request);
# - nightly, on big-release;
# - by hand, on any branch: gh workflow run e2e-full.yml --repo stemdeckapp/stemdeck --ref <branch>
# (or -f ref=<branch-or-sha> to test another ref with this branch's workflow).
#
# The schedule only fires from the default branch: GitHub reads this file, and
# the e2e.yml it calls, from main. Until this file reaches main there is no
# nightly run; pushes to big-release and manual runs work before that.

name: e2e-full

on:
push:
branches: [big-release]
schedule:
# 04:17 UTC, off the top of the hour when hosted runners are busiest.
- cron: "17 4 * * *"
workflow_dispatch:
inputs:
ref:
description: "Branch, tag or SHA to test (empty: the branch this was run on)"
type: string
default: ""

permissions: {}

# No concurrency group on purpose: a push to big-release, the nightly and a
# manual run are each a record of one commit, and none may cancel another.

jobs:
full-suite:
# Forks inherit the schedule; only this repository runs it.
if: github.repository == 'stemdeckapp/stemdeck'
uses: ./.github/workflows/e2e.yml
with:
mode: full
# A scheduled run checks out the default branch (main) unless told
# otherwise, so the nightly names big-release explicitly. A push tests
# its own commit; a manual run tests the ref input, else its own branch.
checkout-ref: ${{ github.event_name == 'schedule' && 'big-release' || inputs.ref || github.sha }}
Loading
Loading