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
6 changes: 5 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ pub enum Commands {
/// Show status of the foc-devnet system
Status,
/// Show version information
Version,
Version {
/// Force plain output without tracing prefixes, even when stdout is a terminal
#[arg(long)]
notty: bool,
},
}

/// Build subcommands
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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 { notty } => main_app::version::handle_version(notty),
};

// Handle the result
Expand Down
94 changes: 61 additions & 33 deletions src/main_app/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,26 @@

use foc_devnet::config::{Config, Location};
use foc_devnet::version_info::VersionInfo;
use std::io::IsTerminal;
use tracing::info;

/// Execute the version command
pub fn handle_version() -> Result<(), Box<dyn std::error::Error>> {
/// 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.
///
/// 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<dyn std::error::Error>> {
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() {
Expand All @@ -16,59 +32,71 @@ pub fn handle_version() -> Result<(), Box<dyn std::error::Error>> {
"-dirty"
};

info!("foc-devnet {}", version_info.version);
info!("Commit: {}{}", version_info.commit, dirty_suffix);
info!("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);

// Calculate relative time
let now = chrono::Utc::now().timestamp();
let diff_seconds = now - version_info.build_timestamp;

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!(
plain,
"Built (UTC): {} {}",
version_info.build_time_utc, relative_time
version_info.build_time_utc,
relative_time
);
info!("Built (Local): {}", version_info.build_time_local);
emit!(plain, "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!(plain, "");
print_location_info(plain, "default:code:lotus", &default_config.lotus);
print_location_info(plain, "default:code:curio", &default_config.curio);
print_location_info(
plain,
"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(plain, "default:code:multicall3", &default_config.multicall3);
emit!(
plain,
"default:yugabyte: {}",
default_config.yugabyte_download_url
);

Ok(())
}

/// Print location information in a formatted way
fn print_location_info(label: &str, location: &Location) {
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 < SECS_PER_MIN {
format!("({} seconds ago)", diff_seconds)
} 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 / SECS_PER_DAY)
}
}

/// 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);
}
}
}
Loading