Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "2.0.0-beta.1"
description = "Self-hosted music server with WaveFlow and Subsonic compatibility."
authors = ["InstaZDLL <github.105mh@8shield.net>"]
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"
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<p align="center">
<img src="https://img.shields.io/static/v1?label=version&message=2.0.0-beta.1&color=emerald&style=flat-square" alt="Version" />
<img src="https://img.shields.io/badge/rust-1.94%2B-orange?style=flat-square&logo=rust" alt="Rust 1.94+" />
<img src="https://img.shields.io/badge/rust-1.98%2B-orange?style=flat-square&logo=rust" alt="Rust 1.98+" />
<img src="https://img.shields.io/badge/database-SQLite%20only-003b57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLite only" />
<img src="https://img.shields.io/badge/API-OpenSubsonic-6a4bd6?style=flat-square" alt="OpenSubsonic" />
<img src="https://img.shields.io/badge/docker-ghcr.io-2496ed?style=flat-square&logo=docker&logoColor=white" alt="Docker image on GHCR" />
Expand Down Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion src/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
24 changes: 24 additions & 0 deletions tests/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,35 @@ use waveflow_server::Config;
/// `git ls-tree --name-only <tag> 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}\", <version of the newest file under migrations-v2/>)"
);
}

/// The identifiers the fixture below writes and the assertions read back.
///
/// UUIDs because every real row's is: a fixture shaped unlike production is
Expand Down
19 changes: 12 additions & 7 deletions tests/uploads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1465,21 +1465,26 @@ 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;
}

assert!(
!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.
Expand Down
Loading