From aaf92411915c5640855bb49459a4047a7d11e656 Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Sat, 17 Oct 2026 10:55:32 +0200 Subject: [PATCH 1/5] ci(codeql): analyse every pull request, whatever paths it touches The ruleset on main requires a CodeQL result, and the pull_request trigger was filtered to the Rust paths. A branch touching only webapp/ or docs/ never started an analysis, so it finished green and stayed BLOCKED waiting for one: #250, #251, #254, #262, #278 and #279. Signed-off-by: InstaZDLL --- .github/workflows/codeql.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3f02c24..d2eba44 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -10,15 +10,13 @@ on: - "migrations-v2/**" - "tests/**" - ".github/workflows/**" + # Every pull request, with no path filter. The ruleset on `main` requires a + # CodeQL result, and a filtered run never comes for a branch that touches + # only `webapp/` or `docs/`: such a PR finished green and still BLOCKED, + # waiting for an analysis nothing would start. On those the analysis finds + # nothing new, which is the answer the requirement is waiting for. pull_request: branches: [main] - paths: - - "Cargo.toml" - - "Cargo.lock" - - "src/**" - - "migrations-v2/**" - - "tests/**" - - ".github/workflows/**" schedule: - cron: "34 7 * * 4" From 483444b038ba19387749904287517c05ddc7d5a8 Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Sat, 17 Oct 2026 10:55:32 +0200 Subject: [PATCH 2/5] test(migrations): fail the release bump that forgets its schema RELEASED_SCHEMAS had to be extended by hand at every tag, and nothing noticed when it was not. The crate version is the tag a release ships as, so a test now requires a line for it: the PR that bumps the version fails until it records the schema, while the baseline is still simply the newest migration. Signed-off-by: InstaZDLL --- tests/migrations.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/migrations.rs b/tests/migrations.rs index b9a80a4..1a92482 100644 --- a/tests/migrations.rs +++ b/tests/migrations.rs @@ -40,11 +40,35 @@ use waveflow_server::Config; /// `git ls-tree --name-only migrations-v2/`, not read off the changelog, /// because the changelog records what was written and the tree records what /// shipped. +/// +/// Remembering was the whole mechanism, so +/// `the_version_being_shipped_has_its_schema_recorded` now holds it: the PR +/// that bumps `version` in `Cargo.toml` for a release fails until it adds that +/// tag here, with the newest migration of that moment as its baseline. const RELEASED_SCHEMAS: [(&str, i64); 2] = [ ("v2.0.0-beta.0", 20_260_823_020_000), ("v2.0.0-beta.1", 20_260_914_030_000), ]; +/// The crate's version is the tag it ships as, so it must name a line above. +/// +/// A release is cut by a PR that bumps `version` and is then tagged, and the +/// version stays put until the next one. So this holds between releases, and +/// fails exactly on the bump that forgot the schema — before the tag exists, +/// which is the only moment the baseline is still the newest migration and +/// needs no archaeology to find. +#[test] +fn the_version_being_shipped_has_its_schema_recorded() { + let tag = concat!("v", env!("CARGO_PKG_VERSION")); + assert!( + RELEASED_SCHEMAS + .iter() + .any(|(released, _)| *released == tag), + "Cargo.toml is at {tag}, and RELEASED_SCHEMAS has no line for it: add \ + (\"{tag}\", )" + ); +} + /// The identifiers the fixture below writes and the assertions read back. /// /// UUIDs because every real row's is: a fixture shaped unlike production is From 1f8c725c041459757f37b61f0b2f81fca5d261c6 Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Sat, 17 Oct 2026 10:55:33 +0200 Subject: [PATCH 3/5] test(uploads): wait for the swept row as well as the swept file The sweep removes the staging file first and the session row second, and the test waited on the file only before counting the row once. On the Windows runner it landed between the two, twice. Closes #280 Signed-off-by: InstaZDLL --- tests/uploads.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/uploads.rs b/tests/uploads.rs index c3986dc..2c6bfed 100644 --- a/tests/uploads.rs +++ b/tests/uploads.rs @@ -1465,8 +1465,18 @@ async fn abandoned_transfers_are_swept_without_a_negotiation() { // No negotiation, no request of any kind: only the scheduled sweep. state.services.spawn_upload_sweeper(); + // The sweep removes the file first and the row second, so waiting on the + // file alone and then counting the row once reads the gap between the + // two: a slow runner failed here with the row still present (#280). + let rows = || async { + sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM upload_session WHERE id=?") + .bind(&session) + .fetch_one(state.db.pool()) + .await + .unwrap() + }; let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10); - while staging.exists() && std::time::Instant::now() < deadline { + while (staging.exists() || rows().await > 0) && std::time::Instant::now() < deadline { tokio::time::sleep(std::time::Duration::from_millis(25)).await; } @@ -1474,12 +1484,7 @@ async fn abandoned_transfers_are_swept_without_a_negotiation() { !staging.exists(), "an abandoned transfer must not hold the disk until someone happens to upload again" ); - let rows: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM upload_session WHERE id=?") - .bind(&session) - .fetch_one(state.db.pool()) - .await - .unwrap(); - assert_eq!(rows, 0); + assert_eq!(rows().await, 0); } /// A staging file that cannot be removed keeps its session. From a4ea9d28bf72e289320aecdd15ca45cbd99674ca Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Sat, 17 Oct 2026 10:55:33 +0200 Subject: [PATCH 4/5] fix(media): give the start-up tool check thirty seconds instead of five ffmpeg -version overran five seconds on the Windows runner under a parallel test run. The check exists to catch a missing or hung binary, which thirty seconds still does; a healthy boot answers long before. Closes #281 Signed-off-by: InstaZDLL --- src/media.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/media.rs b/src/media.rs index 26e0e34..028fa46 100644 --- a/src/media.rs +++ b/src/media.rs @@ -1316,9 +1316,19 @@ impl IntoResponse for MediaError { } } +/// How long `ffmpeg -version` and `ffprobe -version` get at start-up. +/// +/// The check refuses to boot on a binary that is missing or hangs; it is not +/// timing a healthy one. Five seconds was overrun on the Windows CI runner, +/// where a parallel test run starts several cold FFmpeg processes at once +/// (#281). Thirty still catches a hung binary, and a healthy boot answers long +/// before it, so the only thing the larger budget changes is how long a broken +/// installation takes to say so. +const TOOL_CHECK_TIMEOUT: Duration = Duration::from_secs(30); + async fn check_tool(path: &Path, name: &str) -> anyhow::Result<()> { let output = tokio::time::timeout( - Duration::from_secs(5), + TOOL_CHECK_TIMEOUT, Command::new(path) .arg("-version") .stdin(Stdio::null()) From 672fad4c1628ea6b9d4a1493fa2b57aa5b87cbf5 Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Sat, 17 Oct 2026 10:55:33 +0200 Subject: [PATCH 5/5] build: move the MSRV and the release image to rust 1.98 CI and development compile with stable (1.98.1) while the release image was built by rust:1.94. Both now say 1.98, so the compiler that tests a PR is the one that builds what ships. Closes #283 Signed-off-by: InstaZDLL --- Cargo.toml | 2 +- Dockerfile | 2 +- README.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c99c9a..995fb81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0-beta.1" description = "Self-hosted music server with WaveFlow and Subsonic compatibility." authors = ["InstaZDLL "] edition = "2021" -rust-version = "1.94" +rust-version = "1.98" license = "AGPL-3.0-only" repository = "https://github.com/InstaZDLL/waveflow-server" homepage = "https://waveflow.app" diff --git a/Dockerfile b/Dockerfile index 6c7eefd..9d1088e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ RUN bun install --frozen-lockfile COPY webapp/ ./ RUN bun run build -FROM rust:1.94-bookworm AS builder +FROM rust:1.98-bookworm AS builder WORKDIR /build # cmake builds the libopus that waveflow-core bundles (`opusic-sys`); the # rust image ships a C compiler but not cmake. diff --git a/README.md b/README.md index a8ca4e1..9d55e10 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

Version - Rust 1.94+ + Rust 1.98+ SQLite only OpenSubsonic Docker image on GHCR @@ -57,7 +57,7 @@ Open `http://your-host:4533` and sign in. The scan starts on its own. ### From source -Requires **Rust 1.94+** as well. +Requires **Rust 1.98+** as well. ```bash bun --cwd=webapp install