controls/SelectionModel.qml kept the anchor in a string property and then looked it up with a strict comparison, so a list whose rows carry numeric ids never found its anchor again and every Shift-click there degraded to a plain toggle. Ids now compare as strings, which is what the selection map keys already were. The range fill also wrote `m[rows[i].id]` for every row in the span, so a list that mixes group headers into its rows gained an "undefined" key and a count one above the ticks on screen. Rows without an id are skipped. `idKey` names the identifying field for rows that do not call it `id`. scripts/qml-tests/tst_multi_select.qml starts with the rule's own cases and joins qt-runtime-gate.sh and test-crates.yml. cc
289 lines
13 KiB
YAML
289 lines
13 KiB
YAML
# CI for the crates/ workspace on the integration branch.
|
|
#
|
|
# Playback memory presets: shared persistence/migration, live budgets and growth;
|
|
# Qt profile descriptions/selection are covered by the shared QML runtime gate.
|
|
# Three jobs, three economies:
|
|
# test — validate all eight gettext catalogs, then `cargo test` on
|
|
# every crate that needs no Qt (scripts/cargo-test.sh:
|
|
# excludes the frozen Slint crates and qbz-qt), then the named
|
|
# listen-log suite check (qbz-app::listen_log must exist and be
|
|
# green — the per-user listen store's schema, accumulator, end
|
|
# reasons and pause/clear rules), and the account-migration +
|
|
# portable-blacklist suite check (Settings > Import / Export:
|
|
# snapshot/plan/apply/ledger/local copy, additive blacklist
|
|
# merge), plus QConnect controller handoff/read-vs-write,
|
|
# proto3-zero cursor, manual skip/loop/shuffle/autoplay and
|
|
# consecutive-log repetition regression checks, plus SACD
|
|
# 2048/2064-sector TOC/frame/seek equivalence and scanner import/
|
|
# failed-read preservation, plus the DLNA device-description
|
|
# tolerance suite (empty and relative SCPDURL/controlURL must
|
|
# still parse with current http — local rupnp patch, #745).
|
|
# Normal-renderer v1/v3 transport/volume SOAP is also gated.
|
|
# ~5 min cold.
|
|
# qt-gate — the Qt frontend: the five static QML audits, `cargo test -p
|
|
# qbz-qt`, the Slint-free dep-graph gate, and an OFFSCREEN BOOT of
|
|
# debug AND release binaries (including process liveness).
|
|
# Release also boots on Xvfb with a deliberately silent D-Bus.
|
|
# Node executes the actual Local Library filter/tab expressions.
|
|
# This is the thing `cargo check` cannot give:
|
|
# a missing QML component or a call to a bridge member that does
|
|
# not exist compiles clean and throws when the user opens the
|
|
# view. Qt comes from aqt — ubuntu-24.04's own Qt is 6.4.2 and has
|
|
# no QtQuick.Effects module.
|
|
#
|
|
# Rewritten 2026-08-25 for the Slint -> Qt frontend switch. Release packaging
|
|
# stays tag/dispatch-only; this workflow also tests optimized startup on
|
|
# pre-release
|
|
# integration so regressions land before a release cut.
|
|
|
|
# Shared cargo-test.sh also gates signed updater verification, version selection
|
|
# and preference preservation; qt-runtime-gate.sh covers the update UI.
|
|
# Shared cargo-test.sh also gates Orbit HTTP host isolation, library profiles
|
|
# and the SSE public-identity projection. No separate test command to drift.
|
|
# Shared cargo-test.sh also gates the 2026-09 static-review regressions: image-cache
|
|
# LRU eviction, loudness-cache degrade, live-writer log rotation lock, URL-free
|
|
# CDN fetch errors + signed-param redaction, and the link-resolver timeout.
|
|
# Loudness (2026-09): absolute-LUFS cache with ranked sources, full-track scan,
|
|
# one-shot live gain with a 400 ms glide, and the player's start-gain plan.
|
|
# Qt runtime gate includes Library source/count regressions and the Rust
|
|
# playlist-deletion/creation/offline-gapless and maintenance retry regressions.
|
|
# The shared library service gate preserves manual scan feedback while keeping
|
|
# background completions silent, including coalesced work and unavailable roots.
|
|
# Watcher read-feedback and no-op content invalidation are in cargo-test.sh;
|
|
# Library folders status/action layout and tooltips are in qt-runtime-gate.sh.
|
|
name: test-crates
|
|
|
|
# Shared playback-cache gate covers #774 sealed buffers, Hi-Res decode continuity,
|
|
# memory policy admission/pressure, persistence and qbzd settings round trips.
|
|
# Purchases navigation placement matrix; missing-root watcher retry/recovery.
|
|
# Disk-backed streaming, 24/192 seek, cache reader leases and cancelled prefetch.
|
|
|
|
on:
|
|
pull_request:
|
|
# pre-release is the real base; main is listed so a PR opened against main
|
|
# still runs once before redirect-pr-to-prerelease retargets it.
|
|
branches: [pre-release, main]
|
|
paths:
|
|
- "crates/**"
|
|
- "scripts/cargo-test.sh"
|
|
- "scripts/install.sh"
|
|
- "scripts/test-installer.py"
|
|
- "scripts/test_qt_library.mjs"
|
|
- "scripts/test_qt_purchases_navigation.mjs"
|
|
- "scripts/qt-*.py"
|
|
- "scripts/qt-*.sh"
|
|
- "scripts/test_qt_build_gates.py"
|
|
- "scripts/packaging/windows_crt.py"
|
|
- "scripts/packaging/test_windows_crt.py"
|
|
- "scripts/packaging/smoke_release.py"
|
|
- "scripts/packaging/test_smoke_release.py"
|
|
- "scripts/qml-audits/**"
|
|
- "scripts/qml-tests/**"
|
|
- "scripts/test-search-local-qml.sh"
|
|
- "scripts/test-orbit-qml.sh"
|
|
- "scripts/test-updates-qml.sh"
|
|
- "scripts/test-playback-cache-qml.sh"
|
|
- "scripts/test-text-edit-menu-qml.sh"
|
|
- "scripts/test-compact-seek-qml.sh"
|
|
- "scripts/test-library-folders-qml.sh"
|
|
- "scripts/test-array-model-qml.sh"
|
|
- "scripts/test-keyed-model-qml.sh"
|
|
- "scripts/test-multi-select-qml.sh"
|
|
- "scripts/test-wallpaper-field-qml.sh"
|
|
- "scripts/test-card-menu-qml.sh"
|
|
- "scripts/test-orbit-daemon.py"
|
|
- ".github/workflows/test-crates.yml"
|
|
push:
|
|
# Integration branch only — never main (a push to main is a release merge).
|
|
branches: [pre-release]
|
|
paths:
|
|
- "crates/**"
|
|
- "scripts/cargo-test.sh"
|
|
- "scripts/install.sh"
|
|
- "scripts/test-installer.py"
|
|
- "scripts/test_qt_library.mjs"
|
|
- "scripts/test_qt_purchases_navigation.mjs"
|
|
- "scripts/qt-*.py"
|
|
- "scripts/qt-*.sh"
|
|
- "scripts/test_qt_build_gates.py"
|
|
- "scripts/packaging/windows_crt.py"
|
|
- "scripts/packaging/test_windows_crt.py"
|
|
- "scripts/packaging/smoke_release.py"
|
|
- "scripts/packaging/test_smoke_release.py"
|
|
- "scripts/qml-audits/**"
|
|
- "scripts/qml-tests/**"
|
|
- "scripts/test-search-local-qml.sh"
|
|
- "scripts/test-orbit-qml.sh"
|
|
- "scripts/test-updates-qml.sh"
|
|
- "scripts/test-playback-cache-qml.sh"
|
|
- "scripts/test-text-edit-menu-qml.sh"
|
|
- "scripts/test-compact-seek-qml.sh"
|
|
- "scripts/test-library-folders-qml.sh"
|
|
- "scripts/test-array-model-qml.sh"
|
|
- "scripts/test-keyed-model-qml.sh"
|
|
- "scripts/test-multi-select-qml.sh"
|
|
- "scripts/test-wallpaper-field-qml.sh"
|
|
- "scripts/test-card-menu-qml.sh"
|
|
- "scripts/test-orbit-daemon.py"
|
|
- ".github/workflows/test-crates.yml"
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: test-crates-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
# Same pin as build-qt-linux.yml (the Linux floor). Bump both together.
|
|
QT_VERSION: ${{ vars.QT_VERSION_LINUX || '6.8.3' }}
|
|
|
|
jobs:
|
|
test:
|
|
name: cargo test (workspace, no UI)
|
|
runs-on: ubuntu-24.04
|
|
# The rule: --no-fail-fast, 20 minutes. All cores: measured cold on an
|
|
# equivalent 4-vCPU VM, 392 s (jobs=4) vs 589 s (jobs=2), 2.7 GB peak —
|
|
# the old jobs=2 was a Slint-era memory tier with nothing left to protect.
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Install system dependencies
|
|
run: |
|
|
# Non-UI crates still link ALSA / JACK / D-Bus (qbz-audio) and
|
|
# OpenSSL (reqwest).
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends \
|
|
build-essential pkg-config gettext dbus-daemon desktop-file-utils \
|
|
libasound2-dev libjack-jackd2-dev \
|
|
libdbus-1-dev libssl-dev
|
|
|
|
- name: Setup Rust
|
|
uses: dtolnay/rust-toolchain@stable
|
|
|
|
- name: Rust cache
|
|
uses: Swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: crates -> target
|
|
shared-key: test-crates-linux
|
|
cache-on-failure: true
|
|
save-if: ${{ github.ref == 'refs/heads/pre-release' }}
|
|
|
|
- name: cargo test (scripts/cargo-test.sh — the local mirror)
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
run: |
|
|
rustc -V; cargo -V
|
|
START=$(date +%s)
|
|
./scripts/cargo-test.sh
|
|
echo "cargo test wall time: $(( $(date +%s) - START ))s"
|
|
|
|
# Every shader must bake. Separate job so qsb is never on PATH while cargo
|
|
# runs (see qt-gate). The BAKE toolchain is pinned apart from the runtime
|
|
# floor: the committed .qsb need qsb >= 6.9 (plasma/ambient's uint hash
|
|
# constants vs the GLSL 1.20 variant, which desktop GL <= 3.1 selects at
|
|
# runtime, so it must stay in the pack) — see shader_bake_gate.sh.
|
|
shader-gate:
|
|
name: shader bake gate
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 15
|
|
env:
|
|
QT_SHADERTOOLS_VERSION: ${{ vars.QT_VERSION_SHADERTOOLS || '6.9.3' }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Install Qt ${{ env.QT_SHADERTOOLS_VERSION }} + shader tools
|
|
uses: jurplel/install-qt-action@v4
|
|
with:
|
|
version: ${{ env.QT_SHADERTOOLS_VERSION }}
|
|
host: linux
|
|
target: desktop
|
|
arch: linux_gcc_64
|
|
modules: qtshadertools
|
|
cache: true
|
|
- name: Bake every shader to a temp dir
|
|
run: scripts/qml-audits/shader_bake_gate.sh "$QT_ROOT_DIR/bin/qsb"
|
|
|
|
qt-gate:
|
|
name: qt gate (audits, tests, offscreen boot)
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 60
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Install system dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends \
|
|
build-essential pkg-config cmake clang libclang-dev lld nasm dbus \
|
|
libasound2-dev libjack-jackd2-dev libdbus-1-dev libssl-dev \
|
|
libgl1-mesa-dev libegl1-mesa-dev libxkbcommon-dev libwayland-dev \
|
|
xvfb nodejs libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
|
|
libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0
|
|
# cxx-qt prefers LLD when present; without it, Ubuntu falls back to
|
|
# the deprecated gold linker. Do not add workspace-wide RUSTFLAGS.
|
|
ld.lld --version
|
|
|
|
# No qtshadertools here ON PURPOSE: with qsb on PATH build.rs re-bakes
|
|
# shaders by mtime, which makes a fresh checkout nondeterministic. The
|
|
# build explicitly validates/uses committed .qsb; shader health is the
|
|
# shader-gate job, independent of tools accidentally present on PATH.
|
|
- name: Install Qt ${{ env.QT_VERSION }}
|
|
uses: jurplel/install-qt-action@v4
|
|
with:
|
|
version: ${{ env.QT_VERSION }}
|
|
host: linux
|
|
target: desktop
|
|
arch: linux_gcc_64
|
|
cache: true
|
|
|
|
- name: qmllint (QML type check)
|
|
run: scripts/qml-audits/qmllint_gate.sh "$QT_ROOT_DIR"
|
|
|
|
# The audits need Qt's own plugins.qmltypes to resolve QtQuick types —
|
|
# hence after the Qt install, pointed at it. ~1 s, and the class of
|
|
# mistake nothing else in CI can see (a missing component, a bridge
|
|
# member that does not exist).
|
|
- name: QML audits
|
|
env:
|
|
QBZ_QT_QML_DIR: ${{ env.QT_ROOT_DIR }}/qml
|
|
run: |
|
|
set -euo pipefail
|
|
for a in qml_resolution_audit qml_singleton_xref qml_eager_tab_audit qml_module_registration_audit qml_icon_bake_audit qml_on_prefixed_property_audit; do
|
|
echo "::group::$a"
|
|
python3 "scripts/qml-audits/$a.py" "$PWD/crates/qbz-qt"
|
|
echo "::endgroup::"
|
|
done
|
|
|
|
- name: Setup Rust
|
|
uses: dtolnay/rust-toolchain@stable
|
|
|
|
- name: Rust cache
|
|
uses: Swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: crates -> target
|
|
shared-key: qt-gate-${{ env.QT_VERSION }}
|
|
cache-on-failure: true
|
|
save-if: ${{ github.ref == 'refs/heads/pre-release' }}
|
|
|
|
# The exact QT=1 half of scripts/cargo-test.sh, minus its `test` job
|
|
# (already run above) and its shader gate (the shader-gate job, so qsb
|
|
# is never on PATH while cargo runs here): dep-graph gate, cargo test
|
|
# -p qbz-qt, Local Library/Kiosk artwork/navigation and federated-search
|
|
# view/activation regressions, debug/release
|
|
# offscreen boot. One shared runtime script.
|
|
- name: qt gates (scripts/cargo-test.sh QT=1, without the workspace test)
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
QBZ_QT_QML_DIR: ${{ env.QT_ROOT_DIR }}/qml
|
|
QBZ_PREBUILT_SHADERS: '1'
|
|
run: |
|
|
set -euo pipefail
|
|
hits=$(cargo tree --manifest-path crates/Cargo.toml -p qbz-qt -e normal \
|
|
| grep -E '\b(slint|qbz-ui|qbz-slint-common|qbz-dac-wizard) v' || true)
|
|
[ -z "$hits" ] || { echo "::error::qbz-qt graph resolves Slint crates:"; echo "$hits"; exit 1; }
|
|
bash scripts/qt-runtime-gate.sh
|