Fix 18 issues flagged in audit + bump to v0.6.2
P1-A: normalizer derives `online` from rtnetlink event kind (link.up/down,
route.default.changed, address.added/removed) so bread.network.connected
fires correctly on all systems using rtnetlink.
P1-B: stream_events consumes the subscribe ack before the event loop so the
first line is not printed as garbage.
P1-C: UPowerAdapter::probe() validates D-Bus synchronously before committing;
the sysfs fallback now actually triggers when D-Bus is unavailable.
P2-A: profile.list returns the full profile state (active + history) instead
of the always-empty profiles map.
P2-B: profile history capped at 50 entries in both StateCommand and
apply_event_to_state to prevent unbounded growth.
P2-C: RtnetlinkAdapter::new() no longer spawns an orphaned tokio task;
it validates availability by constructing and immediately dropping the
connection tuple.
P2-D: Lua-side hyprland_request_socket() logs a warn when multiple
Hyprland instances are found, matching the adapter-side behaviour.
P2-E: Malformed JSON from an IPC client returns an error response and
continues rather than closing the entire connection.
P3-A: Remove the `ends_with(".*")` prefix-match shortcut from both the
subscription table and the IPC event filter. `bread.*` now means
one segment (matching documented API semantics: `* = one segment`).
Tests updated accordingly.
P4-A: Remove unused `git2` and `glob` workspace dependencies (left over
from bread-sync extraction).
P4-B: breadd dev-dependency `tempfile` declared via workspace = true.
P4-C: Remove unreachable XDG_CONFIG_HOME branch in modules_dir(); dirs
already reads that var internally before returning None.
P4-D: Delete duplicate send_request_with_stream(); print_doctor() now
uses socket.exists() + send_request() directly.
P5-A: release.yml drops `--lib` from cargo test so integration tests run
in the release gate.
P6-A: bluetooth_spawn / bluetooth_query replace expect() on tokio runtime
construction with error logging / error propagation.
P6-B: Spin loops in lua/mod.rs add std:🧵:yield_now() after the
PAUSE hint to reduce CPU burn under sustained RwLock contention.
P6-C: All Mutex::lock().expect("... poisoned") in lua/mod.rs replaced with
unwrap_or_else(|e| e.into_inner()) for poison recovery.
P7-B: bread.system.startup event moved from main.rs into ipc::Server::serve()
so it fires after the socket is bound (smaller race window for early
subscribers).
This commit is contained in:
parent
0f3136ca8d
commit
3115a4230b
17 changed files with 146 additions and 136 deletions
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bread-cli"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
|
|
|
|||
|
|
@ -374,6 +374,18 @@ async fn stream_events(
|
|||
.await?;
|
||||
|
||||
let mut lines = BufReader::new(read_half).lines();
|
||||
|
||||
// Consume the subscribe ack before entering the event loop.
|
||||
match lines.next_line().await? {
|
||||
Some(ack) => {
|
||||
let v: Value = serde_json::from_str(&ack)?;
|
||||
if let Some(err) = v.get("error").and_then(Value::as_str) {
|
||||
anyhow::bail!("{err}");
|
||||
}
|
||||
}
|
||||
None => anyhow::bail!("daemon closed connection during subscribe"),
|
||||
}
|
||||
|
||||
while let Some(line) = lines.next_line().await? {
|
||||
let value: Value = serde_json::from_str(&line)?;
|
||||
if raw_json {
|
||||
|
|
@ -507,23 +519,17 @@ async fn watch_reload(socket: &Path) -> Result<()> {
|
|||
}
|
||||
|
||||
async fn print_doctor(socket: &Path) -> Result<()> {
|
||||
let stream = match UnixStream::connect(socket).await {
|
||||
Ok(stream) => stream,
|
||||
Err(err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
println!("bread doctor");
|
||||
println!(" daemon ✗ not running");
|
||||
println!(" socket {} (not found)", socket.display());
|
||||
println!();
|
||||
println!(" start the daemon: systemctl --user start breadd");
|
||||
println!(" view logs: journalctl --user -u breadd -f");
|
||||
return Ok(());
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
if !socket.exists() {
|
||||
println!("bread doctor");
|
||||
println!(" daemon ✗ not running");
|
||||
println!(" socket {} (not found)", socket.display());
|
||||
println!();
|
||||
println!(" start the daemon: systemctl --user start breadd");
|
||||
println!(" view logs: journalctl --user -u breadd -f");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let response = send_request_with_stream(stream, "health", json!({})).await?;
|
||||
let response = send_request(socket, "health", json!({})).await?;
|
||||
render_doctor(&response);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -585,33 +591,6 @@ fn render_doctor(health: &Value) {
|
|||
}
|
||||
}
|
||||
|
||||
async fn send_request_with_stream(
|
||||
stream: UnixStream,
|
||||
method: &str,
|
||||
params: Value,
|
||||
) -> Result<Value> {
|
||||
let (read_half, mut write_half) = stream.into_split();
|
||||
let request = json!({
|
||||
"id": "1",
|
||||
"method": method,
|
||||
"params": params,
|
||||
});
|
||||
|
||||
write_half
|
||||
.write_all(format!("{}\n", serde_json::to_string(&request)?).as_bytes())
|
||||
.await?;
|
||||
|
||||
let mut lines = BufReader::new(read_half).lines();
|
||||
let Some(line) = lines.next_line().await? else {
|
||||
anyhow::bail!("daemon closed connection without response");
|
||||
};
|
||||
let response: Value = serde_json::from_str(&line)?;
|
||||
if let Some(error) = response.get("error").and_then(Value::as_str) {
|
||||
anyhow::bail!(error.to_string());
|
||||
}
|
||||
Ok(response.get("result").cloned().unwrap_or_else(|| json!({})))
|
||||
}
|
||||
|
||||
fn config_directory() -> PathBuf {
|
||||
if let Ok(xdg) = env::var("XDG_CONFIG_HOME") {
|
||||
return Path::new(&xdg).join("bread");
|
||||
|
|
|
|||
|
|
@ -134,9 +134,6 @@ pub fn modules_dir() -> PathBuf {
|
|||
if let Some(cfg) = dirs::config_dir() {
|
||||
return cfg.join("bread").join("modules");
|
||||
}
|
||||
if let Ok(xdg) = std::env::var("XDG_CONFIG_HOME") {
|
||||
return PathBuf::from(xdg).join("bread").join("modules");
|
||||
}
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
return PathBuf::from(home)
|
||||
.join(".config")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue