Run unit/doc tests for the shipping crates workspace on PRs without
compiling qbz-ui. Fix three green-blockers: local playlist schema for
tests, Track.work fixture, and a stale ListenBrainz doctest.
release-linux had save-if:false deferring cache writes to build-slint.yml,
which no longer runs — so the amd64 `slint-linux` dep cache was never created
and every release/snap amd64 build ran fully cold (no dep cache; qbz_ui
recompiles regardless). release-linux-aarch64 already saves (save-if:true),
which is why only the aarch64 cache existed. Save on main so pre-flights warm
it and tag runs restore it.
cc
The publish step failed the whole run with "login_data is empty" when
SNAPCRAFT_STORE_CREDENTIALS is unset, even though both binaries compiled and
the amd64 snap packed fine. Move the secret to job level and gate the publish
step on it: with no secret the snap is still built and uploaded as an artifact,
publish is skipped (green run) instead of failing. Applies to both arches.
cc
Both arches now publish from CI: the existing amd64 track plus a new
build-binary-arm64 (native ubuntu-24.04-arm, free for public repos, reuses
release-linux-aarch64's cargo cache) -> snap-arm64 that dumps the prebuilt
aarch64 binary with packaging/snap-slint/snapcraft-arm64.yaml. This CI upload
is the sole publish path; Launchpad / "Build from GitHub" is not used.
cc
Two contributors in one week sent solid PRs against the retired
Tauri/Svelte tree (#561, #564) because nothing in the repo says it is
dead. Three zero-risk notices, no code touched: a pull_request_target
bot that leaves one sticky comment when a PR touches src//src-tauri/
(target on purpose — fork PRs cannot be commented under plain
pull_request; the PR code is never checked out), sentinel READMEs at
both tree roots (what GitHub renders when browsing), and a PR template
checkbox. The tree deletion + local reference worktree is the 2.0.2
follow-up (owner hold: no dependency risk allowed before 2.0.1).
cc
A Manifest published without its amd64 DIST entry bricks emerge for
every amd64 user, which is worse than failing the job; a 3x30s retry
absorbs release-asset propagation lag right after the tag. Also
documents the #557 root cause at the top: the overlay must never be
edited by hand — an ebuild change without Manifest regeneration is a
guaranteed digest mismatch; template changes are republished by
re-dispatching this workflow.
cc
Users on CPUs without AES-NI (Nehalem i7-860, pre-2010 Intel Macs) or
without SSSE3 (AMD Phenom II) crashed at launch with SIGILL. The
global `-C target-feature=+aes,+ssse3` was kept after the #353/#343
x86-64-v3 incident on the belief that the aes crate's cpufeatures
runtime check would protect CPUs without AES-NI — but a compile-time
target feature makes cpufeatures compile its check to a constant, so
the AES-NI intrinsics ran unconditionally (credentials AES-GCM decrypt
at startup), and +ssse3 let LLVM autovectorize anywhere, breaking
Phenom II. Measured on the qbz-cmaf hot path (Ctr128BE<Aes128>
apply_keystream, 256 MB, release): 2145 MB/s compiled-in vs 2162 MB/s
runtime-dispatched, identical output — the flags bought nothing on
capable CPUs. Removed from .cargo/config.toml, all dev/build scripts
and the release/build workflows; old CPUs now get the software AES
fallback instead of a crash.
Fixes#549
cc
The root snapcraft.yaml (source build, kept for Snap Store remote
builders) shadowed the staged snap/snapcraft.yaml dump recipe, so the
v2.0.0 snap job recompiled the entire workspace inside LXD and the
container OOM'd. Remove the root recipes from the CI working copy
before invoking snapcraft.
cc
The v2.0.0 tag run was cancelled at exactly 30 minutes with snapcraft's
LXD build still running. The job does no compilation, but container
bootstrap plus platform snap downloads on public runners routinely
exceed the old budget.
cc
threads=2/cgu256/opt3 under the widened cap margin — completed with a
5.7 GB global floor and zero kill signals while the desktop ran. Same
flags across runs keeps the runner's target cache warm.
cc
The uncapped opt-3/threads-2 recipe dies to earlyoom whenever the
desktop holds RAM (its trigger is RAM-only, so free swap cannot save
an uncapped build). Switch to the capped threads-1/opt-2 recipe,
proven end-to-end under an 8G/10G cgroup cap with the desktop open —
slower, but it finishes regardless of box load.
cc
Repo policy: public workflows fire on release tags only. A push to
main is a release merge whose tag immediately fires the real release
builds, so the auto-build on main was pure double spend.
cc
Restores the ALSA layout binds in both snap recipes (bit-perfect hw:
access inside the snap), lets the build cache save on main as well as
slint-mvp, installs the desktop file as com.blitzfc.qbz.desktop so the
AppStream launchable and the MPRIS DesktopEntry resolve in deb/rpm,
clears the stale boot-restore error when a new browser sign-in starts,
and adds the X11 dlopen libraries to the flake's runtime set. The
flake version/rev/srcHash bump remains a tag-time step (documented in
the release checklist, which is rewritten for the 2.0 Slint flow).
cc
release-linux, release-linux-aarch64, release-macos and release-flatpak
now build the crates/ workspace (Tauri npm builds removed) with the
proven build-slint recipe: 18 GB extra swap, mold, pinned nightly, MIN
memory tier. Build-once-package-many: nfpm deb/rpm, minimal-AppDir
AppImage, and the AUR/Gentoo/APT tarball with its name and layout
contract preserved. macOS assembles a QBZ.app skeleton, ad-hoc signs
it, and packs a dmg with hdiutil.
Cross-cutting: prerelease detection extended to -rc/-beta/-alpha/-pre,
tag-on-main ancestry guard (manual dispatch skips it), fail-fast
version assert against crates/Cargo.toml, unstripped binary archived
per tag. Tauri-updater bridge: AppImages and macOS .app.tar.gz are
signed with the existing Tauri minisign key so 1.2.x installs
auto-update into v2 via the unchanged release-updater-manifest.yml.
cc
Feature branches (slint-mvp) no longer auto-run the build on every push — that
burned minutes, and the qbz_ui compile is the expensive one. build-slint.yml now
auto-fires only on push to main; workflow_dispatch keeps it manually triggerable
on any branch.
cc
The dev box is Gentoo/OpenRC with no systemd, hence no snapd, so it cannot
build or upload snaps. This pipeline keeps everything on GitHub-hosted Ubuntu
runners (which have snapd) instead:
- packaging/snap-slint/snapcraft.yaml: a `plugin: dump` recipe that packages a
PRE-BUILT qbz binary (no compilation), with the Slint runtime stack
(Vulkan/GL via the gnome extension, winit Wayland/X11, alsa/pipewire, mpris).
Kept separate from the Tauri snapcraft.yaml so neither clobbers the other.
- .github/workflows/release-snap.yml: job 1 compiles the binary on a 16 GB
runner using the same MIN-tier + 18 GB swap recipe as build-slint.yml; job 2
downloads it and runs snapcraft `dump` + snapcore/action-publish. The heavy
compile is on a free public runner; the snap packaging is on a runner that
has snapd — neither touches the OpenRC box.
Requires the SNAPCRAFT_STORE_CREDENTIALS secret (generate once via
`snapcraft export-login` on any snapd-capable host). The runtime confinement
(stage-packages/plugs for the non-GTK GPU stack) is a first cut and will need a
real `snapcraft build` + `snap run` on the runner to finalize; it cannot be
tested locally without snapd.
cc
The qbz_ui rustc (generated Slint UI, one ~1.6M-line module) is the build
wall: measured peak demand ~30 GB (RSS+Swap), and it is frontend-bound.
Codegen knobs barely move it (cu=256/opt=2 + -Z threads=1/jobs=1 shave only
~3 GB vs cargo defaults), so the build is made to FIT via RAM+swap, not tuned
down. Extracting feature crates out of the bin does not lower it either: the
bin peaks at only ~4.5 GB; qbz_ui is strictly upstream of it.
- Swap 8 -> 18 GB: 16 GB RAM + ~22 GB swap (~38 GB) comfortably exceeds the
~30 GB demand. 8 GB was an OOM risk (16+4+8 ~= 28 GB < 30 GB).
- Swatinem/rust-cache@v2 (crates -> target, shared-key, save-if slint-mvp):
a controller-only push restores the prebuilt qbz_ui rlib from cache and
rebuilds only the cheap qbz bin. This, not the peak, is the minute saver.
- Pin a dated nightly (was floating @nightly): rustc -V is part of the cache
key, so a floating nightly re-keyed the cache and invalidated every Cargo
fingerprint daily (the from-scratch-every-run waste).
- concurrency cancel-in-progress: a new push to slint-mvp cancels the older
in-flight build instead of stacking ~20-min runs.
cc
Slint cannot split the .slint compilation across crates, but a Slint LIBRARY
crate exposes its generated Rust types to dependent crates (verified with a
minimal proof). So:
- NEW crate `qbz-ui` (lib): owns the whole .slint tree + build.rs + bundled
translations; `slint::include_modules!()` + the generated types (AppWindow,
MiniPlayerWindow, all globals/structs) are re-exported. Translation domain is
now `qbz-ui` (the .po files renamed accordingly).
- `qbz-slint` (bin) -> renamed to `qbz` (package + `[[bin]] name = "qbz"`),
reclaiming the binary name from the retired Tauri app. It depends on `qbz-ui`
and does `pub use qbz_ui::*;` so existing `crate::AppWindow` / `crate::*State`
references resolve unchanged.
This splits the single ~20 GB rustc into two crates (UI/codegen vs the 75k LOC
of logic) — the foundation for lower build peak, incremental caching, and the
upcoming per-feature crate extraction.
Also fixed for the move: qbz-i18n bundled-.po include paths, the font/WGSL
`include_bytes!`/`include_str!` paths (-> ../../qbz-ui/ui/), workspace members,
dev scripts (`-p qbz`), and the CI workflows. `cargo check -p qbz` is green.
cc
The GitHub-hosted private runner is 2 vCPU/8 GB and cannot build qbz-slint
(died at 1h42m). The dev box CAN, so a self-hosted runner (qbz-box) gives a
reliable CI artifact. Manual trigger; uncapped threads=2 (box-proven);
persistent work dir = incremental after first build.
cc
mupdf-sys (via qbz-booklet) links the system third-party libs; the build
got 21 min in and failed on a missing harfbuzz. Add the full mupdf system-lib
set so it links cleanly.
cc
The runner already ships an active /swapfile, so fallocate on it fails with
"Text file busy". Add the extra swap on a fresh path (/swap_extra) and guard
the whole swap block so a hiccup never fails the job — memory was never the
blocker (the prior run died on disk, not OOM).
cc
ubuntu-24.04 has no separate large /mnt disk — it's all on the 72 GB root
(~16 GB free), so the 12 GB swapfile + cargo target overflowed it (protobuf
failed with ENOSPC, not OOM — the build was memory-fine). Free ~25-30 GB of
unused preinstalled toolchains, drop swap to 8 GB, and keep the target on the
default path.
cc
Build-only workflow on ubuntu-24.04 with a 12 GB swapfile and the
lowest-memory rustc settings (serial frontend, codegen-units 256,
opt-level 2, one rustc at a time) so a stock 16 GB runner can compile the
Slint app. Triggers on push to slint-mvp and via workflow_dispatch.
cc
Cherry-picked from PR #377 (thanks @Mar0xy) — Node-only portion, no
Discord IPC additions:
- 4 workflows (release-{linux,linux-aarch64,macos,flatpak}.yml) now
install Node 24
- packaging/flatpak/com.blitzfc.qbz.yml: SDK extension node20 → node24,
build append-path and npm_config_nodedir aligned
Forcing function: @icons-pack/svelte-simple-icons@7.x already in use
declares engines.node >=24, so Node 20 produces an EBADENGINE warning
in CI. Node 20 also reached EOL on 2026-04-30. Node 24 has been LTS
since October 2025 (~7 months of maturity).
Discord IPC permissions and qbz-wrapper script from #377 were
intentionally NOT cherry-picked — there's no Discord RPC feature in
QBZ today (#336 is still planned but not implemented), so adding the
sandbox permission and the launch wrapper now would expand the
attack surface for nothing. They'll land together with the actual
feature when it ships.
cc
The qbzd daemon is still in early development and the build pipeline
isn't ready to ship artifacts. Drop the workflow until the daemon
work lands so it doesn't fire on tag push and produce broken jobs.
cc
Triaged the 8 open CodeQL alerts raised on the 1.2.5 merge commit.
Critical (1) — real, fixed:
* .github/workflows/release-updater-manifest.yml:29 — the resolve
step interpolated `${{ github.event.workflow_run.head_branch }}`
directly into a shell heredoc. `head_branch` on `workflow_run` is
attacker-controllable (any outside contributor can push a branch
whose name embeds a payload). Route the value through an env var
(HEAD_BRANCH) and validate the shape with a strict regex before
letting it flow into shell or $GITHUB_OUTPUT.
Warnings (2) — real, fixed:
* .github/workflows/build-qbzd.yml had no `permissions:` block, so
every job inherited the repo's default GITHUB_TOKEN scope. Add a
top-level `permissions: contents: read`; the existing `release`
job keeps its own `contents: write` override for publishing
artifacts.
Warnings (3) — real, low severity, mitigated:
* crates/qbzd/src/{daemon.rs,session.rs} logged `user_id` at info
level (CodeQL rust/cleartext-logging). Demoted the three lines to
`log::debug!` so default-level logs stay free of the numeric user
id, with a comment noting RUST_LOG=debug re-enables them for
diagnostics.
Warnings (2) — false positives, dismissed in GitHub:
* crates/qbzd/src/login.rs:103 — `println!` to stdout inside the
interactive `qbzd login` subcommand; login confirmation UX, not a
persistent log. (alert #63 dismissed, reason: false positive)
* crates/qbzd/src/qconnect.rs:540 — `session_uuid` is an ephemeral
per-connection identifier QConnect exchanges in cleartext itself;
not a credential or token. (alert #65 dismissed, reason: false
positive)
cc
qbzd is feature-complete on disk but not ready for public release yet.
Keep all code paths in place so it can be unhidden with a one-line
flip, but remove it from the user-facing surface:
* build-qbzd.yml: comment out the `push.tags` trigger so neither a
regular `v*` version tag nor a `qbzd-v*` tag auto-builds artifacts.
workflow_dispatch is preserved for manual testing.
* CastPicker.svelte: guard the QBZ Daemon tab, the manual-connect
section, and the three `v2_qbzd_*` Tauri invocations behind a
`QBZD_ENABLED` flag (currently false). Default activeProtocol
switches to 'chromecast' so the picker doesn't start on a hidden
tab.
* SettingsView: DAC Setup Wizard icon filter swapped — button icon
drops the `invert(1)` so it reads on light accent colors (e.g.
Catppuccin Lavender), and the tooltip copy of the same icon now
has `invert(1)` so it reads on the dark surface bg. Unrelated to
qbzd but bundled since both are pre-release polish.
cc
Adds TAURI_SIGNING_PRIVATE_KEY(_PASSWORD) env to release-linux,
release-linux-aarch64, and release-macos build steps, collects the
resulting .sig files into the release artifacts, and adds a new
release-updater-manifest workflow that waits for all platform sig
files to land on the release and assembles latest.json for the
updater endpoint.
Cherry-picked from #305 by @afonsojramos.
cc
QEMU emulation crashes compiling ring's C code (SIGSEGV in cc).
Switch to ubuntu-24.04-arm native runner — faster and no emulation
issues. Build time drops from ~40min to ~5min.
cc
Ubuntu runners' apt multi-arch is broken for arm64 cross-deps
(security.ubuntu.com 404s for arm64 packages, DEB822 format
sources not handled by sed).
Switch to uraimo/run-on-arch-action: builds natively inside a
Debian Bookworm aarch64 container via QEMU. Slower (~15 min)
but reliable. Binary is native aarch64 with Bookworm's glibc 2.36.
cc
The sed was only modifying /etc/apt/sources.list but security.ubuntu.com
and other sources in sources.list.d/ were also trying to fetch arm64
packages from repos that don't have them (404).
Now: force [arch=amd64] on ALL .list files, arm64 exclusively from
ports.ubuntu.com.
cc
glibc 2.31 is too old for system shared libs (libasound, libdbus)
on Ubuntu 22.04 runners which have glibc 2.35. The linker rejects
symbols from libs compiled against newer glibc.
glibc 2.35 covers: Raspberry Pi OS Bookworm (2.36), Ubuntu 22.04+,
Armbian Bookworm, DietPi Bookworm. Bullseye (2.31) is EOL June 2026.
cc
reqwest default features included native-tls which pulled in
openssl-sys. This broke zigbuild cross-compilation because zig's
C compiler doesn't support the glibc version suffix for openssl
header expansion.
Fix: default-features = false on reqwest in workspace and
qbz-integrations (which had its own reqwest dep with defaults).
Now the entire daemon tree uses rustls-tls exclusively.
cc
Uses cargo-zigbuild to target glibc 2.31 for maximum compatibility:
- Raspberry Pi OS Bullseye and newer
- DietPi, Armbian, Ubuntu 22.04+
Triggered by qbzd-v* tags or manual dispatch.
Produces two binaries: qbzd-linux-x86_64, qbzd-linux-aarch64.
Creates pre-release on tag push.
cc
- Upgrade rust_cast 0.18.1 → 0.21 (switches from openssl to rustls,
removing the openssl-sys dependency that blocked x86_64 cross-compile)
- Split mupdf sys-lib features to Linux-only (on macOS there are no GTK
symbol conflicts, and system libs don't support x86_64 cross-compile
from Apple Silicon)
- Update release-macos workflow to build both aarch64 and x86_64 DMGs
using a matrix strategy
Ebuilds without matching Manifest hashes cause 'Missing digest'
errors on emerge. Download DIST files and compute BLAKE2B/SHA512
for ebuilds, metadata.xml, and tarballs.
cc
When a new issue selects macOS as platform, automatically labels it
platform:macos and assigns afonsojramos. Linux issues get labeled
platform:linux.
cc
Adds Linux/macOS selection as first field in bug reports, feature
requests, and feedback forms. Adapts Linux-specific fields (DE, display
server) to indicate they are Linux-only and adds macOS options to
install method and audio backend dropdowns.
cc