Skip to content

Commit 65baf12

Browse files
brynaryclaude
andcommitted
feat(cli): warn on client/server version mismatch in fabro version
When stderr is a TTY and text output is used, print a yellow `warning:` line on stderr if the server reports a version that differs from the client. JSON output and non-interactive contexts stay silent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0d9f71d commit 65baf12

1 file changed

Lines changed: 84 additions & 0 deletions

File tree

lib/crates/fabro-cli/src/commands/version.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::io::IsTerminal;
2+
13
use anyhow::Result;
24
use fabro_types::settings::CliSettings;
35
use fabro_types::settings::cli::{CliLayer, OutputFormat};
@@ -57,9 +59,41 @@ pub(crate) async fn version_command(
5759
}
5860

5961
print_text_output(&client, &server_info);
62+
warn_on_version_mismatch(&client, &server_info, printer);
6063
Ok(())
6164
}
6265

66+
fn warn_on_version_mismatch(
67+
client: &ClientVersionInfo,
68+
server: &ServerVersionInfo,
69+
printer: Printer,
70+
) {
71+
if !std::io::stderr().is_terminal() {
72+
return;
73+
}
74+
let Some(message) = version_mismatch_message(client.version, server) else {
75+
return;
76+
};
77+
let yellow = console::Style::new().yellow();
78+
fabro_util::printerr!(printer, "\n{} {}", yellow.apply_to("warning:"), message);
79+
}
80+
81+
fn version_mismatch_message(client_version: &str, server: &ServerVersionInfo) -> Option<String> {
82+
let ServerVersionInfo::Success {
83+
version: Some(server_version),
84+
..
85+
} = server
86+
else {
87+
return None;
88+
};
89+
if server_version == client_version {
90+
return None;
91+
}
92+
Some(format!(
93+
"client version ({client_version}) does not match server version ({server_version})"
94+
))
95+
}
96+
6397
struct ClientVersionInfo {
6498
version: &'static str,
6599
git_sha: &'static str,
@@ -210,3 +244,53 @@ fn format_uptime(total_secs: i64) -> String {
210244
format!("{seconds}s")
211245
}
212246
}
247+
248+
#[cfg(test)]
249+
mod tests {
250+
use super::*;
251+
252+
#[test]
253+
fn version_mismatch_message_covers_all_branches() {
254+
let matching = ServerVersionInfo::Success {
255+
address: "http://localhost".into(),
256+
version: Some("1.0.0".into()),
257+
git_sha: None,
258+
build_date: None,
259+
os: None,
260+
arch: None,
261+
uptime_secs: None,
262+
};
263+
assert_eq!(version_mismatch_message("1.0.0", &matching), None);
264+
265+
let mismatched = ServerVersionInfo::Success {
266+
address: "http://localhost".into(),
267+
version: Some("1.2.0".into()),
268+
git_sha: None,
269+
build_date: None,
270+
os: None,
271+
arch: None,
272+
uptime_secs: None,
273+
};
274+
assert_eq!(
275+
version_mismatch_message("1.0.0", &mismatched).as_deref(),
276+
Some("client version (1.0.0) does not match server version (1.2.0)")
277+
);
278+
279+
let unknown = ServerVersionInfo::Success {
280+
address: "http://localhost".into(),
281+
version: None,
282+
git_sha: None,
283+
build_date: None,
284+
os: None,
285+
arch: None,
286+
uptime_secs: None,
287+
};
288+
assert_eq!(version_mismatch_message("1.0.0", &unknown), None);
289+
290+
let errored = ServerVersionInfo::Error {
291+
address: "http://localhost".into(),
292+
error: "oops".into(),
293+
};
294+
assert_eq!(version_mismatch_message("1.0.0", &errored), None);
295+
}
296+
}

0 commit comments

Comments
 (0)