adapters install rustls ring provider before reqwest client; tui keyring deps to workspace; doc fixes
Some checks failed
CI / Check / Test (push) Has been cancelled
CI / Check / Test (pull_request) Has been cancelled

This commit is contained in:
2026-10-26 05:24:25 +01:00
parent f76559356a
commit a270632e87
16 changed files with 55 additions and 8 deletions

3
Cargo.lock generated
View File

@@ -3052,6 +3052,7 @@ dependencies = [
"config", "config",
"domain", "domain",
"reqwest 0.13.3", "reqwest 0.13.3",
"rustls",
"serde", "serde",
] ]
@@ -3748,6 +3749,7 @@ dependencies = [
"config", "config",
"domain", "domain",
"reqwest 0.13.3", "reqwest 0.13.3",
"rustls",
] ]
[[package]] [[package]]
@@ -5539,6 +5541,7 @@ dependencies = [
"config", "config",
"domain", "domain",
"reqwest 0.13.3", "reqwest 0.13.3",
"rustls",
"serde", "serde",
] ]

View File

@@ -113,6 +113,9 @@ webp = "0.3"
rss = { version = "2" } rss = { version = "2" }
ratatui = "0.30.0" ratatui = "0.30.0"
keyring-core = "1.0.0" keyring-core = "1.0.0"
apple-native-keyring-store = { version = "1.0.0", features = ["keychain"] }
zbus-secret-service-keyring-store = { version = "1.0.0" }
windows-native-keyring-store = { version = "1.0.0" }
directories = "6" directories = "6"
api-types = { path = "crates/api-types" } api-types = { path = "crates/api-types" }

View File

