From 7639624c6d47380fb905b0a3f9a75f93dc70468d Mon Sep 17 00:00:00 2001 From: Chris O'Neil Date: Wed, 18 Sep 2024 23:27:47 +0100 Subject: [PATCH 1/2] chore: standardise versioning for binaries The RFC for the release process stipulated there should be four versioning arguments: * --version * --crate-version * --package-version * --network-version Here, `--crate-version` is the semantic version of the crate, `--package-version` is the release cycle version, e.g., `2024.09.1.1`, and `--network-version` prints the compatible network protocol. The `--version` argument will then print all of this information. The `--package-version` argument does not apply to the nightly release. The approach for printing the version information is to provide our own `version` flag, by using the `disable_version_flag` attribute with `clap`. If you want to use `clap`, all the information needs to be completely static and available at compile time. This is convoluted, especially for things like the network protocol version and the crate version, and it would have led to a lot of repetition. We can avoid the difficulty by providing the information dynamically, and we can use the `sn_build_info` crate to define the version string once. On binaries that used subcommands, for the versioning to work correctly, the subcommand needs to be made optional. The `get_bin_version` helper used by the node manager was updated parse the new versioning information from both stable and nightly releases. Now, the function can handle different `--version` output formats by extracting the version number for stable releases prefixed with a 'v' and the date for nightly releases. Since we are going to introduce a nightly build, this commit also introduces a nightly feature, which will control what versioning information is used. For the nightly, the binaries will be versioned with the date on which they are built. The `--package-version` argument does not apply to the nightly release; it is not necessary if all the binaries have the same version, which is the date. --- Cargo.lock | 10 +++ nat-detection/Cargo.toml | 5 ++ nat-detection/src/main.rs | 39 +++++++++- node-launchpad/Cargo.toml | 5 ++ node-launchpad/src/bin/tui/main.rs | 40 +++++++++- node-launchpad/src/utils.rs | 24 ------ sn_auditor/Cargo.toml | 3 + sn_auditor/src/main.rs | 50 ++++++++++++- sn_build_info/Cargo.toml | 8 ++ sn_build_info/build.rs | 44 +++++++++++ sn_build_info/src/lib.rs | 81 +++++++++++++++++++- sn_cli/Cargo.toml | 1 + sn_cli/src/bin/main.rs | 52 ++++++++++--- sn_cli/src/bin/subcommands/mod.rs | 21 +++++- sn_faucet/Cargo.toml | 1 + sn_faucet/src/main.rs | 99 +++++++++++++++++------- sn_node/Cargo.toml | 7 +- sn_node/src/bin/safenode/main.rs | 61 +++++++++++++-- sn_node_manager/Cargo.toml | 2 + sn_node_manager/src/bin/cli/main.rs | 100 +++++++++++++++++-------- sn_node_manager/src/bin/daemon/main.rs | 45 +++++++++-- sn_node_manager/src/helpers.rs | 33 +++++--- sn_node_manager/src/local.rs | 9 ++- sn_node_rpc_client/Cargo.toml | 4 + sn_node_rpc_client/src/main.rs | 42 +++++++++-- 25 files changed, 649 insertions(+), 137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f296410140..1b7d8dab75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4621,7 +4621,9 @@ dependencies = [ "color-eyre", "futures", "libp2p", + "sn_build_info", "sn_networking", + "sn_protocol", "tokio", "tracing", "tracing-log 0.2.0", @@ -4758,7 +4760,9 @@ dependencies = [ "signal-hook", "sn-node-manager", "sn-releases", + "sn_build_info", "sn_peers_acquisition", + "sn_protocol", "sn_service_management", "strip-ansi-escapes", "strum", @@ -6986,6 +6990,7 @@ dependencies = [ "serde_json", "service-manager", "sn-releases", + "sn_build_info", "sn_logging", "sn_peers_acquisition", "sn_protocol", @@ -7034,9 +7039,11 @@ dependencies = [ "lazy_static", "serde", "serde_json", + "sn_build_info", "sn_client", "sn_logging", "sn_peers_acquisition", + "sn_protocol", "tiny_http", "tokio", "tracing", @@ -7059,6 +7066,8 @@ dependencies = [ name = "sn_build_info" version = "0.1.13" dependencies = [ + "chrono", + "tracing", "vergen", ] @@ -7368,6 +7377,7 @@ dependencies = [ "hex 0.4.3", "libp2p", "libp2p-identity", + "sn_build_info", "sn_client", "sn_logging", "sn_node", diff --git a/nat-detection/Cargo.toml b/nat-detection/Cargo.toml index 21e67b55e8..47f925dd70 100644 --- a/nat-detection/Cargo.toml +++ b/nat-detection/Cargo.toml @@ -13,6 +13,9 @@ version = "0.2.5" name = "nat-detection" path = "src/main.rs" +[features] +nightly = [] + [dependencies] clap = { version = "4.5.4", features = ["derive"] } clap-verbosity-flag = "2.2.0" @@ -28,7 +31,9 @@ libp2p = { version = "0.54.1", features = [ "macros", "upnp", ] } +sn_build_info = { path = "../sn_build_info", version = "0.1.13" } sn_networking = { path = "../sn_networking", version = "0.18.2" } +sn_protocol = { path = "../sn_protocol", version = "0.17.9" } tokio = { version = "1.32.0", features = ["full"] } tracing = { version = "~0.1.26" } tracing-log = "0.2.0" diff --git a/nat-detection/src/main.rs b/nat-detection/src/main.rs index 645b181266..fccbe3ea4c 100644 --- a/nat-detection/src/main.rs +++ b/nat-detection/src/main.rs @@ -35,7 +35,7 @@ const RETRY_INTERVAL: Duration = Duration::from_secs(10); /// - 11: Public under UPnP /// - 12: Private or Unknown NAT #[derive(Debug, Parser)] -#[clap(version, author, verbatim_doc_comment)] +#[clap(disable_version_flag = true)] struct Opt { /// Port to listen on. /// @@ -60,15 +60,50 @@ struct Opt { #[command(flatten)] verbose: clap_verbosity_flag::Verbosity, + + /// Print the crate version + #[clap(long)] + crate_version: bool, + + /// Print the package version + #[clap(long)] + #[cfg(not(feature = "nightly"))] + package_version: bool, + + /// Print version information. + #[clap(long)] + version: bool, } #[tokio::main] async fn main() -> Result<()> { color_eyre::install()?; - // Process command line arguments. let opt = Opt::parse(); + if opt.version { + println!( + "{}", + sn_build_info::version_string( + "Autonomi NAT Detection", + env!("CARGO_PKG_VERSION"), + None + ) + ); + return Ok(()); + } + + if opt.crate_version { + println!("Crate version: {}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if opt.package_version { + println!("Package version: {}", sn_build_info::package_version()); + return Ok(()); + } + let registry = tracing_subscriber::registry().with(tracing_subscriber::fmt::layer()); // Use `RUST_LOG` if set, else use the verbosity flag (where `-vvvv` is trace level). let _ = if std::env::var_os("RUST_LOG").is_some() { diff --git a/node-launchpad/Cargo.toml b/node-launchpad/Cargo.toml index 7f2f493f51..f1b006bd67 100644 --- a/node-launchpad/Cargo.toml +++ b/node-launchpad/Cargo.toml @@ -14,6 +14,9 @@ build = "build.rs" name = "node-launchpad" path = "src/bin/tui/main.rs" +[features] +nightly = [] + [dependencies] atty = "0.2.14" better-panic = "0.3.0" @@ -48,8 +51,10 @@ reqwest = { version = "0.12.2", default-features = false, features = [ serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" signal-hook = "0.3.17" +sn_build_info = { path = "../sn_build_info", version = "0.1.13" } sn-node-manager = { version = "0.10.4", path = "../sn_node_manager" } sn_peers_acquisition = { version = "0.5.1", path = "../sn_peers_acquisition" } +sn_protocol = { path = "../sn_protocol", version = "0.17.9" } sn-releases = "~0.2.6" sn_service_management = { version = "0.3.12", path = "../sn_service_management" } strip-ansi-escapes = "0.2.0" diff --git a/node-launchpad/src/bin/tui/main.rs b/node-launchpad/src/bin/tui/main.rs index 2ceb235900..d3074018af 100644 --- a/node-launchpad/src/bin/tui/main.rs +++ b/node-launchpad/src/bin/tui/main.rs @@ -16,7 +16,7 @@ use color_eyre::eyre::Result; use node_launchpad::{ app::App, config::configure_winsw, - utils::{initialize_logging, initialize_panic_handler, version}, + utils::{initialize_logging, initialize_panic_handler}, }; #[cfg(target_os = "windows")] use sn_node_manager::config::is_running_as_root; @@ -25,7 +25,7 @@ use std::{env, path::PathBuf}; use tokio::task::LocalSet; #[derive(Parser, Debug)] -#[command(author, version = version(), about)] +#[command(disable_version_flag = true)] pub struct Cli { #[arg( short, @@ -53,12 +53,48 @@ pub struct Cli { #[command(flatten)] pub(crate) peers: PeersArgs, + + /// Print the crate version. + #[clap(long)] + crate_version: bool, + + /// Print the package version. + #[clap(long)] + #[cfg(not(feature = "nightly"))] + package_version: bool, + + /// Print the version. + #[clap(long)] + version: bool, } async fn tokio_main() -> Result<()> { initialize_panic_handler()?; let args = Cli::parse(); + if args.version { + println!( + "{}", + sn_build_info::version_string( + "Autonomi Node Launchpad", + env!("CARGO_PKG_VERSION"), + None + ) + ); + return Ok(()); + } + + if args.crate_version { + println!("{}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if args.package_version { + println!("{}", sn_build_info::package_version()); + return Ok(()); + } + info!("Starting app with args: {args:?}"); let mut app = App::new( args.tick_rate, diff --git a/node-launchpad/src/utils.rs b/node-launchpad/src/utils.rs index ffb997246c..02b6b72fa1 100644 --- a/node-launchpad/src/utils.rs +++ b/node-launchpad/src/utils.rs @@ -14,15 +14,6 @@ use tracing_subscriber::{ self, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer, }; -const VERSION_MESSAGE: &str = concat!( - env!("CARGO_PKG_VERSION"), - "-", - env!("VERGEN_GIT_DESCRIBE"), - " (", - env!("VERGEN_BUILD_DATE"), - ")" -); - pub fn initialize_panic_handler() -> Result<()> { let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default() .panic_section(format!( @@ -132,18 +123,3 @@ macro_rules! trace_dbg { trace_dbg!(level: tracing::Level::DEBUG, $ex) }; } - -pub fn version() -> String { - let author = clap::crate_authors!(); - - let data_dir_path = get_launchpad_data_dir_path().unwrap().display().to_string(); - - format!( - "\ -{VERSION_MESSAGE} - -Authors: {author} - -Data directory: {data_dir_path}" - ) -} diff --git a/sn_auditor/Cargo.toml b/sn_auditor/Cargo.toml index cf78b8877d..f396596218 100644 --- a/sn_auditor/Cargo.toml +++ b/sn_auditor/Cargo.toml @@ -16,6 +16,7 @@ local-discovery = [ "sn_peers_acquisition/local-discovery", ] network-contacts = ["sn_peers_acquisition/network-contacts"] +nightly = [] open-metrics = ["sn_client/open-metrics"] websockets = ["sn_client/websockets"] svg-dag = ["graphviz-rust", "dag-collection"] @@ -31,9 +32,11 @@ graphviz-rust = { version = "0.9.0", optional = true } lazy_static = "1.4.0" serde = { version = "1.0.133", features = ["derive", "rc"] } serde_json = "1.0.108" +sn_build_info = { path = "../sn_build_info", version = "0.1.13" } sn_client = { path = "../sn_client", version = "0.110.1" } sn_logging = { path = "../sn_logging", version = "0.2.34" } sn_peers_acquisition = { path = "../sn_peers_acquisition", version = "0.5.1" } +sn_protocol = { path = "../sn_protocol", version = "0.17.9" } tiny_http = { version = "0.12", features = ["ssl-rustls"] } tracing = { version = "~0.1.26" } tokio = { version = "1.32.0", features = [ diff --git a/sn_auditor/src/main.rs b/sn_auditor/src/main.rs index 2faf8551e1..1cbdaf2f58 100644 --- a/sn_auditor/src/main.rs +++ b/sn_auditor/src/main.rs @@ -19,6 +19,7 @@ use dag_db::SpendDagDb; use sn_client::Client; use sn_logging::{Level, LogBuilder, LogFormat, LogOutputDest}; use sn_peers_acquisition::PeersArgs; +use sn_protocol::version::IDENTIFY_PROTOCOL_STR; use std::collections::BTreeSet; use std::path::PathBuf; use tiny_http::{Response, Server}; @@ -27,7 +28,7 @@ use tiny_http::{Response, Server}; const BETA_REWARDS_BACKUP_INTERVAL_SECS: u64 = 20 * 60; #[derive(Parser)] -#[command(author, version, about, long_about = None)] +#[command(disable_version_flag = true)] struct Opt { #[command(flatten)] peers: PeersArgs, @@ -70,14 +71,59 @@ struct Opt { /// discord usernames of the beta participants #[clap(short = 'k', long, value_name = "hex_secret_key")] beta_encryption_key: Option, + + /// Print the crate version. + #[clap(long)] + pub crate_version: bool, + + /// Print the network protocol version. + #[clap(long)] + pub protocol_version: bool, + + /// Print the package version. + #[cfg(not(feature = "nightly"))] + #[clap(long)] + pub package_version: bool, + + /// Print version information. + #[clap(long)] + version: bool, } #[tokio::main] async fn main() -> Result<()> { let opt = Opt::parse(); + + if opt.version { + println!( + "{}", + sn_build_info::version_string( + "Autonomi Auditor", + env!("CARGO_PKG_VERSION"), + Some(&IDENTIFY_PROTOCOL_STR) + ) + ); + return Ok(()); + } + + if opt.crate_version { + println!("{}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if opt.package_version { + println!("{}", sn_build_info::package_version()); + return Ok(()); + } + + if opt.protocol_version { + println!("{}", *IDENTIFY_PROTOCOL_STR); + return Ok(()); + } + let log_builder = logging_init(opt.log_output_dest, opt.log_format)?; let _log_handles = log_builder.initialize()?; - let beta_participants = load_and_update_beta_participants(opt.beta_participants)?; let maybe_sk = if let Some(sk_str) = opt.beta_encryption_key { diff --git a/sn_build_info/Cargo.toml b/sn_build_info/Cargo.toml index a14eed90dd..51142a3f5d 100644 --- a/sn_build_info/Cargo.toml +++ b/sn_build_info/Cargo.toml @@ -9,9 +9,17 @@ name = "sn_build_info" readme = "README.md" repository = "https://github.com/maidsafe/safe_network" version = "0.1.13" +build = "build.rs" [build-dependencies] vergen = { version = "8.0.0", features = ["build", "git", "gitcl"] } +[features] +nightly = [] + [lints] workspace = true + +[dependencies] +chrono = "0.4" +tracing = { version = "~0.1.26" } diff --git a/sn_build_info/build.rs b/sn_build_info/build.rs index 392c55da4e..7ca807729d 100644 --- a/sn_build_info/build.rs +++ b/sn_build_info/build.rs @@ -5,6 +5,8 @@ // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. +use std::fs; +use std::path::Path; use vergen::EmitBuilder; fn main() -> Result<(), Box> { @@ -18,5 +20,47 @@ fn main() -> Result<(), Box> { .git_describe(true, false, None) .emit()?; + let release_info_path = Path::new("../release-cycle-info"); + let contents = + fs::read_to_string(release_info_path).expect("Failed to read release-cycle-info"); + + let mut year = String::new(); + let mut month = String::new(); + let mut cycle = String::new(); + let mut counter = String::new(); + + for line in contents.lines() { + if line.starts_with("release-year:") { + year = line + .split(':') + .nth(1) + .map(|s| s.trim().to_string()) + .unwrap_or_default(); + } else if line.starts_with("release-month:") { + month = line + .split(':') + .nth(1) + .map(|s| s.trim().to_string()) + .unwrap_or_default(); + } else if line.starts_with("release-cycle:") { + cycle = line + .split(':') + .nth(1) + .map(|s| s.trim().to_string()) + .unwrap_or_default(); + } else if line.starts_with("release-cycle-counter:") { + counter = line + .split(':') + .nth(1) + .map(|s| s.trim().to_string()) + .unwrap_or_default(); + } + } + + println!("cargo:rustc-env=RELEASE_YEAR={year}"); + println!("cargo:rustc-env=RELEASE_MONTH={month}"); + println!("cargo:rustc-env=RELEASE_CYCLE={cycle}"); + println!("cargo:rustc-env=RELEASE_CYCLE_COUNTER={counter}"); + Ok(()) } diff --git a/sn_build_info/src/lib.rs b/sn_build_info/src/lib.rs index 6b858254ac..1e270f2a73 100644 --- a/sn_build_info/src/lib.rs +++ b/sn_build_info/src/lib.rs @@ -6,14 +6,15 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. +use chrono::Utc; +use tracing::debug; + /// Git information separated by slashes: ` / / ` pub const fn git_info() -> &'static str { concat!( - env!("VERGEN_GIT_SHA"), - " / ", env!("VERGEN_GIT_BRANCH"), " / ", - env!("VERGEN_GIT_DESCRIBE"), + env!("VERGEN_GIT_SHA"), " / ", env!("VERGEN_BUILD_DATE") ) @@ -33,3 +34,77 @@ pub const fn git_branch() -> &'static str { pub const fn git_sha() -> &'static str { env!("VERGEN_GIT_SHA") } + +/// Nightly version format: YYYY.MM.DD +pub fn nightly_version() -> String { + let now = Utc::now(); + now.format("%Y.%m.%d").to_string() +} + +/// Git information for nightly builds: ` / / ` +pub fn nightly_git_info() -> String { + format!("{} / {} / {}", nightly_version(), git_branch(), git_sha(),) +} + +pub fn package_version() -> String { + format!( + "{}.{}.{}.{}", + env!("RELEASE_YEAR"), + env!("RELEASE_MONTH"), + env!("RELEASE_CYCLE"), + env!("RELEASE_CYCLE_COUNTER") + ) +} + +pub fn full_version_info( + app_name: &str, + crate_version: &str, + protocol_version: Option<&str>, +) -> String { + let mut info = format!("{app_name} v{crate_version}"); + + if let Some(version) = protocol_version { + info.push_str(&format!("\nNetwork version: {version}")); + } + + info.push_str(&format!( + "\nPackage version: {}\nGit info: {}", + package_version(), + git_info() + )); + + info +} + +pub fn full_nightly_version_info(app_name: &str, protocol_version: Option<&str>) -> String { + let mut info = format!("{app_name} -- Nightly Release {}", nightly_version(),); + if let Some(version) = protocol_version { + info.push_str(&format!("\nNetwork version: {version}")); + } + info.push_str(&format!("\nGit info: {} / {}", git_branch(), git_sha(),)); + info +} + +pub fn version_string( + app_name: &str, + crate_version: &str, + protocol_version: Option<&str>, +) -> String { + if cfg!(feature = "nightly") { + full_nightly_version_info(app_name, protocol_version) + } else { + full_version_info(app_name, crate_version, protocol_version) + } +} + +pub fn log_version_info(crate_version: &str, protocol_version: &str) { + if cfg!(feature = "nightly") { + debug!("nightly build info: {}", nightly_git_info()); + debug!("network version: {protocol_version}"); + } else { + debug!("version: {crate_version}"); + debug!("network version: {protocol_version}"); + debug!("package version: {}", package_version()); + debug!("git info: {}", git_info()); + } +} diff --git a/sn_cli/Cargo.toml b/sn_cli/Cargo.toml index acdaccc9c8..66d29270c8 100644 --- a/sn_cli/Cargo.toml +++ b/sn_cli/Cargo.toml @@ -27,6 +27,7 @@ local-discovery = [ ] metrics = ["sn_logging/process-metrics"] network-contacts = ["sn_peers_acquisition/network-contacts"] +nightly = [] open-metrics = ["sn_client/open-metrics"] [dependencies] diff --git a/sn_cli/src/bin/main.rs b/sn_cli/src/bin/main.rs index 0ac03d458b..d4c8cac1d0 100644 --- a/sn_cli/src/bin/main.rs +++ b/sn_cli/src/bin/main.rs @@ -30,6 +30,7 @@ use sn_client::transfers::bls_secret_from_hex; use sn_client::{Client, ClientEvent, ClientEventsBroadcaster, ClientEventsReceiver}; #[cfg(feature = "metrics")] use sn_logging::{metrics::init_metrics, Level, LogBuilder, LogFormat}; +use sn_protocol::version::IDENTIFY_PROTOCOL_STR; use std::{io, path::PathBuf, time::Duration}; use tokio::{sync::broadcast::error::RecvError, task::JoinHandle}; @@ -39,6 +40,35 @@ const CLIENT_KEY: &str = "clientkey"; async fn main() -> Result<()> { color_eyre::install()?; let opt = Opt::parse(); + + if opt.version { + println!( + "{}", + sn_build_info::version_string( + "Autonomi CLI", + env!("CARGO_PKG_VERSION"), + Some(&IDENTIFY_PROTOCOL_STR) + ) + ); + return Ok(()); + } + + if opt.crate_version { + println!("{}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + if opt.protocol_version { + println!("{}", *IDENTIFY_PROTOCOL_STR); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if opt.package_version { + println!("{}", sn_build_info::package_version()); + return Ok(()); + } + let logging_targets = vec![ // TODO: Reset to nice and clean defaults once we have a better idea of what we want ("sn_networking".to_string(), Level::INFO), @@ -74,7 +104,7 @@ async fn main() -> Result<()> { let client_data_dir_path = get_client_data_dir_path()?; // Perform actions that do not require us connecting to the network and return early - if let SubCmd::Wallet(cmds) = &opt.cmd { + if let Some(SubCmd::Wallet(cmds)) = &opt.cmd { if let WalletCmds::Address { .. } | WalletCmds::Balance { .. } | WalletCmds::Create { .. } @@ -87,7 +117,7 @@ async fn main() -> Result<()> { } } - if let SubCmd::WatchOnlyWallet(cmds) = &opt.cmd { + if let Some(SubCmd::WatchOnlyWallet(cmds)) = &opt.cmd { if let WatchOnlyWalletCmds::Addresses | WatchOnlyWalletCmds::Balance { .. } | WatchOnlyWalletCmds::Deposit { .. } @@ -138,30 +168,32 @@ async fn main() -> Result<()> { }; progress_bar_handler.await?; - // default to verifying storage let should_verify_store = !opt.no_verify; // PowerShell seems having issue to showing the unwrapped error // Hence capture the result and print it out explicity. - let cmd_str = format!("{:?}", opt.cmd); let result = match opt.cmd { - SubCmd::Wallet(cmds) => { + Some(SubCmd::Wallet(cmds)) => { wallet_cmds(cmds, &client, &client_data_dir_path, should_verify_store).await } - SubCmd::WatchOnlyWallet(cmds) => { + Some(SubCmd::WatchOnlyWallet(cmds)) => { wo_wallet_cmds(cmds, &client, &client_data_dir_path, should_verify_store).await } - SubCmd::Files(cmds) => { + Some(SubCmd::Files(cmds)) => { files_cmds(cmds, &client, &client_data_dir_path, should_verify_store).await } - SubCmd::Folders(cmds) => { + Some(SubCmd::Folders(cmds)) => { folders_cmds(cmds, &client, &client_data_dir_path, should_verify_store).await } - SubCmd::Register(cmds) => { + Some(SubCmd::Register(cmds)) => { register_cmds(cmds, &client, &client_data_dir_path, should_verify_store).await } + None => { + println!("Use --help to see available commands"); + return Ok(()); + } }; - println!("Completed with {result:?} of execute {cmd_str:?}"); + println!("Completed with {result:?}"); Ok(()) } diff --git a/sn_cli/src/bin/subcommands/mod.rs b/sn_cli/src/bin/subcommands/mod.rs index 7a7ba11cad..575e90b3d3 100644 --- a/sn_cli/src/bin/subcommands/mod.rs +++ b/sn_cli/src/bin/subcommands/mod.rs @@ -21,7 +21,7 @@ use std::time::Duration; // Please do not remove the blank lines in these doc comments. // They are used for inserting line breaks when the help menu is rendered in the UI. #[derive(Parser)] -#[command(author, version, about, long_about = None)] +#[command(disable_version_flag = true)] pub(crate) struct Opt { /// Specify the logging output destination. /// @@ -49,7 +49,7 @@ pub(crate) struct Opt { /// Available sub commands. #[clap(subcommand)] - pub cmd: SubCmd, + pub cmd: Option, /// The maximum duration to wait for a connection to the network before timing out. #[clap(long = "timeout", global = true, value_parser = |t: &str| -> Result { Ok(t.parse().map(Duration::from_secs)?) })] @@ -60,6 +60,23 @@ pub(crate) struct Opt { /// This may increase operation speed, but offers no guarantees that operations were successful. #[clap(global = true, long = "no-verify", short = 'x')] pub no_verify: bool, + + /// Print the crate version. + #[clap(long)] + pub crate_version: bool, + + /// Print the network protocol version. + #[clap(long)] + pub protocol_version: bool, + + /// Print the package version. + #[clap(long)] + #[cfg(not(feature = "nightly"))] + pub package_version: bool, + + /// Print version information. + #[clap(long)] + pub version: bool, } #[derive(Subcommand, Debug)] diff --git a/sn_faucet/Cargo.toml b/sn_faucet/Cargo.toml index 3a203b3d8d..a8348c2aac 100644 --- a/sn_faucet/Cargo.toml +++ b/sn_faucet/Cargo.toml @@ -15,6 +15,7 @@ default = ["gifting"] distribution = ["base64", "bitcoin", "minreq"] gifting = [] initial-data = ["reqwest", "futures"] +nightly = [] [[bin]] path = "src/main.rs" diff --git a/sn_faucet/src/main.rs b/sn_faucet/src/main.rs index 833178a8f9..e01aecf426 100644 --- a/sn_faucet/src/main.rs +++ b/sn_faucet/src/main.rs @@ -22,15 +22,44 @@ use sn_client::{ }; use sn_logging::{Level, LogBuilder, LogOutputDest}; use sn_peers_acquisition::PeersArgs; +use sn_protocol::version::IDENTIFY_PROTOCOL_STR; use sn_transfers::{get_faucet_data_dir, HotWallet, MainPubkey, NanoTokens, Transfer}; use std::{path::PathBuf, time::Duration}; use tokio::{sync::broadcast::error::RecvError, task::JoinHandle}; -use tracing::{debug, error, info}; +use tracing::{error, info}; #[tokio::main] async fn main() -> Result<()> { let opt = Opt::parse(); + if opt.version { + println!( + "{}", + sn_build_info::version_string( + "Autonomi Test Faucet", + env!("CARGO_PKG_VERSION"), + Some(&IDENTIFY_PROTOCOL_STR.to_string()) + ) + ); + return Ok(()); + } + + if opt.crate_version { + println!("Crate version: {}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + if opt.protocol_version { + println!("Network version: {}", *IDENTIFY_PROTOCOL_STR); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if opt.package_version { + println!("Package version: {}", sn_build_info::package_version()); + return Ok(()); + } + let bootstrap_peers = opt.peers.get_peers().await?; let bootstrap_peers = if bootstrap_peers.is_empty() { // empty vec is returned if `local-discovery` flag is provided @@ -57,14 +86,8 @@ async fn main() -> Result<()> { log_builder.output_dest(opt.log_output_dest); let _log_handles = log_builder.initialize()?; - debug!( - "faucet built with git version: {}", - sn_build_info::git_info() - ); - println!( - "faucet built with git version: {}", - sn_build_info::git_info() - ); + sn_build_info::log_version_info(env!("CARGO_PKG_VERSION"), &IDENTIFY_PROTOCOL_STR); + info!("Instantiating a SAFE Test Faucet..."); let secret_key = bls::SecretKey::random(); @@ -147,7 +170,7 @@ fn spawn_connection_progress_bar(mut rx: ClientEventsReceiver) -> (ProgressBar, } #[derive(Parser)] -#[command(author, version, about, long_about = None)] +#[command(disable_version_flag = true)] struct Opt { /// Specify the logging output destination. /// @@ -167,7 +190,24 @@ struct Opt { /// Available sub commands. #[clap(subcommand)] - pub cmd: SubCmd, + pub cmd: Option, + + /// Print the crate version + #[clap(long)] + crate_version: bool, + + /// Print the protocol version + #[clap(long)] + protocol_version: bool, + + /// Print the package version + #[cfg(not(feature = "nightly"))] + #[clap(long)] + package_version: bool, + + /// Print version information. + #[clap(long)] + version: bool, } #[derive(Subcommand, Debug, Clone)] @@ -198,22 +238,29 @@ enum SubCmd { RestartServer, } -async fn faucet_cmds(cmds: SubCmd, client: &Client, funded_wallet: HotWallet) -> Result<()> { - match cmds { - SubCmd::ClaimGenesis => { - claim_genesis(client, funded_wallet).await?; - } - SubCmd::Send { amount, to } => { - send_tokens(client, funded_wallet, &amount, &to).await?; - } - SubCmd::Server => { - // shouldn't return except on error - run_faucet_server(client).await?; - } - SubCmd::RestartServer => { - // shouldn't return except on error - restart_faucet_server(client).await?; +async fn faucet_cmds( + cmds: Option, + client: &Client, + funded_wallet: HotWallet, +) -> Result<()> { + if let Some(cmds) = cmds { + match cmds { + SubCmd::ClaimGenesis => { + claim_genesis(client, funded_wallet).await?; + } + SubCmd::Send { amount, to } => { + send_tokens(client, funded_wallet, &amount, &to).await?; + } + SubCmd::Server => { + run_faucet_server(client).await?; + } + SubCmd::RestartServer => { + restart_faucet_server(client).await?; + } } + } else { + // Handle the case when no subcommand is provided + println!("No subcommand provided. Use --help for more information."); } Ok(()) } diff --git a/sn_node/Cargo.toml b/sn_node/Cargo.toml index b5bf576e7c..99c6d3f273 100644 --- a/sn_node/Cargo.toml +++ b/sn_node/Cargo.toml @@ -15,14 +15,15 @@ path = "src/bin/safenode/main.rs" [features] default = ["metrics", "upnp", "reward-forward", "open-metrics"] +encrypt-records = ["sn_networking/encrypt-records"] local-discovery = ["sn_networking/local-discovery"] -otlp = ["sn_logging/otlp"] metrics = ["sn_logging/process-metrics"] network-contacts = ["sn_peers_acquisition/network-contacts"] +nightly = [] open-metrics = ["sn_networking/open-metrics", "prometheus-client"] -encrypt-records = ["sn_networking/encrypt-records"] -upnp = ["sn_networking/upnp"] +otlp = ["sn_logging/otlp"] reward-forward = ["sn_transfers/reward-forward"] +upnp = ["sn_networking/upnp"] [dependencies] assert_fs = "1.0.0" diff --git a/sn_node/src/bin/safenode/main.rs b/sn_node/src/bin/safenode/main.rs index cf30e04c65..c503504528 100644 --- a/sn_node/src/bin/safenode/main.rs +++ b/sn_node/src/bin/safenode/main.rs @@ -11,15 +11,17 @@ extern crate tracing; mod rpc_service; -use clap::Parser; -use eyre::{eyre, Result}; +use clap::{command, Parser}; +use color_eyre::{eyre::eyre, Result}; use libp2p::{identity::Keypair, PeerId}; #[cfg(feature = "metrics")] use sn_logging::metrics::init_metrics; use sn_logging::{Level, LogFormat, LogOutputDest, ReloadHandle}; use sn_node::{Marker, NodeBuilder, NodeEvent, NodeEventsReceiver}; use sn_peers_acquisition::PeersArgs; -use sn_protocol::{node::get_safenode_root_dir, node_rpc::NodeCtrl}; +use sn_protocol::{ + node::get_safenode_root_dir, node_rpc::NodeCtrl, version::IDENTIFY_PROTOCOL_STR, +}; use std::{ env, io::Write, @@ -65,7 +67,7 @@ pub fn parse_log_output(val: &str) -> Result { // Please do not remove the blank lines in these doc comments. // They are used for inserting line breaks when the help menu is rendered in the UI. #[derive(Parser, Debug)] -#[clap(name = "safenode cli", version = env!("CARGO_PKG_VERSION"))] +#[command(disable_version_flag = true)] struct Opt { /// Specify whether the node is operating from a home network and situated behind a NAT without port forwarding /// capabilities. Setting this to true, activates hole-punching to facilitate direct connections from other nodes. @@ -177,12 +179,57 @@ struct Opt { required_if_eq("metrics_server_port", "0") )] enable_metrics_server: bool, + + /// Print the crate version. + #[clap(long)] + crate_version: bool, + + /// Print the network protocol version. + #[clap(long)] + protocol_version: bool, + + /// Print the package version. + #[cfg(not(feature = "nightly"))] + #[clap(long)] + package_version: bool, + + /// Print version information. + #[clap(long)] + version: bool, } fn main() -> Result<()> { color_eyre::install()?; let opt = Opt::parse(); + if opt.version { + println!( + "{}", + sn_build_info::version_string( + "Autonomi Node", + env!("CARGO_PKG_VERSION"), + Some(&IDENTIFY_PROTOCOL_STR) + ) + ); + return Ok(()); + } + + if opt.crate_version { + println!("Crate version: {}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + if opt.protocol_version { + println!("Network version: {}", *IDENTIFY_PROTOCOL_STR); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if opt.package_version { + println!("Package version: {}", sn_build_info::package_version()); + return Ok(()); + } + let node_socket_addr = SocketAddr::new(opt.ip, opt.port); let (root_dir, keypair) = get_root_dir_and_keypair(&opt.root_dir)?; @@ -197,10 +244,8 @@ fn main() -> Result<()> { env!("CARGO_PKG_VERSION") ); info!("\n{}\n{}", msg, "=".repeat(msg.len())); - debug!( - "safenode built with git version: {}", - sn_build_info::git_info() - ); + + sn_build_info::log_version_info(env!("CARGO_PKG_VERSION"), &IDENTIFY_PROTOCOL_STR); info!("Node started with initial_peers {bootstrap_peers:?}"); diff --git a/sn_node_manager/Cargo.toml b/sn_node_manager/Cargo.toml index 3e415a0d9c..6dfd50bd04 100644 --- a/sn_node_manager/Cargo.toml +++ b/sn_node_manager/Cargo.toml @@ -22,6 +22,7 @@ chaos = [] default = ["quic"] local-discovery = [] network-contacts = [] +nightly = [] open-metrics = [] otlp = [] quic = [] @@ -44,6 +45,7 @@ semver = "1.0.20" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" service-manager = "0.7.0" +sn_build_info = { path = "../sn_build_info", version = "0.1.13" } sn_logging = { path = "../sn_logging", version = "0.2.34" } sn_peers_acquisition = { path = "../sn_peers_acquisition", version = "0.5.1" } sn_protocol = { path = "../sn_protocol", version = "0.17.9" } diff --git a/sn_node_manager/src/bin/cli/main.rs b/sn_node_manager/src/bin/cli/main.rs index 7e89275279..b827e3f6a4 100644 --- a/sn_node_manager/src/bin/cli/main.rs +++ b/sn_node_manager/src/bin/cli/main.rs @@ -22,22 +22,35 @@ use tracing::Level; const DEFAULT_NODE_COUNT: u16 = 25; #[derive(Parser)] -#[command(author, version, about, long_about = None)] +#[command(disable_version_flag = true)] pub(crate) struct Cmd { /// Available sub commands. #[clap(subcommand)] - pub cmd: SubCmd, + pub cmd: Option, - #[clap(short, long, action = clap::ArgAction::Count, default_value_t = 2)] - verbose: u8, + /// Print the crate version. + #[clap(long)] + pub crate_version: bool, /// Output debug-level logging to stderr. #[clap(long, conflicts_with = "trace")] debug: bool, + /// Print the package version. + #[cfg(not(feature = "nightly"))] + #[clap(long)] + pub package_version: bool, + /// Output trace-level logging to stderr. #[clap(long, conflicts_with = "debug")] trace: bool, + + #[clap(short, long, action = clap::ArgAction::Count, default_value_t = 2)] + verbose: u8, + + /// Print version information. + #[clap(long)] + version: bool, } #[derive(Subcommand, Debug)] @@ -997,6 +1010,26 @@ pub enum LocalSubCmd { async fn main() -> Result<()> { color_eyre::install()?; let args = Cmd::parse(); + + if args.version { + println!( + "{}", + sn_build_info::version_string("Autonomi Node Manager", env!("CARGO_PKG_VERSION"), None) + ); + return Ok(()); + } + + if args.crate_version { + println!("{}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if args.package_version { + println!("{}", sn_build_info::package_version()); + return Ok(()); + } + let verbosity = VerbosityLevel::from(args.verbose); let _log_handle = if args.debug || args.trace { @@ -1015,7 +1048,7 @@ async fn main() -> Result<()> { tracing::info!("Executing cmd: {:?}", args.cmd); match args.cmd { - SubCmd::Add { + Some(SubCmd::Add { auto_restart, auto_set_nat_flags, count, @@ -1038,7 +1071,7 @@ async fn main() -> Result<()> { upnp, user, version, - } => { + }) => { let _ = cmd::node::add( auto_restart, auto_set_nat_flags, @@ -1067,7 +1100,7 @@ async fn main() -> Result<()> { .await?; Ok(()) } - SubCmd::Auditor(AuditorSubCmd::Add { + Some(SubCmd::Auditor(AuditorSubCmd::Add { beta_encryption_key, env_variables, log_dir_path, @@ -1075,7 +1108,7 @@ async fn main() -> Result<()> { peers, url, version, - }) => { + })) => { cmd::auditor::add( beta_encryption_key, env_variables, @@ -1088,32 +1121,32 @@ async fn main() -> Result<()> { ) .await } - SubCmd::Auditor(AuditorSubCmd::Start {}) => cmd::auditor::start(verbosity).await, - SubCmd::Auditor(AuditorSubCmd::Stop {}) => cmd::auditor::stop(verbosity).await, - SubCmd::Auditor(AuditorSubCmd::Upgrade { + Some(SubCmd::Auditor(AuditorSubCmd::Start {})) => cmd::auditor::start(verbosity).await, + Some(SubCmd::Auditor(AuditorSubCmd::Stop {})) => cmd::auditor::stop(verbosity).await, + Some(SubCmd::Auditor(AuditorSubCmd::Upgrade { do_not_start, force, env_variables, url, version, - }) => { + })) => { cmd::auditor::upgrade(do_not_start, force, env_variables, url, version, verbosity).await } - SubCmd::Balance { + Some(SubCmd::Balance { peer_id: peer_ids, service_name: service_names, - } => cmd::node::balance(peer_ids, service_names, verbosity).await, - SubCmd::Daemon(DaemonSubCmd::Add { + }) => cmd::node::balance(peer_ids, service_names, verbosity).await, + Some(SubCmd::Daemon(DaemonSubCmd::Add { address, env_variables, port, path, url, version, - }) => cmd::daemon::add(address, env_variables, port, path, url, version, verbosity).await, - SubCmd::Daemon(DaemonSubCmd::Start {}) => cmd::daemon::start(verbosity).await, - SubCmd::Daemon(DaemonSubCmd::Stop {}) => cmd::daemon::stop(verbosity).await, - SubCmd::Faucet(faucet_command) => match faucet_command { + })) => cmd::daemon::add(address, env_variables, port, path, url, version, verbosity).await, + Some(SubCmd::Daemon(DaemonSubCmd::Start {})) => cmd::daemon::start(verbosity).await, + Some(SubCmd::Daemon(DaemonSubCmd::Stop {})) => cmd::daemon::stop(verbosity).await, + Some(SubCmd::Faucet(faucet_command)) => match faucet_command { FaucetSubCmd::Add { env_variables, log_dir_path, @@ -1153,7 +1186,7 @@ async fn main() -> Result<()> { .await } }, - SubCmd::Local(local_command) => match local_command { + Some(SubCmd::Local(local_command)) => match local_command { LocalSubCmd::Join { build, count, @@ -1239,27 +1272,27 @@ async fn main() -> Result<()> { json, } => cmd::local::status(details, fail, json).await, }, - SubCmd::NatDetection(NatDetectionSubCmd::Run { + Some(SubCmd::NatDetection(NatDetectionSubCmd::Run { path, servers, url, version, - }) => { + })) => { cmd::nat_detection::run_nat_detection(servers, true, path, url, version, verbosity) .await } - SubCmd::Remove { + Some(SubCmd::Remove { keep_directories, peer_id: peer_ids, service_name: service_names, - } => cmd::node::remove(keep_directories, peer_ids, service_names, verbosity).await, - SubCmd::Reset { force } => cmd::node::reset(force, verbosity).await, - SubCmd::Start { + }) => cmd::node::remove(keep_directories, peer_ids, service_names, verbosity).await, + Some(SubCmd::Reset { force }) => cmd::node::reset(force, verbosity).await, + Some(SubCmd::Start { connection_timeout, interval, peer_id: peer_ids, service_name: service_names, - } => { + }) => { cmd::node::start( connection_timeout, interval, @@ -1269,16 +1302,16 @@ async fn main() -> Result<()> { ) .await } - SubCmd::Status { + Some(SubCmd::Status { details, fail, json, - } => cmd::node::status(details, fail, json).await, - SubCmd::Stop { + }) => cmd::node::status(details, fail, json).await, + Some(SubCmd::Stop { peer_id: peer_ids, service_name: service_names, - } => cmd::node::stop(peer_ids, service_names, verbosity).await, - SubCmd::Upgrade { + }) => cmd::node::stop(peer_ids, service_names, verbosity).await, + Some(SubCmd::Upgrade { connection_timeout, do_not_start, force, @@ -1289,7 +1322,7 @@ async fn main() -> Result<()> { env_variables: provided_env_variable, url, version, - } => { + }) => { cmd::node::upgrade( connection_timeout, do_not_start, @@ -1305,6 +1338,7 @@ async fn main() -> Result<()> { ) .await } + None => Ok(()), } } diff --git a/sn_node_manager/src/bin/daemon/main.rs b/sn_node_manager/src/bin/daemon/main.rs index 99925943be..5de75e2904 100644 --- a/sn_node_manager/src/bin/daemon/main.rs +++ b/sn_node_manager/src/bin/daemon/main.rs @@ -27,16 +27,26 @@ use tonic::{transport::Server, Code, Request, Response, Status}; use tracing::Level; #[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] +#[command(disable_version_flag = true)] struct Args { - /// Specify a port for the daemon to listen for RPCs. It defaults to 12500 if not set. - #[clap(long, default_value_t = DAEMON_DEFAULT_PORT)] - port: u16, /// Specify an Ipv4Addr for the daemon to listen on. This is useful if you want to manage the nodes remotely. /// /// If not set, the daemon listens locally for commands. #[clap(long, default_value_t = Ipv4Addr::new(127, 0, 0, 1))] address: Ipv4Addr, + /// Print the crate version. + #[clap(long)] + pub crate_version: bool, + /// Print the package version. + #[cfg(not(feature = "nightly"))] + #[clap(long)] + pub package_version: bool, + /// Specify a port for the daemon to listen for RPCs. It defaults to 12500 if not set. + #[clap(long, default_value_t = DAEMON_DEFAULT_PORT)] + port: u16, + /// Print version information. + #[clap(long)] + version: bool, } struct SafeNodeManagerDaemon {} @@ -128,12 +138,35 @@ impl SafeNodeManagerDaemon {} #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { + let args = Args::parse(); + + if args.version { + println!( + "{}", + sn_build_info::version_string( + "Autonomi Node Manager RPC Daemon", + env!("CARGO_PKG_VERSION"), + None + ) + ); + return Ok(()); + } + + if args.crate_version { + println!("{}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if args.package_version { + println!("{}", sn_build_info::package_version()); + return Ok(()); + } + let _log_handles = get_log_builder()?.initialize()?; println!("Starting safenodemand"); - let args = Args::parse(); let service = SafeNodeManagerDaemon {}; - // adding our service to our server. if let Err(err) = Server::builder() .add_service(SafeNodeManagerServer::new(service)) .serve(SocketAddr::new(IpAddr::V4(args.address), args.port)) diff --git a/sn_node_manager/src/helpers.rs b/sn_node_manager/src/helpers.rs index a841b54e6f..bd0ca2baae 100644 --- a/sn_node_manager/src/helpers.rs +++ b/sn_node_manager/src/helpers.rs @@ -276,7 +276,7 @@ pub async fn download_and_extract_release( } pub fn get_bin_version(bin_path: &PathBuf) -> Result { - trace!("Obtaining version of binary {bin_path:?}"); + debug!("Obtaining version of binary {bin_path:?}"); let mut cmd = Command::new(bin_path) .arg("--version") .stdout(Stdio::piped()) @@ -293,15 +293,28 @@ pub fn get_bin_version(bin_path: &PathBuf) -> Result { .read_to_string(&mut output) .inspect_err(|err| error!("Output contained non utf8 chars: {err:?}"))?; - let version = output - .split_whitespace() - .last() - .ok_or_else(|| { - error!("Failed to parse version"); - eyre!("Failed to parse version") - })? - .to_string(); - trace!("Obtained version of binary: {version}"); + // Extract the first line of the output + let first_line = output.lines().next().ok_or_else(|| { + error!("No output received from binary"); + eyre!("No output received from binary") + })?; + + let version = if let Some(v_pos) = first_line.find('v') { + // Stable binary: Extract version after 'v' + first_line[v_pos + 1..] + .split_whitespace() + .next() + .map(String::from) + } else { + // Nightly binary: Extract the date at the end of the first line + first_line.split_whitespace().last().map(String::from) + } + .ok_or_else(|| { + error!("Failed to parse version from output"); + eyre!("Failed to parse version from output") + })?; + + debug!("Obtained version of binary: {version}"); Ok(version) } diff --git a/sn_node_manager/src/local.rs b/sn_node_manager/src/local.rs index ec3a7ae34e..58d650cf67 100644 --- a/sn_node_manager/src/local.rs +++ b/sn_node_manager/src/local.rs @@ -62,11 +62,18 @@ impl Launcher for LocalSafeLauncher { fn launch_faucet(&self, genesis_multiaddr: &Multiaddr) -> Result { info!("Launching the faucet server..."); + debug!("Using genesis_multiaddr: {}", genesis_multiaddr.to_string()); let args = vec![ "--peer".to_string(), genesis_multiaddr.to_string(), "server".to_string(), ]; + + debug!( + "Using faucet binary: {}", + self.faucet_bin_path.to_string_lossy() + ); + debug!("Using args: {}", args.join(" ")); let child = Command::new(self.faucet_bin_path.clone()) .args(args) .stdout(Stdio::inherit()) @@ -369,8 +376,8 @@ pub async fn run_network( if !options.join { println!("Launching the faucet server..."); - let pid = launcher.launch_faucet(&bootstrap_peers[0])?; let version = get_bin_version(&options.faucet_bin_path)?; + let pid = launcher.launch_faucet(&bootstrap_peers[0])?; let faucet = FaucetServiceData { faucet_path: options.faucet_bin_path, local: true, diff --git a/sn_node_rpc_client/Cargo.toml b/sn_node_rpc_client/Cargo.toml index 5949d13ecd..055f1913b9 100644 --- a/sn_node_rpc_client/Cargo.toml +++ b/sn_node_rpc_client/Cargo.toml @@ -14,6 +14,9 @@ version = "0.6.29" name = "safenode_rpc_client" path = "src/main.rs" +[features] +nightly = [] + [dependencies] assert_fs = "1.0.0" async-trait = "0.1" @@ -23,6 +26,7 @@ color-eyre = "0.6.2" hex = "~0.4.3" libp2p = { version = "0.54.1", features = ["kad"]} libp2p-identity = { version="0.2.7", features = ["rand"] } +sn_build_info = { path = "../sn_build_info", version = "0.1.13" } sn_client = { path = "../sn_client", version = "0.110.1" } sn_logging = { path = "../sn_logging", version = "0.2.34" } sn_node = { path = "../sn_node", version = "0.111.2" } diff --git a/sn_node_rpc_client/src/main.rs b/sn_node_rpc_client/src/main.rs index 7d019bff95..7930a3b712 100644 --- a/sn_node_rpc_client/src/main.rs +++ b/sn_node_rpc_client/src/main.rs @@ -9,26 +9,35 @@ use clap::Parser; use color_eyre::eyre::Result; - use sn_logging::{Level, LogBuilder}; use sn_node::NodeEvent; - use sn_protocol::safenode_proto::{safe_node_client::SafeNodeClient, NodeEventsRequest}; - use sn_service_management::rpc::{RpcActions, RpcClient}; - use std::{net::SocketAddr, time::Duration}; use tokio_stream::StreamExt; use tonic::Request; #[derive(Parser, Debug)] -#[clap(version, name = "safenode RPC client")] +#[command(disable_version_flag = true)] struct Opt { /// Address of the node's RPC service, e.g. 127.0.0.1:12001. addr: SocketAddr, /// subcommands #[clap(subcommand)] cmd: Cmd, + + /// Print the crate version. + #[clap(long)] + crate_version: bool, + + /// Print the package version. + #[cfg(not(feature = "nightly"))] + #[clap(long)] + package_version: bool, + + /// Print version information. + #[clap(long)] + version: bool, } #[derive(Parser, Debug)] @@ -90,6 +99,29 @@ async fn main() -> Result<()> { let _log_appender_guard = LogBuilder::new(logging_targets).initialize()?; let opt = Opt::parse(); + + if opt.version { + println!( + "{}", + sn_build_info::version_string( + "Autonomi Node RPC Client", + env!("CARGO_PKG_VERSION"), + None + ) + ); + } + + if opt.crate_version { + println!("Crate version: {}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if opt.package_version { + println!("Package version: {}", sn_build_info::package_version()); + return Ok(()); + } + let addr = opt.addr; match opt.cmd { From 73194e3b252417b43abb0daa2cf60238bd319193 Mon Sep 17 00:00:00 2001 From: Chris O'Neil Date: Tue, 24 Sep 2024 17:25:22 +0100 Subject: [PATCH 2/2] ci: nightly releases The nightly release will build the binary set from the `main` branch every night at midnight UTC. The workflow: * Builds the binaries with the `nightly` feature, which versions them using the current date, rather than a Semantic Version. * Uploads packaged binaries to S3, again with the date for the version. * Also uploads packaged binaries to S3 with 'nightly' as the version, which will always be the latest. The previous 'nightly' version will be deleted from the bucket. * Creates a Github Release and uploads archives with the binaries packaged by architecture, as per the stable release. To prevent our releases page being flooded, the previous nightly release will be deleted before the current one is created. --- .github/workflows/nightly-release.yml | 251 ++++++++++++++++++++++++++ Justfile | 109 ++++++++--- 2 files changed, 338 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/nightly-release.yml diff --git a/.github/workflows/nightly-release.yml b/.github/workflows/nightly-release.yml new file mode 100644 index 0000000000..70db60d68e --- /dev/null +++ b/.github/workflows/nightly-release.yml @@ -0,0 +1,251 @@ +name: nightly release + +on: + schedule: + - cron: '0 0 * * *' # Run every night at midnight UTC + workflow_dispatch: # This also allows the workflow to be triggered manually + +env: + WORKFLOW_URL: https://github.com/maidsafe/safe_network/actions/runs + +jobs: + build: + if: ${{ github.repository_owner == 'maidsafe' }} + name: build + environment: stable + env: + FOUNDATION_PK: ${{ vars.FOUNDATION_PK }} + GENESIS_PK: ${{ vars.GENESIS_PK }} + GENESIS_SK: ${{ secrets.GENESIS_SK }} + NETWORK_ROYALTIES_PK: ${{ vars.NETWORK_ROYALTIES_PK }} + PAYMENT_FORWARD_PK: ${{ vars.PAYMENT_FORWARD_PK }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: windows-latest + target: x86_64-pc-windows-msvc + - os: macos-latest + target: x86_64-apple-darwin + - os: macos-latest + target: aarch64-apple-darwin + - os: ubuntu-latest + target: x86_64-unknown-linux-musl + - os: ubuntu-latest + target: arm-unknown-linux-musleabi + - os: ubuntu-latest + target: armv7-unknown-linux-musleabihf + - os: ubuntu-latest + target: aarch64-unknown-linux-musl + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: cargo-bins/cargo-binstall@main + - shell: bash + run: cargo binstall --no-confirm just + + - name: build nightly release artifacts + shell: bash + run: | + just build-release-artifacts "${{ matrix.target }}" "true" + + - uses: actions/upload-artifact@main + with: + name: safe_network-${{ matrix.target }} + path: | + artifacts + !artifacts/.cargo-lock + + - name: post notification to slack on failure + if: ${{ failure() }} + uses: bryannice/gitactions-slack-notification@2.0.0 + env: + SLACK_INCOMING_WEBHOOK: ${{ secrets.SLACK_GH_ACTIONS_WEBHOOK_URL }} + SLACK_MESSAGE: "Please check the logs for the run at ${{ env.WORKFLOW_URL }}/${{ github.run_id }}" + SLACK_TITLE: "Release Failed" + + s3-release: + if: ${{ github.repository_owner == 'maidsafe' }} + name: s3 release + runs-on: ubuntu-latest + needs: [build] + env: + AWS_ACCESS_KEY_ID: ${{ secrets.S3_DEPLOY_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_DEPLOY_AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: eu-west-2 + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@master + with: + name: safe_network-x86_64-pc-windows-msvc + path: artifacts/x86_64-pc-windows-msvc/release + - uses: actions/download-artifact@master + with: + name: safe_network-x86_64-unknown-linux-musl + path: artifacts/x86_64-unknown-linux-musl/release + - uses: actions/download-artifact@master + with: + name: safe_network-x86_64-apple-darwin + path: artifacts/x86_64-apple-darwin/release + - uses: actions/download-artifact@master + with: + name: safe_network-aarch64-apple-darwin + path: artifacts/aarch64-apple-darwin/release + - uses: actions/download-artifact@master + with: + name: safe_network-arm-unknown-linux-musleabi + path: artifacts/arm-unknown-linux-musleabi/release + - uses: actions/download-artifact@master + with: + name: safe_network-armv7-unknown-linux-musleabihf + path: artifacts/armv7-unknown-linux-musleabihf/release + - uses: actions/download-artifact@master + with: + name: safe_network-aarch64-unknown-linux-musl + path: artifacts/aarch64-unknown-linux-musl/release + + - uses: cargo-bins/cargo-binstall@main + - name: install just + shell: bash + run: cargo binstall --no-confirm just + + - name: remove latest nightly release + shell: bash + run: | + just delete-s3-bin "faucet" "nightly" + just delete-s3-bin "nat-detection" "nightly" + just delete-s3-bin "node-launchpad" "nightly" + just delete-s3-bin "safe" "nightly" + just delete-s3-bin "safenode" "nightly" + just delete-s3-bin "safenode_rpc_client" "nightly" + just delete-s3-bin "safenode-manager" "nightly" + just delete-s3-bin "safenodemand" "nightly" + just delete-s3-bin "sn_auditor" "nightly" + + - name: upload binaries to S3 + shell: bash + run: | + version=$(date +"%Y.%m.%d") + just package-bin "faucet" "$version" + just package-bin "nat-detection" "$version" + just package-bin "node-launchpad" "$version" + just package-bin "safe" "$version" + just package-bin "safenode" "$version" + just package-bin "safenode_rpc_client" "$version" + just package-bin "safenode-manager" "$version" + just package-bin "safenodemand" "$version" + just package-bin "sn_auditor" "$version" + just upload-all-packaged-bins-to-s3 + + rm -rf packaged_bins + just package-bin "faucet" "nightly" + just package-bin "nat-detection" "nightly" + just package-bin "node-launchpad" "nightly" + just package-bin "safe" "nightly" + just package-bin "safenode" "nightly" + just package-bin "safenode_rpc_client" "nightly" + just package-bin "safenode-manager" "nightly" + just package-bin "safenodemand" "nightly" + just package-bin "sn_auditor" "nightly" + just upload-all-packaged-bins-to-s3 + + github-release: + if: ${{ github.repository_owner == 'maidsafe' }} + name: github release + runs-on: ubuntu-latest + needs: [s3-release] + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@master + with: + name: safe_network-x86_64-pc-windows-msvc + path: artifacts/x86_64-pc-windows-msvc/release + - uses: actions/download-artifact@master + with: + name: safe_network-x86_64-unknown-linux-musl + path: artifacts/x86_64-unknown-linux-musl/release + - uses: actions/download-artifact@master + with: + name: safe_network-x86_64-apple-darwin + path: artifacts/x86_64-apple-darwin/release + - uses: actions/download-artifact@master + with: + name: safe_network-aarch64-apple-darwin + path: artifacts/aarch64-apple-darwin/release + - uses: actions/download-artifact@master + with: + name: safe_network-arm-unknown-linux-musleabi + path: artifacts/arm-unknown-linux-musleabi/release + - uses: actions/download-artifact@master + with: + name: safe_network-armv7-unknown-linux-musleabihf + path: artifacts/armv7-unknown-linux-musleabihf/release + - uses: actions/download-artifact@master + with: + name: safe_network-aarch64-unknown-linux-musl + path: artifacts/aarch64-unknown-linux-musl/release + + - uses: cargo-bins/cargo-binstall@main + - name: install just + shell: bash + run: cargo binstall --no-confirm just + + - name: set package version + shell: bash + run: | + version=$(date +"%Y.%m.%d") + echo "PACKAGE_VERSION=$version" >> $GITHUB_ENV + + - name: package release artifacts + shell: bash + run: just package-all-architectures + + - name: delete existing nightly release + env: + GITHUB_TOKEN: ${{ secrets.VERSION_BUMP_COMMIT_PAT }} + run: | + releases=$(gh api repos/${{ github.repository }}/releases --paginate) + echo "$releases" | jq -c '.[]' | while read release; do + tag_name=$(echo $release | jq -r '.tag_name') + release_id=$(echo $release | jq -r '.id') + + if [[ $tag_name == nightly* ]]; then + echo "deleting nightly release $tag_name" + gh api -X DELETE repos/${{ github.repository }}/releases/$release_id + exit 0 + fi + done + + - name: create new nightly release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.VERSION_BUMP_COMMIT_PAT }} + with: + tag_name: nightly-${{ env.PACKAGE_VERSION }} + release_name: "${{ env.PACKAGE_VERSION }} Nightly Release" + body: | + Nightly release of the Autonomi binary set, built from the `main` branch. + + These binaries should be compatible with the stable network, but they should be considered experimental. + + For the most reliable experience, prefer the latest stable release. + draft: false + prerelease: true + + - name: upload artifacts as assets + env: + GITHUB_TOKEN: ${{ secrets.VERSION_BUMP_COMMIT_PAT }} + shell: bash + run: | + ( + cd packaged_architectures + ls | xargs gh release upload nightly-${{ env.PACKAGE_VERSION }} + ) + + - name: post notification to slack on failure + if: ${{ failure() }} + uses: bryannice/gitactions-slack-notification@2.0.0 + env: + SLACK_INCOMING_WEBHOOK: ${{ secrets.SLACK_GH_ACTIONS_WEBHOOK_URL }} + SLACK_MESSAGE: "Please check the logs for the run at ${{ env.WORKFLOW_URL }}/${{ github.run_id }}" + SLACK_TITLE: "Nightly Release Failed" \ No newline at end of file diff --git a/Justfile b/Justfile index 8e260b9804..693929fcaf 100644 --- a/Justfile +++ b/Justfile @@ -65,11 +65,12 @@ kill-testbed: doctl compute droplet delete $droplet_id fi -build-release-artifacts arch: +build-release-artifacts arch nightly="false": #!/usr/bin/env bash set -e arch="{{arch}}" + nightly="{{nightly}}" supported_archs=( "x86_64-pc-windows-msvc" "x86_64-apple-darwin" @@ -107,9 +108,9 @@ build-release-artifacts arch: mkdir artifacts cargo clean - echo "===============" - echo "= Public Keys =" - echo "===============" + echo "================" + echo "= Network Keys =" + echo "================" echo "FOUNDATION_PK: $FOUNDATION_PK" echo "GENESIS_PK: $GENESIS_PK" echo "NETWORK_ROYALTIES_PK: $NETWORK_ROYALTIES_PK" @@ -118,28 +119,33 @@ build-release-artifacts arch: cross_container_opts="--env \"GENESIS_PK=$GENESIS_PK\" --env \"GENESIS_SK=$GENESIS_SK\" --env \"FOUNDATION_PK=$FOUNDATION_PK\" --env \"NETWORK_ROYALTIES_PK=$NETWORK_ROYALTIES_PK\" --env \"PAYMENT_FORWARD_PK=$PAYMENT_FORWARD_PK\"" export CROSS_CONTAINER_OPTS=$cross_container_opts + nightly_feature="" + if [[ "$nightly" == "true" ]]; then + nightly_feature="--features nightly" + fi + if [[ $arch == arm* || $arch == armv7* || $arch == aarch64* ]]; then echo "Passing to cross CROSS_CONTAINER_OPTS=$CROSS_CONTAINER_OPTS" cargo binstall --no-confirm cross - cross build --release --target $arch --bin faucet --features=distribution - cross build --release --target $arch --bin nat-detection - cross build --release --target $arch --bin node-launchpad - cross build --release --features="network-contacts,distribution" --target $arch --bin safe - cross build --release --features=network-contacts --target $arch --bin safenode - cross build --release --target $arch --bin safenode-manager - cross build --release --target $arch --bin safenodemand - cross build --release --target $arch --bin safenode_rpc_client - cross build --release --target $arch --bin sn_auditor + cross build --release --target $arch --bin faucet --features=distribution $nightly_feature + cross build --release --target $arch --bin nat-detection $nightly_feature + cross build --release --target $arch --bin node-launchpad $nightly_feature + cross build --release --features="network-contacts,distribution" --target $arch --bin safe $nightly_feature + cross build --release --features=network-contacts --target $arch --bin safenode $nightly_feature + cross build --release --target $arch --bin safenode-manager $nightly_feature + cross build --release --target $arch --bin safenodemand $nightly_feature + cross build --release --target $arch --bin safenode_rpc_client $nightly_feature + cross build --release --target $arch --bin sn_auditor $nightly_feature else - cargo build --release --target $arch --bin faucet --features=distribution - cargo build --release --target $arch --bin nat-detection - cargo build --release --target $arch --bin node-launchpad - cargo build --release --features="network-contacts,distribution" --target $arch --bin safe - cargo build --release --features=network-contacts --target $arch --bin safenode - cargo build --release --target $arch --bin safenode-manager - cargo build --release --target $arch --bin safenodemand - cargo build --release --target $arch --bin safenode_rpc_client - cargo build --release --target $arch --bin sn_auditor + cargo build --release --target $arch --bin faucet --features=distribution $nightly_feature + cargo build --release --target $arch --bin nat-detection $nightly_feature + cargo build --release --target $arch --bin node-launchpad $nightly_feature + cargo build --release --features="network-contacts,distribution" --target $arch --bin safe $nightly_feature + cargo build --release --features=network-contacts --target $arch --bin safenode $nightly_feature + cargo build --release --target $arch --bin safenode-manager $nightly_feature + cargo build --release --target $arch --bin safenodemand $nightly_feature + cargo build --release --target $arch --bin safenode_rpc_client $nightly_feature + cargo build --release --target $arch --bin sn_auditor $nightly_feature fi find target/$arch/release -maxdepth 1 -type f -exec cp '{}' artifacts \; @@ -347,6 +353,65 @@ upload-packaged-bin-to-s3 bin_name: fi done +delete-s3-bin bin_name version: + #!/usr/bin/env bash + set -e + + case "{{bin_name}}" in + faucet) + bucket="sn-faucet" + ;; + nat-detection) + bucket="nat-detection" + ;; + node-launchpad) + bucket="node-launchpad" + ;; + safe) + bucket="sn-cli" + ;; + safenode) + bucket="sn-node" + ;; + safenode-manager) + bucket="sn-node-manager" + ;; + safenodemand) + bucket="sn-node-manager" + ;; + safenode_rpc_client) + bucket="sn-node-rpc-client" + ;; + sn_auditor) + bucket="sn-auditor" + ;; + *) + echo "The {{bin_name}} binary is not supported" + exit 1 + ;; + esac + + architectures=( + "x86_64-pc-windows-msvc" + "x86_64-apple-darwin" + "aarch64-apple-darwin" + "x86_64-unknown-linux-musl" + "arm-unknown-linux-musleabi" + "armv7-unknown-linux-musleabihf" + "aarch64-unknown-linux-musl" + ) + + for arch in "${architectures[@]}"; do + zip_filename="{{bin_name}}-{{version}}-${arch}.zip" + tar_filename="{{bin_name}}-{{version}}-${arch}.tar.gz" + s3_zip_path="s3://$bucket/$zip_filename" + s3_tar_path="s3://$bucket/$tar_filename" + aws s3 rm "$s3_zip_path" + echo "deleted $s3_zip_path" + aws s3 rm "$s3_tar_path" + echo "deleted $s3_tar_path" + done + package-all-architectures: #!/usr/bin/env bash set -e