From fa979b0ad68525a637f27be704329173f66440fb Mon Sep 17 00:00:00 2001 From: redpanda-f Date: Mon, 9 Mar 2026 09:07:47 +0000 Subject: [PATCH 1/3] feat: introduce --noterminal flag for version command --- src/cli.rs | 6 ++- src/main.rs | 2 +- src/main_app/version.rs | 100 +++++++++++++++++++++++++++------------- 3 files changed, 73 insertions(+), 35 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 993301dc..9c31dd5a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -59,7 +59,11 @@ pub enum Commands { /// Show status of the foc-devnet system Status, /// Show version information - Version, + Version { + /// Print plain output without tracing prefixes + #[arg(long)] + noterminal: bool, + }, } /// Build subcommands diff --git a/src/main.rs b/src/main.rs index 1a076fb6..b8cd02cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,7 +49,7 @@ fn main() -> Result<(), Box> { main_app::command_handlers::handle_build(build_command) } Commands::Status => main_app::command_handlers::handle_status(), - Commands::Version => main_app::version::handle_version(), + Commands::Version { noterminal } => main_app::version::handle_version(noterminal), }; // Handle the result diff --git a/src/main_app/version.rs b/src/main_app/version.rs index a0c6a576..45fa0c5b 100644 --- a/src/main_app/version.rs +++ b/src/main_app/version.rs @@ -6,8 +6,21 @@ use foc_devnet::config::{Config, Location}; use foc_devnet::version_info::VersionInfo; use tracing::info; -/// Execute the version command -pub fn handle_version() -> Result<(), Box> { +/// Emit a line either as a tracing INFO event or a plain println. +macro_rules! emit { + ($plain:expr, $fmt:literal $(, $arg:expr)*) => { + if $plain { + println!($fmt $(, $arg)*); + } else { + info!($fmt $(, $arg)*); + } + }; +} + +/// Execute the version command. +/// +/// When `noterminal` is true, output is printed without tracing prefixes. +pub fn handle_version(noterminal: bool) -> Result<(), Box> { // Version information is read-only, no poison protection needed let version_info = VersionInfo::from_env(); let dirty_suffix = if version_info.dirty.is_empty() { @@ -16,59 +29,80 @@ pub fn handle_version() -> Result<(), Box> { "-dirty" }; - info!("foc-devnet {}", version_info.version); - info!("Commit: {}{}", version_info.commit, dirty_suffix); - info!("Branch: {}", version_info.branch); - - // Calculate relative time - let now = chrono::Utc::now().timestamp(); - let diff_seconds = now - version_info.build_timestamp; + emit!(noterminal, "foc-devnet {}", version_info.version); + emit!( + noterminal, + "Commit: {}{}", + version_info.commit, + dirty_suffix + ); + emit!(noterminal, "Branch: {}", version_info.branch); - let relative_time = if diff_seconds < 60 { - format!("({} seconds ago)", diff_seconds) - } else if diff_seconds < 3600 { - format!("({} minutes ago)", diff_seconds / 60) - } else if diff_seconds < 86400 { - format!("({} hours ago)", diff_seconds / 3600) - } else { - format!("({} days ago)", diff_seconds / 86400) - }; + let relative_time = + format_relative_time(chrono::Utc::now().timestamp() - version_info.build_timestamp); - info!( + emit!( + noterminal, "Built (UTC): {} {}", - version_info.build_time_utc, relative_time + version_info.build_time_utc, + relative_time + ); + emit!( + noterminal, + "Built (Local): {}", + version_info.build_time_local ); - info!("Built (Local): {}", version_info.build_time_local); - // Print default configuration values let default_config = Config::default(); - info!(""); - print_location_info("default:code:lotus", &default_config.lotus); - print_location_info("default:code:curio", &default_config.curio); + emit!(noterminal, ""); + print_location_info(noterminal, "default:code:lotus", &default_config.lotus); + print_location_info(noterminal, "default:code:curio", &default_config.curio); print_location_info( + noterminal, "default:code:filecoin-services", &default_config.filecoin_services, ); - print_location_info("default:code:multicall3", &default_config.multicall3); - info!("default:yugabyte: {}", default_config.yugabyte_download_url); + print_location_info( + noterminal, + "default:code:multicall3", + &default_config.multicall3, + ); + emit!( + noterminal, + "default:yugabyte: {}", + default_config.yugabyte_download_url + ); Ok(()) } -/// Print location information in a formatted way -fn print_location_info(label: &str, location: &Location) { +/// Format a duration in seconds as a human-readable relative time string. +fn format_relative_time(diff_seconds: i64) -> String { + if diff_seconds < 60 { + format!("({} seconds ago)", diff_seconds) + } else if diff_seconds < 3600 { + format!("({} minutes ago)", diff_seconds / 60) + } else if diff_seconds < 86400 { + format!("({} hours ago)", diff_seconds / 3600) + } else { + format!("({} days ago)", diff_seconds / 86400) + } +} + +/// Print location information in a formatted way. +fn print_location_info(plain: bool, label: &str, location: &Location) { match location { Location::LocalSource { dir } => { - info!("{}: local source at {}", label, dir); + emit!(plain, "{}: local source at {}", label, dir); } Location::GitCommit { url, commit } => { - info!("{}: {}, commit {}", label, url, commit); + emit!(plain, "{}: {}, commit {}", label, url, commit); } Location::GitTag { url, tag } => { - info!("{}: {}, tag {}", label, url, tag); + emit!(plain, "{}: {}, tag {}", label, url, tag); } Location::GitBranch { url, branch } => { - info!("{}: {}, branch {}", label, url, branch); + emit!(plain, "{}: {}, branch {}", label, url, branch); } } } From 926568ca6db8589e002091548f8dc87bce64fdd1 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 17:48:12 +0530 Subject: [PATCH 2/3] refactor: replace magic numbers with named constants in `format_relative_time` (#86) * Initial plan * refactor: replace magic numbers with named constants in format_relative_time Co-authored-by: redpanda-f <181817029+redpanda-f@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: redpanda-f <181817029+redpanda-f@users.noreply.github.com> --- src/main_app/version.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main_app/version.rs b/src/main_app/version.rs index 45fa0c5b..12ba05a6 100644 --- a/src/main_app/version.rs +++ b/src/main_app/version.rs @@ -76,16 +76,20 @@ pub fn handle_version(noterminal: bool) -> Result<(), Box Ok(()) } +const SECS_PER_MIN: i64 = 60; +const SECS_PER_HOUR: i64 = 3_600; +const SECS_PER_DAY: i64 = 86_400; + /// Format a duration in seconds as a human-readable relative time string. fn format_relative_time(diff_seconds: i64) -> String { - if diff_seconds < 60 { + if diff_seconds < SECS_PER_MIN { format!("({} seconds ago)", diff_seconds) - } else if diff_seconds < 3600 { - format!("({} minutes ago)", diff_seconds / 60) - } else if diff_seconds < 86400 { - format!("({} hours ago)", diff_seconds / 3600) + } else if diff_seconds < SECS_PER_HOUR { + format!("({} minutes ago)", diff_seconds / SECS_PER_MIN) + } else if diff_seconds < SECS_PER_DAY { + format!("({} hours ago)", diff_seconds / SECS_PER_HOUR) } else { - format!("({} days ago)", diff_seconds / 86400) + format!("({} days ago)", diff_seconds / SECS_PER_DAY) } } From 8c79174606ec78d7df1811fdb92fe391338d3932 Mon Sep 17 00:00:00 2001 From: redpanda-f Date: Tue, 10 Mar 2026 06:03:50 +0000 Subject: [PATCH 3/3] add: notty --- src/cli.rs | 4 ++-- src/main.rs | 2 +- src/main_app/version.rs | 42 ++++++++++++++++------------------------- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 9c31dd5a..a6946b71 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -60,9 +60,9 @@ pub enum Commands { Status, /// Show version information Version { - /// Print plain output without tracing prefixes + /// Force plain output without tracing prefixes, even when stdout is a terminal #[arg(long)] - noterminal: bool, + notty: bool, }, } diff --git a/src/main.rs b/src/main.rs index b8cd02cd..f24a4188 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,7 +49,7 @@ fn main() -> Result<(), Box> { main_app::command_handlers::handle_build(build_command) } Commands::Status => main_app::command_handlers::handle_status(), - Commands::Version { noterminal } => main_app::version::handle_version(noterminal), + Commands::Version { notty } => main_app::version::handle_version(notty), }; // Handle the result diff --git a/src/main_app/version.rs b/src/main_app/version.rs index 12ba05a6..0e53c054 100644 --- a/src/main_app/version.rs +++ b/src/main_app/version.rs @@ -4,6 +4,7 @@ use foc_devnet::config::{Config, Location}; use foc_devnet::version_info::VersionInfo; +use std::io::IsTerminal; use tracing::info; /// Emit a line either as a tracing INFO event or a plain println. @@ -19,8 +20,10 @@ macro_rules! emit { /// Execute the version command. /// -/// When `noterminal` is true, output is printed without tracing prefixes. -pub fn handle_version(noterminal: bool) -> Result<(), Box> { +/// Plain output (no tracing prefixes) is used when stdout is not a terminal, +/// or when `notty` is `true` (explicit override). +pub fn handle_version(notty: bool) -> Result<(), Box> { + let plain = notty || !std::io::stdout().is_terminal(); // Version information is read-only, no poison protection needed let version_info = VersionInfo::from_env(); let dirty_suffix = if version_info.dirty.is_empty() { @@ -29,46 +32,33 @@ pub fn handle_version(noterminal: bool) -> Result<(), Box "-dirty" }; - emit!(noterminal, "foc-devnet {}", version_info.version); - emit!( - noterminal, - "Commit: {}{}", - version_info.commit, - dirty_suffix - ); - emit!(noterminal, "Branch: {}", version_info.branch); + emit!(plain, "foc-devnet {}", version_info.version); + emit!(plain, "Commit: {}{}", version_info.commit, dirty_suffix); + emit!(plain, "Branch: {}", version_info.branch); let relative_time = format_relative_time(chrono::Utc::now().timestamp() - version_info.build_timestamp); emit!( - noterminal, + plain, "Built (UTC): {} {}", version_info.build_time_utc, relative_time ); - emit!( - noterminal, - "Built (Local): {}", - version_info.build_time_local - ); + emit!(plain, "Built (Local): {}", version_info.build_time_local); let default_config = Config::default(); - emit!(noterminal, ""); - print_location_info(noterminal, "default:code:lotus", &default_config.lotus); - print_location_info(noterminal, "default:code:curio", &default_config.curio); + emit!(plain, ""); + print_location_info(plain, "default:code:lotus", &default_config.lotus); + print_location_info(plain, "default:code:curio", &default_config.curio); print_location_info( - noterminal, + plain, "default:code:filecoin-services", &default_config.filecoin_services, ); - print_location_info( - noterminal, - "default:code:multicall3", - &default_config.multicall3, - ); + print_location_info(plain, "default:code:multicall3", &default_config.multicall3); emit!( - noterminal, + plain, "default:yugabyte: {}", default_config.yugabyte_download_url );