@@ -176,7 +176,7 @@ Build without federation with `--no-default-features`. `sqlite` and `sqlite-fede
### Build notes ### Build notes
- `cargo build` / `cargo run` build every crate except the deprecated `tui`, which sits outside `default-members`. Use `-p tui` for it; `make check` and CI use `--workspace`, so it is still linted and tested. - `cargo build` / `cargo test` build every crate except the deprecated `tui`, which sits outside `default-members`. Use `-p tui` for it; `make check` and CI use `--workspace`, so it is still linted and tested.
- Release builds use `codegen-units = 1` and `strip = true` (smaller binaries; thin LTO was measured and made builds slower and binaries larger, so it is off). - Release builds use `codegen-units = 1` and `strip = true` (smaller binaries; thin LTO was measured and made builds slower and binaries larger, so it is off).
- SQLite is compiled from source (bundled `libsqlite3-sys`), so the first clean build spends about a minute on it; incremental builds and the Docker/CI caches don't repeat that. - SQLite is compiled from source (bundled `libsqlite3-sys`), so the first clean build spends about a minute on it; incremental builds and the Docker/CI caches don't repeat that.
- TLS uses rustls with the `ring` provider only; `aws-lc` and `actix` are kept out of the graph by `make check-no-second-web-stack`. - TLS uses rustls with the `ring` provider only; `aws-lc` and `actix` are kept out of the graph by `make check-no-second-web-stack`.

View File

@@ -8,5 +8,6 @@ config = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
async-trait = { workspace = true } async-trait = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
rustls = { workspace = true, features = ["ring", "std"] }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
domain = { workspace = true } domain = { workspace = true }

View File

@@ -16,6 +16,8 @@ pub(crate) struct OmdbProvider {
impl OmdbProvider { impl OmdbProvider {
pub(crate) fn new(api_key: String) -> Self { pub(crate) fn new(api_key: String) -> Self {
// reqwest is built with rustls-no-provider; install ring so this works without bootstrap.
let _ = rustls::crypto::ring::default_provider().install_default();
Self { Self {
client: reqwest::Client::new(), client: reqwest::Client::new(),
api_key, api_key,

View File

@@ -15,6 +15,8 @@ pub(crate) struct TmdbProvider {
impl TmdbProvider { impl TmdbProvider {
pub(crate) fn new(api_key: String) -> Self { pub(crate) fn new(api_key: String) -> Self {
// reqwest is built with rustls-no-provider; install ring so this works without bootstrap.
let _ = rustls::crypto::ring::default_provider().install_default();
Self { Self {
client: reqwest::Client::new(), client: reqwest::Client::new(),
api_key, api_key,

View File

@@ -0,0 +1,6 @@
//! Fresh test binary, no bootstrap: constructing the client must not panic
//! for lack of a rustls CryptoProvider.
#[test]
fn omdb_client_builds_without_bootstrap() {
let _ = metadata::MetadataClientImpl::new_omdb("key".into());
}

View File

@@ -0,0 +1,6 @@
//! Fresh test binary, no bootstrap: constructing the client must not panic
//! for lack of a rustls CryptoProvider.
#[test]
fn tmdb_client_builds_without_bootstrap() {
let _ = metadata::MetadataClientImpl::new_tmdb("key".into());
}

View File

@@ -8,4 +8,5 @@ config = { workspace = true }
domain = { workspace = true } domain = { workspace = true }
async-trait = { workspace = true } async-trait = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
rustls = { workspace = true, features = ["ring", "std"] }
anyhow = { workspace = true } anyhow = { workspace = true }

View File

@@ -16,6 +16,8 @@ pub struct ReqwestPosterFetcher {
impl ReqwestPosterFetcher { impl ReqwestPosterFetcher {
pub fn new(config: PosterFetcherConfig) -> anyhow::Result<Self> { pub fn new(config: PosterFetcherConfig) -> anyhow::Result<Self> {
// reqwest is built with rustls-no-provider; install ring so this works without bootstrap.
let _ = rustls::crypto::ring::default_provider().install_default();
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.timeout(Duration::from_secs(config.timeout_seconds)) .timeout(Duration::from_secs(config.timeout_seconds))
.build()?; .build()?;

View File

@@ -0,0 +1,8 @@
//! Fresh test binary, no bootstrap: constructing the client must not panic
//! for lack of a rustls CryptoProvider.
use poster_fetcher::{PosterFetcherConfig, ReqwestPosterFetcher};
#[test]
fn poster_fetcher_builds_without_bootstrap() {
ReqwestPosterFetcher::new(PosterFetcherConfig { timeout_seconds: 5 }).expect("client builds");
}

View File

@@ -7,6 +7,7 @@ edition.workspace = true
config = { workspace = true } config = { workspace = true }
domain = { workspace = true } domain = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
rustls = { workspace = true, features = ["ring", "std"] }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
async-trait = { workspace = true } async-trait = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }

View File

@@ -12,6 +12,8 @@ impl TmdbEnrichmentClient {
.tmdb_api_key .tmdb_api_key
.clone() .clone()
.ok_or_else(|| DomainError::InfrastructureError("TMDB_API_KEY is not set".into()))?; .ok_or_else(|| DomainError::InfrastructureError("TMDB_API_KEY is not set".into()))?;
// reqwest is built with rustls-no-provider; install ring so this works without bootstrap.
let _ = rustls::crypto::ring::default_provider().install_default();
Ok(Self { Ok(Self {
api_key, api_key,
http: reqwest::Client::new(), http: reqwest::Client::new(),

View File

@@ -0,0 +1,12 @@
//! Fresh test binary, no bootstrap: constructing the client must not panic
//! for lack of a rustls CryptoProvider.
use tmdb_enrichment::TmdbEnrichmentClient;
#[test]
fn tmdb_enrichment_builds_without_bootstrap() {
let cfg = config::MetadataConfig {
tmdb_api_key: Some("key".into()),
..Default::default()
};
TmdbEnrichmentClient::from_config(&cfg).expect("client builds");
}

View File

@@ -20,8 +20,8 @@ pub fn install_crypto_provider() {
mod tests { mod tests {
use super::*; use super::*;
/// Runs alone in the lib test binary, so no other code (k-ap included) /// Only test in this crate's lib test binary, so nothing else in this
/// has installed a provider first. /// process installs a provider before `install_crypto_provider` runs.
#[test] #[test]
fn https_client_builds_after_install() { fn https_client_builds_after_install() {
install_crypto_provider(); install_crypto_provider();

View File

@@ -18,11 +18,9 @@ keyring-core = { workspace = true }
directories = { workspace = true } directories = { workspace = true }
csv = { workspace = true } csv = { workspace = true }
apple-native-keyring-store = { version = "1.0.0", optional = true, features = [ apple-native-keyring-store = { workspace = true, optional = true }
"keychain", zbus-secret-service-keyring-store = { workspace = true, optional = true }
] } windows-native-keyring-store = { workspace = true, optional = true }
zbus-secret-service-keyring-store = { version = "1.0.0", optional = true }
windows-native-keyring-store = { version = "1.0.0", optional = true }
api-types = { workspace = true } api-types = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }