diff --git a/.github/actions/build_binaries/action.yml b/.github/actions/build_binaries/action.yml index fa28966a390..e50e7e6fdcf 100644 --- a/.github/actions/build_binaries/action.yml +++ b/.github/actions/build_binaries/action.yml @@ -55,9 +55,9 @@ runs: fi if [[ $use_cross_build == "true" ]]; then - cross build --bin ockam --target ${{ inputs.target }} --release --no-default-features -F ockam_command/aws-lc -F ockam_command/orchestrator + cross build --bin ockam --target ${{ inputs.target }} --release --no-default-features -F ockam_command/aws-lc -F ockam_command/admin_commands -F ockam_command/advanced_commands else - cargo build --bin ockam --target ${{ inputs.target }} --release --no-default-features -F ockam_command/aws-lc -F ockam_command/orchestrator + cargo build --bin ockam --target ${{ inputs.target }} --release --no-default-features -F ockam_command/aws-lc -F ockam_command/admin_commands -F ockam_command/advanced_commands fi cp target/${{ inputs.target }}/release/ockam target/${{ inputs.target }}/release/ockam_command diff --git a/Cargo.lock b/Cargo.lock index 38d4318cbbf..adfce88d207 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4846,6 +4846,7 @@ dependencies = [ "arboard", "assert_cmd", "async-trait", + "cfg-if", "cfg_aliases 0.2.1", "clap", "clap_complete", @@ -4874,7 +4875,6 @@ dependencies = [ "open", "opentelemetry", "pem-rfc7468", - "proptest", "r3bl_rs_utils_core", "r3bl_tui", "rand", @@ -4892,7 +4892,6 @@ dependencies = [ "syntect", "tempfile", "thiserror 1.0.69", - "time", "tokio", "tokio-retry", "tracing", diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs b/implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs index 18e04a50169..303ded2d759 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs @@ -12,6 +12,8 @@ use crate::cli_state::CliStateError; use crate::logs::ExportingEnabled; use crate::terminal::notification::Notification; +pub const OCKAM_HOME: &str = "OCKAM_HOME"; + /// Maximum number of notifications present in the channel const NOTIFICATIONS_CHANNEL_CAPACITY: usize = 16; @@ -331,7 +333,7 @@ impl CliState { /// That directory is determined by `OCKAM_HOME` environment variable and is /// $OCKAM_HOME/.ockam. /// - /// If $OCKAM_HOME is not defined then $HOME is used instead + /// If $OCKAM_HOME is not defined, then $HOME is used instead pub(super) fn default_dir() -> Result { Ok(get_env_with_default::( "OCKAM_HOME", diff --git a/implementations/rust/ockam/ockam_api/src/cloud/secure_clients.rs b/implementations/rust/ockam/ockam_api/src/cloud/secure_clients.rs index 38f10c6fb0d..b7c7c9ccc36 100644 --- a/implementations/rust/ockam/ockam_api/src/cloud/secure_clients.rs +++ b/implementations/rust/ockam/ockam_api/src/cloud/secure_clients.rs @@ -23,7 +23,7 @@ pub const DEFAULT_CONTROLLER_ADDRESS: &str = "/dnsaddr/orchestrator.ockam.io/tcp /// from ./static/controller.id. /// How to use: when running a command that spawns a background node or use an embedded node /// add the env variable. `OCKAM_CONTROLLER_IDENTITY_ID={identity.id-contents} ockam ...` -pub(crate) const OCKAM_CONTROLLER_IDENTITY_ID: &str = "OCKAM_CONTROLLER_IDENTITY_ID"; +pub const OCKAM_CONTROLLER_IDENTITY_ID: &str = "OCKAM_CONTROLLER_IDENTITY_ID"; /// Total time to wait for Orchestrator long-running operations to complete pub const ORCHESTRATOR_AWAIT_TIMEOUT: Duration = Duration::from_secs(60 * 10); diff --git a/implementations/rust/ockam/ockam_api/src/ui/terminal/mod.rs b/implementations/rust/ockam/ockam_api/src/ui/terminal/mod.rs index 89d83c03763..77d89325f88 100644 --- a/implementations/rust/ockam/ockam_api/src/ui/terminal/mod.rs +++ b/implementations/rust/ockam/ockam_api/src/ui/terminal/mod.rs @@ -69,22 +69,36 @@ impl Terminal { pub struct TerminalStream { pub writer: T, pub no_color: bool, + bin_name: String, + brand_name: String, } impl TerminalStream { pub fn prepare_msg(&self, msg: impl AsRef) -> Result { + let msg = msg.as_ref().to_string(); + let mut msg = if self.brand_name != "Ockam" { + msg.replace("Ockam", &self.brand_name) + } else { + msg + }; + msg = if self.bin_name != "ockam" { + msg.replace("ockam", &self.bin_name) + } else { + msg + }; + if self.no_color { - Ok(strip_ansi_escapes::strip_str(msg.as_ref())) + Ok(strip_ansi_escapes::strip_str(&msg)) } else { - Ok(msg.as_ref().to_string()) + Ok(msg) } } } /// Trait defining the main methods to write messages to a terminal stream. pub trait TerminalWriter: Clone { - fn stdout(no_color: bool) -> Self; - fn stderr(no_color: bool) -> Self; + fn stdout(no_color: bool, bin_name: impl Into, brand_name: impl Into) -> Self; + fn stderr(no_color: bool, bin_name: impl Into, brand_name: impl Into) -> Self; fn is_tty(&self) -> bool; fn color(&self) -> bool; @@ -95,6 +109,7 @@ pub trait TerminalWriter: Clone { // Core functions impl Terminal { + #[allow(clippy::too_many_arguments)] pub fn new( logging_enabled: bool, logging_goes_to_file: bool, @@ -102,11 +117,15 @@ impl Terminal { no_color: bool, no_input: bool, output_format: OutputFormat, + bin_name: impl Into, + brand_name: impl Into, ) -> Self { + let bin_name = bin_name.into(); + let brand_name = brand_name.into(); let no_color = Self::should_disable_color(no_color); let no_input = Self::should_disable_user_input(no_input); - let stdout = W::stdout(no_color); - let stderr = W::stderr(no_color); + let stdout = W::stdout(no_color, &bin_name, &brand_name); + let stderr = W::stderr(no_color, bin_name, brand_name); let max_width_col_count = get_size().map(|it| it.col_count).unwrap_or(ch!(80)).into(); Self { stdout, @@ -130,6 +149,8 @@ impl Terminal { false, false, OutputFormat::Plain, + "ockam", + "Ockam", ) } diff --git a/implementations/rust/ockam/ockam_api/src/ui/terminal/term.rs b/implementations/rust/ockam/ockam_api/src/ui/terminal/term.rs index bc154f105e1..d307d03b1ae 100644 --- a/implementations/rust/ockam/ockam_api/src/ui/terminal/term.rs +++ b/implementations/rust/ockam/ockam_api/src/ui/terminal/term.rs @@ -7,16 +7,26 @@ use crate::terminal::{TerminalStream, TerminalWriter}; use crate::Result; impl TerminalWriter for TerminalStream { - fn stdout(no_color: bool) -> Self { + fn stdout(no_color: bool, bin_name: impl Into, brand_name: impl Into) -> Self { let writer = Term::stdout(); let no_color = no_color || !writer.features().colors_supported(); - Self { writer, no_color } + Self { + writer, + no_color, + bin_name: bin_name.into(), + brand_name: brand_name.into(), + } } - fn stderr(no_color: bool) -> Self { + fn stderr(no_color: bool, bin_name: impl Into, brand_name: impl Into) -> Self { let writer = Term::stderr(); let no_color = no_color || !writer.features().colors_supported(); - Self { writer, no_color } + Self { + writer, + no_color, + bin_name: bin_name.into(), + brand_name: brand_name.into(), + } } fn is_tty(&self) -> bool { @@ -57,8 +67,16 @@ mod tests { #[test] fn test_write() { - let sut: Terminal> = - Terminal::new(false, false, false, false, false, OutputFormat::Plain); + let sut: Terminal> = Terminal::new( + false, + false, + false, + false, + false, + OutputFormat::Plain, + "", + "", + ); sut.write("1").unwrap(); sut.rewrite("1-r\n").unwrap(); sut.write_line("2".red().to_string()).unwrap(); diff --git a/implementations/rust/ockam/ockam_command/Cargo.toml b/implementations/rust/ockam/ockam_command/Cargo.toml index 62fbb57ea2f..2be83ac58c6 100644 --- a/implementations/rust/ockam/ockam_command/Cargo.toml +++ b/implementations/rust/ockam/ockam_command/Cargo.toml @@ -52,6 +52,7 @@ cfg_aliases = "0.2.1" [dependencies] arboard = "3.4.1" async-trait = "0.1" +cfg-if = "1.0.0" clap = { version = "4.5", features = ["derive", "cargo", "wrap_help"] } clap_complete = "4.5.28" clap_mangen = "0.2.23" @@ -103,14 +104,13 @@ assert_cmd = "2" mockito = "1.5.0" ockam_api = { path = "../ockam_api", version = "0.89.0", default-features = false, features = ["test-utils"] } ockam_macros = { path = "../ockam_macros", version = "^0.36.0" } -proptest = "1.5.0" serial_test = "3.0.0" tempfile = "3.10.1" -time = { version = "0.3", default-features = false, features = ["std", "local-offset"] } [features] -default = ["orchestrator", "rust-crypto", "privileged_portals"] +default = ["rust-crypto", "privileged_portals", "admin_commands", "advanced_commands"] privileged_portals = ["ockam_api/privileged_portals"] -orchestrator = [] aws-lc = ["ockam_vault/aws-lc", "ockam_api/aws-lc", "rustls/aws-lc-rs"] rust-crypto = ["ockam_vault/rust-crypto", "ockam_api/rust-crypto", "rustls/ring"] +admin_commands = [] +advanced_commands = [] diff --git a/implementations/rust/ockam/ockam_command/build.rs b/implementations/rust/ockam/ockam_command/build.rs index e30584d8acd..508c774faae 100644 --- a/implementations/rust/ockam/ockam_command/build.rs +++ b/implementations/rust/ockam/ockam_command/build.rs @@ -1,4 +1,5 @@ use cfg_aliases::cfg_aliases; +use std::env; use std::process::Command; fn hash() { @@ -10,8 +11,37 @@ fn hash() { println!("cargo:rustc-env=GIT_HASH={git_hash}"); } +fn binary_name() { + let bin_name = env::var("OCKAM_COMMAND_BIN_NAME").unwrap_or("ockam".to_string()); + println!("cargo:rustc-env=OCKAM_COMMAND_BIN_NAME={bin_name}"); + println!("cargo:rerun-if-env-changed=OCKAM_COMMAND_BIN_NAME"); + + let brand_name = env::var("OCKAM_COMMAND_BRAND_NAME").unwrap_or("Ockam".to_string()); + println!("cargo:rustc-env=OCKAM_COMMAND_BRAND_NAME={brand_name}"); + println!("cargo:rerun-if-env-changed=OCKAM_COMMAND_BRAND_NAME"); + + let support_email = + env::var("OCKAM_COMMAND_SUPPORT_EMAIL").unwrap_or(format!("support@{}.com", bin_name)); + println!("cargo:rustc-env=OCKAM_COMMAND_SUPPORT_EMAIL={support_email}"); + println!("cargo:rerun-if-env-changed=OCKAM_COMMAND_SUPPORT_EMAIL"); + + let home_dir = env::var("OCKAM_HOME").unwrap_or("".to_string()); + println!("cargo:rustc-env=OCKAM_HOME={home_dir}"); + println!("cargo:rerun-if-env-changed=OCKAM_HOME"); + + let orchestrator_identifier = + env::var("OCKAM_CONTROLLER_IDENTITY_ID").unwrap_or("".to_string()); + println!("cargo:rustc-env=OCKAM_CONTROLLER_IDENTITY_ID={orchestrator_identifier}"); + println!("cargo:rerun-if-env-changed=OCKAM_CONTROLLER_IDENTITY_ID"); + + let orchestrator_address = env::var("OCKAM_CONTROLLER_ADDR").unwrap_or("".to_string()); + println!("cargo:rustc-env=OCKAM_CONTROLLER_ADDR={orchestrator_address}"); + println!("cargo:rerun-if-env-changed=OCKAM_CONTROLLER_ADDR"); +} + fn main() { hash(); + binary_name(); cfg_aliases! { privileged_portals_support: { all(target_os = "linux", feature = "privileged_portals") } } diff --git a/implementations/rust/ockam/ockam_command/src/bin/brand.rs b/implementations/rust/ockam/ockam_command/src/bin/brand.rs new file mode 100644 index 00000000000..f023d18f141 --- /dev/null +++ b/implementations/rust/ockam/ockam_command/src/bin/brand.rs @@ -0,0 +1,226 @@ +use miette::{miette, IntoDiagnostic, Result, WrapErr}; +use ockam_api::cli_state::OCKAM_HOME; +use ockam_api::cloud::{OCKAM_CONTROLLER_ADDR, OCKAM_CONTROLLER_IDENTITY_ID}; +use ockam_api::colors::color_primary; +use ockam_api::fmt_log; +use ockam_api::terminal::PADDING; +use ockam_command::{ + OCKAM_COMMAND_BIN_NAME, OCKAM_COMMAND_BRAND_NAME, OCKAM_COMMAND_SUPPORT_EMAIL, +}; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::fmt::Display; +use std::path::{Path, PathBuf}; + +static CRATE_DIR: Lazy = Lazy::new(|| { + let crate_dir = std::env::var("CARGO_MANIFEST_DIR") + .expect("Couldn't get the value for the `CARGO_MANIFEST_DIR` env variable"); + Path::new(&crate_dir).to_path_buf() +}); + +static BIN_DIR: Lazy = Lazy::new(|| CRATE_DIR.join("src/bin")); + +/// Builds the binaries with the passed configuration +/// How to run: +/// `cargo run --bin brand ./path/to/config.yaml --release` +/// `cargo run --bin brand "{bin1: {brand_name: "Name"}}"` +fn main() -> Result<()> { + // first argument: inline config or path to config file + let config = std::env::args().nth(1).ok_or(miette!( + "Provide at least one argument with binaries configuration" + ))?; + let mut config: Config = match serde_yaml::from_str(&config) { + Ok(config) => config, + Err(_) => { + let config = std::fs::read_to_string(&config).into_diagnostic()?; + serde_yaml::from_str(&config).into_diagnostic()? + } + }; + config.process_defaults()?; + + // build the binaries + for (bin_name, brand_config) in config.items { + build_binary(&bin_name, brand_config)?; + } + Ok(()) +} + +/// Builds the binary with the passed settings +fn build_binary(bin_name: &str, brand_settings: Brand) -> Result<()> { + eprintln!( + "{}\n{brand_settings}", + fmt_log!("Building binary {} with", color_primary(bin_name)) + ); + + let bin_path = create_temporary_binary_file(bin_name)?; + let mut cmd = std::process::Command::new("cargo"); + cmd.args(["build", "--bin", bin_name]); + cmd.env(OCKAM_COMMAND_BIN_NAME, bin_name); + cmd.env(OCKAM_COMMAND_SUPPORT_EMAIL, &brand_settings.support_email); + if let Some(brand_name) = brand_settings.brand_name { + cmd.env(OCKAM_COMMAND_BRAND_NAME, brand_name); + } + if let Some(home_dir) = brand_settings.home_dir { + cmd.env(OCKAM_HOME, home_dir); + } + if let Some(orchestrator_identifier) = brand_settings.orchestrator_identifier { + cmd.env(OCKAM_CONTROLLER_IDENTITY_ID, orchestrator_identifier); + } + if let Some(orchestrator_address) = brand_settings.orchestrator_address { + cmd.env(OCKAM_CONTROLLER_ADDR, orchestrator_address); + } + if let Some(build_args) = brand_settings.build_args { + cmd.args(build_args.split_whitespace()); + } + + let res = cmd + .status() + .into_diagnostic() + .wrap_err(format!("failed to build {bin_name} binary")); + let _ = std::fs::remove_file(bin_path); + res?; + + Ok(()) +} + +/// Copies the `./src/bin/ockam.rs` file into a new file using the passed name +/// This file will be used as the entry point for the binary +fn create_temporary_binary_file(bin_name: &str) -> Result { + let src = BIN_DIR.join("ockam.rs"); + let dst = BIN_DIR.join(format!("{bin_name}.rs")); + std::fs::copy(src, &dst).into_diagnostic()?; + Ok(dst) +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +struct Config { + #[serde(flatten)] + items: BTreeMap, +} + +impl Config { + fn process_defaults(&mut self) -> Result<()> { + for (bin_name, brand) in self.items.iter_mut() { + // Default brand_name to capitalized bin_name + if brand.brand_name.is_none() { + let mut brand_name = bin_name.to_string(); + brand_name[..1].make_ascii_uppercase(); + brand.brand_name = Some(brand_name); + } + + // Default home_dir to $HOME/.{bin_name} + if brand.home_dir.is_none() { + brand.home_dir = Some( + Path::new("$HOME") + .join(format!(".{bin_name}")) + .to_str() + .ok_or(miette!("Failed to convert path to string"))? + .to_string(), + ) + } + } + Ok(()) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] +struct Brand { + support_email: String, + brand_name: Option, + home_dir: Option, + orchestrator_identifier: Option, + orchestrator_address: Option, + build_args: Option, +} + +impl Display for Brand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + "{}", + fmt_log!( + "{PADDING}support email {}", + color_primary(&self.support_email) + ) + )?; + if let Some(brand_name) = &self.brand_name { + writeln!( + f, + "{}", + fmt_log!("{PADDING}brand name {}", color_primary(brand_name)) + )?; + } + if let Some(home_dir) = &self.home_dir { + writeln!( + f, + "{}", + fmt_log!("{PADDING}home dir {}", color_primary(home_dir)) + )?; + } + if let Some(orchestrator_identifier) = &self.orchestrator_identifier { + writeln!( + f, + "{}", + fmt_log!( + "{PADDING}orchestrator identifier {}", + color_primary(orchestrator_identifier) + ) + )?; + } + if let Some(orchestrator_address) = &self.orchestrator_address { + writeln!( + f, + "{}", + fmt_log!( + "{PADDING}orchestrator address {}", + color_primary(orchestrator_address) + ) + )?; + } + if let Some(build_args) = &self.build_args { + writeln!( + f, + "{}", + fmt_log!("{PADDING}build args {}", color_primary(build_args)) + )?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_config_file() { + let config = r#" + bin1: + support_email: bin@support.io + brand_name: Brand1 + home_dir: /home/brand1 + orchestrator_identifier: brand1 + orchestrator_address: brand1.network + bin2: + support_email: bin2@support.io + brand_name: Brand2 + "#; + let parsed: Config = serde_yaml::from_str(config).unwrap(); + assert_eq!(parsed.items.len(), 2); + assert_eq!(parsed.items["bin1"].brand_name.as_deref(), Some("Brand1")); + assert_eq!(parsed.items["bin2"].support_email, "bin2@support.io"); + assert_eq!(parsed.items["bin2"].brand_name.as_deref(), Some("Brand2")); + + let mut processed = parsed.clone(); + processed.process_defaults().unwrap(); + assert_eq!(parsed.items["bin1"], processed.items["bin1"]); + + let bin2 = &processed.items["bin2"]; + assert_eq!(bin2.support_email, "bin2@support.io"); + assert_eq!(bin2.brand_name.as_deref(), Some("Brand2")); + assert_eq!(bin2.home_dir.as_ref().unwrap(), "$HOME/.bin2"); + assert_eq!(bin2.orchestrator_identifier.as_deref(), None); + assert_eq!(bin2.orchestrator_address.as_deref(), None); + } +} diff --git a/implementations/rust/ockam/ockam_command/src/bin/brand.sample.yaml b/implementations/rust/ockam/ockam_command/src/bin/brand.sample.yaml new file mode 100644 index 00000000000..988ed50272e --- /dev/null +++ b/implementations/rust/ockam/ockam_command/src/bin/brand.sample.yaml @@ -0,0 +1,7 @@ +bin: + support_email: support@domain.com + # the following fields are optional + brand_name: Brand1 # if not set, will default to Bin + home_dir: /home/brand1 # if not set, will default to $HOME/.bin + orchestrator_identifier: brand1 # if not set, will default to the OCKAM_CONTROLLER_IDENTITY_ID env var + orchestrator_address: brand1.network # if not set, will default to the OCKAM_CONTROLLER_ADDR env var diff --git a/implementations/rust/ockam/ockam_command/src/command.rs b/implementations/rust/ockam/ockam_command/src/command.rs index 3e512cbd479..da9f93ed09f 100644 --- a/implementations/rust/ockam/ockam_command/src/command.rs +++ b/implementations/rust/ockam/ockam_command/src/command.rs @@ -18,14 +18,17 @@ const ABOUT: &str = include_str!("./static/about.txt"); const LONG_ABOUT: &str = include_str!("./static/long_about.txt"); const AFTER_LONG_HELP: &str = include_str!("./static/after_long_help.txt"); +pub use crate::environment::compile_time_vars::{ + BIN_NAME, BRAND_NAME, OCKAM_COMMAND_BIN_NAME, OCKAM_COMMAND_BRAND_NAME, + OCKAM_COMMAND_SUPPORT_EMAIL, +}; + /// Top-level command, with: -/// -/// - Global arguments (which apply to any OckamSubcommand) +/// - Global arguments /// - A specific subcommand -/// #[derive(Debug, Parser)] #[command( -name = "ockam", +name = BIN_NAME, term_width = 100, about = docs::about(ABOUT), long_about = docs::about(LONG_ABOUT), diff --git a/implementations/rust/ockam/ockam_command/src/command_global_opts.rs b/implementations/rust/ockam/ockam_command/src/command_global_opts.rs index a65f7d85c5d..50c3556e154 100644 --- a/implementations/rust/ockam/ockam_command/src/command_global_opts.rs +++ b/implementations/rust/ockam/ockam_command/src/command_global_opts.rs @@ -6,6 +6,12 @@ use std::sync::Arc; use tokio::runtime::Runtime; use tracing::{debug, info}; +use crate::command::{BIN_NAME, BRAND_NAME}; +use crate::environment::compile_time_vars::load_compile_time_vars; +use crate::subcommand::OckamSubcommand; +use crate::util::exitcode; +use crate::version::Version; +use crate::GlobalArgs; use ockam_api::colors::color_primary; use ockam_api::logs::{ logging_configuration, Colored, ExportingConfiguration, LogLevelWithCratesFilter, @@ -14,11 +20,6 @@ use ockam_api::logs::{ use ockam_api::terminal::{Terminal, TerminalStream}; use ockam_api::{fmt_err, fmt_log, fmt_ok, CliState}; -use crate::subcommand::OckamSubcommand; -use crate::util::exitcode; -use crate::version::Version; -use crate::GlobalArgs; - /// This struct contains the main structs used to implement commands: /// /// - The arguments applicable to all commands @@ -46,6 +47,7 @@ impl CommandGlobalOpts { global_args: &GlobalArgs, cmd: &OckamSubcommand, ) -> miette::Result { + load_compile_time_vars(); let mut state = match CliState::from_env() { Ok(state) => state, Err(err) => { @@ -53,13 +55,16 @@ impl CommandGlobalOpts { // we can try to hard reset the local state. if let OckamSubcommand::Reset(c) = cmd { c.hard_reset(); - println!("{}", fmt_ok!("Local Ockam configuration deleted")); + println!("{}", fmt_ok!("Local {} configuration deleted", BIN_NAME)); exit(exitcode::OK); } eprintln!("{}", fmt_err!("Failed to initialize local state")); eprintln!( "{}", - fmt_log!("Consider upgrading to the latest version of Ockam Command") + fmt_log!( + "Consider upgrading to the latest version of {} Command", + BIN_NAME + ) ); let ockam_home = std::env::var("OCKAM_HOME").unwrap_or("~/.ockam".to_string()); eprintln!( @@ -86,6 +91,8 @@ impl CommandGlobalOpts { global_args.no_color, global_args.no_input, global_args.output_format(), + BIN_NAME, + BRAND_NAME, ); let tracing_guard = Self::setup_logging_tracing(cmd, &logging_configuration, &tracing_configuration); diff --git a/implementations/rust/ockam/ockam_command/src/credential/mod.rs b/implementations/rust/ockam/ockam_command/src/credential/mod.rs index 71c4e6a89fa..7d2374dfb27 100644 --- a/implementations/rust/ockam/ockam_command/src/credential/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/credential/mod.rs @@ -31,7 +31,6 @@ pub struct CredentialCommand { #[derive(Clone, Debug, Subcommand)] pub enum CredentialSubcommand { - #[command(display_order = 900)] List(ListCommand), Issue(IssueCommand), Store(StoreCommand), diff --git a/implementations/rust/ockam/ockam_command/src/docs.rs b/implementations/rust/ockam/ockam_command/src/docs.rs index 444ca39a78d..2dcb468d37c 100644 --- a/implementations/rust/ockam/ockam_command/src/docs.rs +++ b/implementations/rust/ockam/ockam_command/src/docs.rs @@ -1,3 +1,4 @@ +use crate::command::{BIN_NAME, BRAND_NAME}; use crate::Result; use colorful::Colorful; use ockam_api::terminal::TextHighlighter; @@ -70,14 +71,29 @@ pub(crate) fn after_help(text: &str) -> &'static str { /// Render the string if the document should be displayed in a terminal /// Otherwise, if it is a Markdown document just return a static string fn render(body: &str) -> &'static str { + let body = process_branding(body); if is_markdown() { - Box::leak(body.to_string().into_boxed_str()) + Box::leak(body.into_boxed_str()) } else { - let syntax_highlighted = process_terminal_docs(body.to_string()); + let syntax_highlighted = process_terminal_docs(body); Box::leak(syntax_highlighted.into_boxed_str()) } } +fn process_branding(text: &str) -> String { + let mut text = if BRAND_NAME != "Ockam" { + text.replace("Ockam", BRAND_NAME) + } else { + text.to_string() + }; + text = if BIN_NAME != "ockam" { + text.replace("ockam", BIN_NAME) + } else { + text + }; + text +} + /// Use a shell syntax highlighter to render the fenced code blocks in terminals fn process_terminal_docs(input: String) -> String { let mut output: Vec = Vec::new(); diff --git a/implementations/rust/ockam/ockam_command/src/enroll/command.rs b/implementations/rust/ockam/ockam_command/src/enroll/command.rs index a49bab67610..7dea51e56df 100644 --- a/implementations/rust/ockam/ockam_command/src/enroll/command.rs +++ b/implementations/rust/ockam/ockam_command/src/enroll/command.rs @@ -40,19 +40,21 @@ use ockam_api::{fmt_separator, CliState}; const LONG_ABOUT: &str = include_str!("./static/long_about.txt"); const AFTER_LONG_HELP: &str = include_str!("./static/after_long_help.txt"); -/// Enroll your Ockam Identity with Ockam Orchestrator #[derive(Clone, Debug, Args)] #[command( +about = docs::about("Enroll your Ockam Identity with Ockam Orchestrator"), long_about = docs::about(LONG_ABOUT), after_long_help = docs::after_help(AFTER_LONG_HELP) )] pub struct EnrollCommand { - /// The name of an existing Ockam Identity that you wish to enroll. - /// You can use `ockam identity list` to get a list of existing Identities. - /// To create a new Identity, use `ockam identity create`. - /// If you don't specify an Identity name, and you don't have a default Identity, this command - /// will create a default Identity for you and save it locally in the default Vault #[arg(global = true, value_name = "IDENTITY_NAME", long)] + #[arg(help = docs::about("\ + The name of an existing Ockam Identity that you wish to enroll. \ + You can use `ockam identity list` to get a list of existing Identities. \ + To create a new Identity, use `ockam identity create`. \ + If you don't specify an Identity name, and you don't have a default Identity, this command \ + will create a default Identity for you and save it locally in the default Vault + "))] pub identity: Option, /// This option allows you to bypass pasting the one-time code and confirming device diff --git a/implementations/rust/ockam/ockam_command/src/entry_point.rs b/implementations/rust/ockam/ockam_command/src/entry_point.rs index 8b9f7c4ed9d..c8a0d20d084 100644 --- a/implementations/rust/ockam/ockam_command/src/entry_point.rs +++ b/implementations/rust/ockam/ockam_command/src/entry_point.rs @@ -14,7 +14,7 @@ use ockam_api::logs::{ }; use ockam_api::output::Output; -/// Main method for running the `ockam` executable: +/// Main method for running the command executable: /// /// - Parse the input arguments /// - Display the help if the arguments cannot be parsed and store a user journey error diff --git a/implementations/rust/ockam/ockam_command/src/environment/compile_time_vars.rs b/implementations/rust/ockam/ockam_command/src/environment/compile_time_vars.rs new file mode 100644 index 00000000000..b6a8f8b306c --- /dev/null +++ b/implementations/rust/ockam/ockam_command/src/environment/compile_time_vars.rs @@ -0,0 +1,38 @@ +use ockam_api::cli_state::OCKAM_HOME; +use ockam_api::cloud::{OCKAM_CONTROLLER_ADDR, OCKAM_CONTROLLER_IDENTITY_ID}; +use ockam_core::env::get_env_with_default; + +pub const OCKAM_COMMAND_BIN_NAME: &str = "OCKAM_COMMAND_BIN_NAME"; +pub const OCKAM_COMMAND_BRAND_NAME: &str = "OCKAM_COMMAND_BRAND_NAME"; +pub const OCKAM_COMMAND_SUPPORT_EMAIL: &str = "OCKAM_COMMAND_SUPPORT_EMAIL"; + +pub const BIN_NAME: &str = env!("OCKAM_COMMAND_BIN_NAME"); +pub const BRAND_NAME: &str = env!("OCKAM_COMMAND_BRAND_NAME"); +pub const SUPPORT_EMAIL: &str = env!("OCKAM_COMMAND_SUPPORT_EMAIL"); + +pub fn load_compile_time_vars() { + std::env::set_var(OCKAM_COMMAND_BIN_NAME, BIN_NAME); + std::env::set_var(OCKAM_COMMAND_BRAND_NAME, BRAND_NAME); + std::env::set_var(OCKAM_COMMAND_SUPPORT_EMAIL, SUPPORT_EMAIL); + if let Ok(home_dir) = get_env_with_default(OCKAM_HOME, env!("OCKAM_HOME").to_string()) { + if !home_dir.is_empty() { + std::env::set_var(OCKAM_HOME, home_dir); + } + } + if let Ok(orchestrator_identifier) = get_env_with_default( + OCKAM_CONTROLLER_IDENTITY_ID, + env!("OCKAM_CONTROLLER_IDENTITY_ID").to_string(), + ) { + if !orchestrator_identifier.is_empty() { + std::env::set_var(OCKAM_CONTROLLER_IDENTITY_ID, orchestrator_identifier); + } + } + if let Ok(orchestrator_address) = get_env_with_default( + OCKAM_CONTROLLER_ADDR, + env!("OCKAM_CONTROLLER_ADDR").to_string(), + ) { + if !orchestrator_address.is_empty() { + std::env::set_var(OCKAM_CONTROLLER_ADDR, orchestrator_address); + } + } +} diff --git a/implementations/rust/ockam/ockam_command/src/environment/mod.rs b/implementations/rust/ockam/ockam_command/src/environment/mod.rs index e250bde91bb..848eefa90bd 100644 --- a/implementations/rust/ockam/ockam_command/src/environment/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/environment/mod.rs @@ -1,9 +1,13 @@ +pub mod compile_time_vars; + use clap::Args; +use crate::docs; + const ENV_INFO: &str = include_str!("./static/env_info.txt"); -/// Outputs information about environment variables used by the Ockam CLI #[derive(Clone, Debug, Args)] +#[command(about = docs::about("Outputs information about environment variables used by the Ockam CLI"))] pub struct EnvironmentCommand {} impl EnvironmentCommand { diff --git a/implementations/rust/ockam/ockam_command/src/error.rs b/implementations/rust/ockam/ockam_command/src/error.rs index 131f4481847..e7baaf43f08 100644 --- a/implementations/rust/ockam/ockam_command/src/error.rs +++ b/implementations/rust/ockam/ockam_command/src/error.rs @@ -1,3 +1,4 @@ +use crate::environment::compile_time_vars::SUPPORT_EMAIL; use crate::util::exitcode::{self, ExitCode}; use crate::version::Version; use colorful::Colorful; @@ -172,8 +173,9 @@ impl miette::ReportHandler for ErrorReportHandler { f, "\n{}\n{}", fmt_log!( - "{}", - "If you need help, please email us on support@ockam.io".dark_gray() + "{} {}", + "If you need help, please email us on".dark_gray(), + SUPPORT_EMAIL.dark_gray() ), fmt_log!( "{}", diff --git a/implementations/rust/ockam/ockam_command/src/kafka/inlet/create.rs b/implementations/rust/ockam/ockam_command/src/kafka/inlet/create.rs index 3031d901e20..642d448864b 100644 --- a/implementations/rust/ockam/ockam_command/src/kafka/inlet/create.rs +++ b/implementations/rust/ockam/ockam_command/src/kafka/inlet/create.rs @@ -5,6 +5,7 @@ use crate::tcp::util::alias_parser; use crate::util::parsers::hostname_parser; use crate::util::{print_warning_for_deprecated_flag_replaced, process_nodes_multiaddr}; use crate::{ + docs, kafka::{kafka_default_inlet_bind_address, kafka_inlet_default_addr}, node::NodeOpts, Command, CommandGlobalOpts, @@ -54,8 +55,9 @@ pub struct CreateCommand { #[arg(long)] pub brokers_port_range: Option, - /// The route to the Kafka outlet node, either the project in ockam orchestrator or a rust node, expected something like /project/. - /// Use self when the Kafka outlet is local. + #[arg(help = docs::about("\ + The route to the Kafka outlet node, either the project in Ockam Orchestrator or a rust node, \ + expected something like /project/. Use self when the Kafka outlet is local"))] #[arg(long, default_value_t = kafka_default_project_route(), value_name = "ROUTE")] pub to: MultiAddr, @@ -108,24 +110,27 @@ pub struct CreateCommand { )] pub encrypted_fields: Vec, - /// Policy expression that will be used for access control to the Kafka Inlet. - /// If you don't provide it, the policy set for the "tcp-inlet" resource type will be used. - /// - /// You can check the fallback policy with `ockam policy show --resource-type tcp-inlet`. - #[arg(hide = true, long = "allow", id = "INLET-EXPRESSION")] + #[arg(help = docs::about("\ + Policy expression that will be used for access control to the Kafka Inlet. \ + If you don't provide it, the policy set for the \"tcp-inlet\" resource type will be used. \ + \n\nYou can check the fallback policy with `ockam policy show --resource-type tcp-inlet`. + "))] + #[arg(long = "allow", id = "INLET-EXPRESSION")] pub inlet_policy_expression: Option, - /// Policy expression that will be used for access control to the Kafka Consumer. - /// If you don't provide it, the policy set for the "kafka-consumer" resource type will be used. - /// - /// You can check the fallback policy with `ockam policy show --resource-type kafka-consumer`. + #[arg(help = docs::about("\ + Policy expression that will be used for access control to the Kafka Consumer. \ + If you don't provide it, the policy set for the \"kafka-consumer\" resource type will be used. \ + \n\nYou can check the fallback policy with `ockam policy show --resource-type kafka-consumer`. + "))] #[arg(hide = true, long = "allow-consumer", id = "CONSUMER-EXPRESSION")] pub consumer_policy_expression: Option, - /// Policy expression that will be used for access control to the Kafka Producer. - /// If you don't provide it, the policy set for the "kafka-producer" resource type will be used. - /// - /// You can check the fallback policy with `ockam policy show --resource-type kafka-producer`. + #[arg(help = docs::about("\ + Policy expression that will be used for access control to the Kafka Producer. \ + If you don't provide it, the policy set for the \"kafka-producer\" resource type will be used. \ + \n\nYou can check the fallback policy with `ockam policy show --resource-type kafka-producer`. + "))] #[arg(hide = true, long = "allow-producer", id = "PRODUCER-EXPRESSION")] pub producer_policy_expression: Option, } diff --git a/implementations/rust/ockam/ockam_command/src/kafka/mod.rs b/implementations/rust/ockam/ockam_command/src/kafka/mod.rs index 644426f754e..a09e3648a5c 100644 --- a/implementations/rust/ockam/ockam_command/src/kafka/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/kafka/mod.rs @@ -1,3 +1,4 @@ +use cfg_if::cfg_if; use ockam::transport::{HostnamePort, SchemeHostnamePort, StaticHostnamePort}; use ockam_api::nodes::service::default_address::DefaultAddress; use ockam_api::port_range::PortRange; @@ -5,21 +6,34 @@ use ockam_multiaddr::MultiAddr; use std::cmp::min; use std::str::FromStr; -pub(crate) mod consumer; pub(crate) mod inlet; pub(crate) mod outlet; -pub(crate) mod producer; pub(crate) mod util; +cfg_if! { + if #[cfg(feature = "advanced_commands")] { + pub(crate) mod consumer; + pub(crate) mod producer; + + const KAFKA_DEFAULT_CONSUMER_SERVER: StaticHostnamePort = + StaticHostnamePort::new("127.0.0.1", 4000); + const KAFKA_DEFAULT_PRODUCER_SERVER: StaticHostnamePort = + StaticHostnamePort::new("127.0.0.1", 5000); + + fn kafka_default_consumer_server() -> SchemeHostnamePort { + KAFKA_DEFAULT_CONSUMER_SERVER.try_into().unwrap() + } + fn kafka_default_producer_server() -> SchemeHostnamePort { + KAFKA_DEFAULT_PRODUCER_SERVER.try_into().unwrap() + } + } +} + const KAFKA_DEFAULT_BOOTSTRAP_ADDRESS: StaticHostnamePort = StaticHostnamePort::new("127.0.0.1", 9092); const KAFKA_DEFAULT_PROJECT_ROUTE: &str = "/project/default"; -const KAFKA_DEFAULT_CONSUMER_SERVER: StaticHostnamePort = - StaticHostnamePort::new("127.0.0.1", 4000); const KAFKA_DEFAULT_INLET_BIND_ADDRESS: StaticHostnamePort = StaticHostnamePort::new("127.0.0.1", 4000); -const KAFKA_DEFAULT_PRODUCER_SERVER: StaticHostnamePort = - StaticHostnamePort::new("127.0.0.1", 5000); fn kafka_default_outlet_addr() -> String { DefaultAddress::KAFKA_OUTLET.to_string() @@ -37,18 +51,10 @@ fn kafka_default_outlet_server() -> SchemeHostnamePort { KAFKA_DEFAULT_BOOTSTRAP_ADDRESS.try_into().unwrap() } -fn kafka_default_consumer_server() -> SchemeHostnamePort { - KAFKA_DEFAULT_CONSUMER_SERVER.try_into().unwrap() -} - fn kafka_default_inlet_bind_address() -> SchemeHostnamePort { KAFKA_DEFAULT_INLET_BIND_ADDRESS.try_into().unwrap() } -fn kafka_default_producer_server() -> SchemeHostnamePort { - KAFKA_DEFAULT_PRODUCER_SERVER.try_into().unwrap() -} - pub(crate) fn make_brokers_port_range>(bootstrap_server: T) -> PortRange { let boostrap_server_port = bootstrap_server.into().port() as u32; let start = min(boostrap_server_port + 1, u16::MAX as u32) as u16; diff --git a/implementations/rust/ockam/ockam_command/src/kafka/outlet/create.rs b/implementations/rust/ockam/ockam_command/src/kafka/outlet/create.rs index 2fe86006fee..33403dfc7d4 100644 --- a/implementations/rust/ockam/ockam_command/src/kafka/outlet/create.rs +++ b/implementations/rust/ockam/ockam_command/src/kafka/outlet/create.rs @@ -21,6 +21,7 @@ use ockam_core::api::Request; use crate::node::util::initialize_default_node; use crate::util::parsers::hostname_parser; use crate::{ + docs, kafka::{kafka_default_outlet_addr, kafka_default_outlet_server}, node::NodeOpts, Command, CommandGlobalOpts, @@ -53,10 +54,10 @@ pub struct CreateCommand { #[arg(long, id = "BOOLEAN")] pub tls: bool, - /// Policy expression that will be used for access control to the Kafka Outlet. - /// If you don't provide it, the policy set for the "tcp-outlet" resource type will be used. - /// - /// You can check the fallback policy with `ockam policy show --resource-type tcp-outlet`. + #[arg(help = docs::about("\ + Policy expression that will be used for access control to the Kafka Outlet. \ + If you don't provide it, the policy set for the \"tcp-outlet\" resource type will be used. \ + \n\nYou can check the fallback policy with `ockam policy show --resource-type tcp-outlet`."))] #[arg(long = "allow", id = "EXPRESSION")] pub policy_expression: Option, } diff --git a/implementations/rust/ockam/ockam_command/src/lib.rs b/implementations/rust/ockam/ockam_command/src/lib.rs index 5a2ada0440d..0eb01a5926a 100644 --- a/implementations/rust/ockam/ockam_command/src/lib.rs +++ b/implementations/rust/ockam/ockam_command/src/lib.rs @@ -16,7 +16,9 @@ //! ```bash //! cd implementations/rust/ockam/ockam_command && cargo install --path . //! ``` + pub use arguments::*; +use cfg_if::cfg_if; pub use command::*; pub use command_events::*; pub use command_global_opts::*; @@ -26,9 +28,7 @@ pub use pager::*; pub use subcommand::*; pub use terminal::*; -mod admin; mod arguments; -mod authority; mod command; mod command_events; mod command_global_opts; @@ -39,37 +39,26 @@ pub mod enroll; pub mod entry_point; mod environment; pub mod error; -mod flow_control; mod global_args; pub mod identity; +mod influxdb; mod kafka; -mod lease; mod manpages; -mod markdown; -mod message; pub mod node; mod operation; mod output; pub mod pager; mod policy; mod project; -mod project_admin; -mod project_member; mod relay; mod rendezvous; mod reset; mod run; -mod secure_channel; mod service; -#[cfg(feature = "orchestrator")] -mod share; mod shared_args; -mod sidecar; mod space; -mod space_admin; mod status; mod subcommand; -mod subscription; pub mod tcp; mod terminal; mod upgrade; @@ -77,6 +66,27 @@ pub mod util; pub mod value_parsers; mod vault; mod version; -mod worker; -mod influxdb; +cfg_if! { + if #[cfg(feature = "admin_commands")] { + mod admin; + mod authority; + mod lease; + mod project_admin; + mod project_member; + mod markdown; + mod sidecar; + mod space_admin; + mod subscription; + } +} + +cfg_if! { + if #[cfg(feature = "advanced_commands")] { + mod flow_control; + mod message; + mod secure_channel; + mod share; + mod worker; + } +} diff --git a/implementations/rust/ockam/ockam_command/src/manpages.rs b/implementations/rust/ockam/ockam_command/src/manpages.rs index 009ee33ec04..c9fe265b0b1 100644 --- a/implementations/rust/ockam/ockam_command/src/manpages.rs +++ b/implementations/rust/ockam/ockam_command/src/manpages.rs @@ -12,16 +12,18 @@ use tracing::error; use ockam_core::env::get_env_with_default; -use crate::docs; -use crate::OckamCommand; +use crate::{docs, OckamCommand, BIN_NAME}; -/// Generate man pages for all existing Ockam commands #[derive(Clone, Debug, Args)] -#[command(hide = docs::hide())] +#[command( +about = docs::about("Generate man pages for all existing Ockam commands"), +hide = docs::hide() +)] pub struct ManpagesCommand { - /// Absolute path to the output directory where the generated man pages will be stored. - /// Defaults to "~/local/.share/man/man1/"; fallback to "./ockam_man_pages". #[arg(short, long, value_parser(NonEmptyStringValueParser::new()))] + #[arg(help = docs::about("\ + Absolute path to the output directory where the generated man pages will be stored. \ + Defaults to \"~/local/.share/man/man1/\"; fallback to \"./ockam_man_pages\"."))] dir: Option, #[arg( @@ -67,7 +69,7 @@ fn get_man_page_directory(cmd_man_dir: &Option) -> crate::Result { let mut man_dir = env::current_dir().into_diagnostic()?; - man_dir.push("ockam_man_pages"); + man_dir.push(format!("{BIN_NAME}_man_pages")); println!("Man pages stored at: {}", man_dir.display()); man_dir } diff --git a/implementations/rust/ockam/ockam_command/src/markdown/mod.rs b/implementations/rust/ockam/ockam_command/src/markdown/mod.rs index 4448d1b6b52..e5ccd628f72 100644 --- a/implementations/rust/ockam/ockam_command/src/markdown/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/markdown/mod.rs @@ -9,16 +9,18 @@ use once_cell::sync::Lazy; use regex::Regex; use tracing::error; -use crate::docs; use crate::OckamCommand; +use crate::{docs, BIN_NAME}; -/// Generate markdown files for all existing Ockam commands #[derive(Clone, Debug, Args)] -#[command(hide = docs::hide())] +#[command( +about = docs::about("Generate markdown files for all existing Ockam commands"), +hide = docs::hide())] pub struct MarkdownCommand { - /// Absolute path to the output directory where the generated markdown files will be stored. - /// Defaults to "./ockam_markdown_pages" in the current working directory. #[arg(short, long, value_parser(NonEmptyStringValueParser::new()))] + #[arg(help = docs::about("\ + Absolute path to the output directory where the generated markdown files will be stored. \ + Defaults to \"./ockam_markdown_pages\" in the current working directory."))] dir: Option, } @@ -58,7 +60,7 @@ fn get_markdown_page_directory(cmd_mark_dir: &Option) -> io::Result { let mut mark_dir = env::current_dir()?; - mark_dir.push("ockam_markdown_pages"); + mark_dir.push(format!("{BIN_NAME}_markdown_pages")); println!("Markdown pages stored at: {}", mark_dir.display()); mark_dir } @@ -139,7 +141,7 @@ fn generate_markdown_page( write!( buffer, "## {} {} ", - p_cmd.replace("ockam ", ""), + p_cmd.replace(&format!("{BIN_NAME} "), ""), cmd.get_name() )?; diff --git a/implementations/rust/ockam/ockam_command/src/message/send.rs b/implementations/rust/ockam/ockam_command/src/message/send.rs index db70f5643ee..6f13261e53e 100644 --- a/implementations/rust/ockam/ockam_command/src/message/send.rs +++ b/implementations/rust/ockam/ockam_command/src/message/send.rs @@ -21,10 +21,10 @@ use crate::{docs, Command, CommandGlobalOpts, Error}; const LONG_ABOUT: &str = include_str!("./static/send/long_about.txt"); const AFTER_LONG_HELP: &str = include_str!("./static/send/after_long_help.txt"); -/// Send a message to an Ockam node #[derive(Clone, Debug, Args)] #[command( arg_required_else_help = true, +about = docs::about("Send a message to an Ockam node"), long_about = docs::about(LONG_ABOUT), after_long_help = docs::after_help(AFTER_LONG_HELP) )] diff --git a/implementations/rust/ockam/ockam_command/src/node/create.rs b/implementations/rust/ockam/ockam_command/src/node/create.rs index 6d28c581756..063f756c088 100644 --- a/implementations/rust/ockam/ockam_command/src/node/create.rs +++ b/implementations/rust/ockam/ockam_command/src/node/create.rs @@ -122,12 +122,14 @@ pub struct CreateCommand { #[arg(hide = true, long, visible_alias = "launch-config", value_parser = parse_launch_config)] pub launch_configuration: Option, - /// The name of an existing Ockam Identity that this node will use. - /// You can use `ockam identity list` to get a list of existing Identities. - /// To create a new Identity, use `ockam identity create`. - /// If you don't specify an Identity name, and you don't have a default Identity, this command - /// will create a default Identity for you and save it locally in the default Vault #[arg(long = "identity", value_name = "IDENTITY_NAME")] + #[arg(help = docs::about("\ + The name of an existing Ockam Identity that this node will use. \ + You can use `ockam identity list` to get a list of existing Identities. \ + To create a new Identity, use `ockam identity create`. \ + If you don't specify an Identity name, and you don't have a default Identity, this command \ + will create a default Identity for you and save it locally in the default Vault + "))] pub identity: Option, #[command(flatten)] @@ -565,7 +567,16 @@ mod tests { rt.block_on(async { let opts = CommandGlobalOpts { state: CliState::test().await.unwrap(), - terminal: Terminal::new(false, false, false, true, false, OutputFormat::Plain), + terminal: Terminal::new( + false, + false, + false, + true, + false, + OutputFormat::Plain, + "", + "", + ), rt: rt_moved, global_args: GlobalArgs::default(), tracing_guard: None, @@ -618,7 +629,16 @@ mod tests { rt.block_on(async { let opts = CommandGlobalOpts { state: CliState::test().await.unwrap(), - terminal: Terminal::new(false, false, false, true, false, OutputFormat::Plain), + terminal: Terminal::new( + false, + false, + false, + true, + false, + OutputFormat::Plain, + "", + "", + ), rt: rt_moved, global_args: GlobalArgs::default(), tracing_guard: None, diff --git a/implementations/rust/ockam/ockam_command/src/node/create/config.rs b/implementations/rust/ockam/ockam_command/src/node/create/config.rs index c3f518681eb..10177852e24 100644 --- a/implementations/rust/ockam/ockam_command/src/node/create/config.rs +++ b/implementations/rust/ockam/ockam_command/src/node/create/config.rs @@ -4,7 +4,7 @@ use crate::run::parser::config::ConfigParser; use crate::run::parser::resource::*; use crate::run::parser::Version; use crate::value_parsers::{parse_config_or_path_or_url, parse_key_val}; -use crate::CommandGlobalOpts; +use crate::{docs, CommandGlobalOpts}; use clap::Args; use miette::{miette, IntoDiagnostic}; use ockam_api::cli_state::journeys::APPLICATION_EVENT_COMMAND_CONFIGURATION_FILE; @@ -22,10 +22,12 @@ pub struct ConfigArgs { #[arg(long, visible_alias = "node-config", value_name = "YAML")] pub configuration: Option, - /// A path, URL or inlined hex-encoded enrollment ticket to use for the Ockam Identity associated to this node. - /// When passed, the identity will be given a project membership credential. - /// Check the `project ticket` command for more information about enrollment tickets. #[arg(long, env = "ENROLLMENT_TICKET", value_name = "ENROLLMENT TICKET")] + #[arg(help = docs::about("\ + A path, URL or inlined hex-encoded enrollment ticket to use for the Ockam Identity associated to this node. \ + When passed, the identity will be given a project membership credential. \ + Check the `project ticket` command for more information about enrollment tickets. + "))] pub enrollment_ticket: Option, /// Key-value pairs defining environment variables used in the Node configuration. @@ -124,6 +126,8 @@ pub struct NodeConfig { #[serde(flatten)] pub policies: Policies, #[serde(flatten)] + pub relays: Relays, + #[serde(flatten)] pub tcp_outlets: TcpOutlets, #[serde(flatten)] pub tcp_inlets: TcpInlets, @@ -135,8 +139,6 @@ pub struct NodeConfig { pub kafka_inlet: KafkaInlet, #[serde(flatten)] pub kafka_outlet: KafkaOutlet, - #[serde(flatten)] - pub relays: Relays, } impl NodeConfig { diff --git a/implementations/rust/ockam/ockam_command/src/node/create/foreground.rs b/implementations/rust/ockam/ockam_command/src/node/create/foreground.rs index aa7ff73a700..504577d97c8 100644 --- a/implementations/rust/ockam/ockam_command/src/node/create/foreground.rs +++ b/implementations/rust/ockam/ockam_command/src/node/create/foreground.rs @@ -1,26 +1,35 @@ +use cfg_if::cfg_if; +use miette::IntoDiagnostic; use std::sync::Arc; - -use miette::{miette, IntoDiagnostic}; -use tokio::time::{sleep, Duration}; use tracing::{debug, info, instrument}; -use crate::node::show::is_node_up; use crate::node::CreateCommand; -use crate::secure_channel::listener::create as secure_channel_listener; use crate::util::foreground_args::wait_for_exit_signal; use crate::CommandGlobalOpts; use ockam::tcp::{TcpListenerOptions, TcpTransport}; use ockam::udp::{UdpBindArguments, UdpBindOptions, UdpTransport}; -use ockam::{Address, Context}; -use ockam_api::fmt_log; +use ockam::Context; use ockam_api::nodes::service::NodeManagerTransport; +use ockam_api::nodes::InMemoryNode; use ockam_api::nodes::{ service::{NodeManagerGeneralOptions, NodeManagerTransportOptions}, NodeManagerWorker, NODEMANAGER_ADDR, }; -use ockam_api::nodes::{BackgroundNodeClient, InMemoryNode}; use ockam_api::terminal::notification::NotificationHandler; -use ockam_core::{route, LOCAL}; + +cfg_if! { + if #[cfg(feature = "advanced_commands")] { + use miette::miette; + use tokio::time::{sleep, Duration}; + + use ockam::Address; + use ockam_api::fmt_log; + use ockam_api::nodes::BackgroundNodeClient; + use ockam_core::{route, LOCAL}; + + use crate::node::show::is_node_up; + } +} impl CreateCommand { #[instrument(skip_all, fields(node_name = self.name))] @@ -117,6 +126,7 @@ impl CreateCommand { .into_diagnostic()?; debug!("node manager worker started"); + #[cfg(feature = "advanced_commands")] if self.start_services(ctx, &opts).await.is_err() { //TODO: Process should terminate on any error during its setup phase, // not just during the start_services. @@ -152,6 +162,7 @@ impl CreateCommand { Ok(()) } + #[cfg(feature = "advanced_commands")] async fn start_services(&self, ctx: &Context, opts: &CommandGlobalOpts) -> miette::Result<()> { if let Some(config) = &self.launch_configuration { if let Some(startup_services) = &config.startup_services { @@ -169,7 +180,7 @@ impl CreateCommand { if !cfg.disabled { opts.terminal .write_line(fmt_log!("Starting secure-channel listener ..."))?; - secure_channel_listener::create_listener( + crate::secure_channel::listener::create::create_listener( ctx, Address::from((LOCAL, cfg.address)), cfg.authorized_identifiers, diff --git a/implementations/rust/ockam/ockam_command/src/operation/util.rs b/implementations/rust/ockam/ockam_command/src/operation/util.rs index 572b39d0d3e..6b098e952d6 100644 --- a/implementations/rust/ockam/ockam_command/src/operation/util.rs +++ b/implementations/rust/ockam/ockam_command/src/operation/util.rs @@ -41,6 +41,7 @@ pub async fn check_for_project_completion( Ok(project) } +#[allow(unused)] pub async fn check_for_operation_completion( opts: &CommandGlobalOpts, ctx: &Context, diff --git a/implementations/rust/ockam/ockam_command/src/project/addon/configure_influxdb.rs b/implementations/rust/ockam/ockam_command/src/project/addon/configure_influxdb.rs index 38cbdeda5fd..2706746bcc6 100644 --- a/implementations/rust/ockam/ockam_command/src/project/addon/configure_influxdb.rs +++ b/implementations/rust/ockam/ockam_command/src/project/addon/configure_influxdb.rs @@ -25,8 +25,8 @@ long_about = docs::about(LONG_ABOUT), after_long_help = docs::after_help(AFTER_LONG_HELP), )] pub struct AddonConfigureInfluxdbSubcommand { - /// Ockam Project Name #[arg( + help = docs::about("Ockam Project Name"), long = "project", id = "project", value_name = "PROJECT_NAME", @@ -92,8 +92,8 @@ pub struct AddonConfigureInfluxdbSubcommand { )] max_ttl_secs: i32, - /// Ockam Access Rule for who can use the token lease service #[arg( + help = docs::about("Ockam Access Rule for who can use the token lease service"), long = "user-access-role", id = "user-access-role", hide = true, @@ -102,9 +102,9 @@ pub struct AddonConfigureInfluxdbSubcommand { )] user_access_role: Option, - /// Ockam Access Rule for who can manage the token lease service #[arg( - long = "adamin-access-role", + help = docs::about("Ockam Access Rule for who can manage the token lease service"), + long = "admin-access-role", id = "admin-access-role", hide = true, value_name = "ADMIN_ACCESS_ROLE", diff --git a/implementations/rust/ockam/ockam_command/src/project/addon/configure_kafka.rs b/implementations/rust/ockam/ockam_command/src/project/addon/configure_kafka.rs index 8c93e041464..bd4db933137 100644 --- a/implementations/rust/ockam/ockam_command/src/project/addon/configure_kafka.rs +++ b/implementations/rust/ockam/ockam_command/src/project/addon/configure_kafka.rs @@ -28,8 +28,8 @@ const AFTER_LONG_HELP: &str = include_str!("./static/configure_kafka/after_long_ /// Configure the Apache Kafka addon for a project #[derive(Clone, Debug, Args)] pub struct KafkaCommandConfig { - /// Ockam project name #[arg( + help = docs::about("Ockam project name"), long = "project", id = "project", value_name = "PROJECT_NAME", diff --git a/implementations/rust/ockam/ockam_command/src/project/addon/configure_okta.rs b/implementations/rust/ockam/ockam_command/src/project/addon/configure_okta.rs index d75fcce3c83..20e7f58d68c 100644 --- a/implementations/rust/ockam/ockam_command/src/project/addon/configure_okta.rs +++ b/implementations/rust/ockam/ockam_command/src/project/addon/configure_okta.rs @@ -36,8 +36,8 @@ before_help = docs::before_help(PREVIEW_TAG), after_long_help = docs::after_help(AFTER_LONG_HELP), )] pub struct AddonConfigureOktaSubcommand { - /// Ockam Project name #[arg( + help = docs::about("Ockam Project name"), long = "project", id = "project", value_name = "PROJECT_NAME", @@ -68,7 +68,7 @@ pub struct AddonConfigureOktaSubcommand { #[arg(long = "cert-path", group = "cert", value_name = "CERTIFICATE_PATH")] certificate_path: Option, - /// Okta Client ID. + /// Okta Client ID #[arg( long, id = "client_id", @@ -77,7 +77,7 @@ pub struct AddonConfigureOktaSubcommand { )] client_id: String, - /// Attributes names to copy from Okta userprofile into Ockam credential. + #[arg(help = docs::about("Attributes names to copy from Okta userprofile into Ockam credential"))] #[arg(short, long = "attribute", value_name = "ATTRIBUTE")] attributes: Vec, } diff --git a/implementations/rust/ockam/ockam_command/src/project/mod.rs b/implementations/rust/ockam/ockam_command/src/project/mod.rs index 8e3c50e0ccd..fbe1867b4f6 100644 --- a/implementations/rust/ockam/ockam_command/src/project/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/project/mod.rs @@ -1,37 +1,43 @@ use clap::{Args, Subcommand}; -pub use addon::AddonCommand; -pub use create::CreateCommand; -pub use delete::DeleteCommand; pub use enroll::EnrollCommand; pub use import::ImportCommand; pub use info::InfoCommand; pub use list::ListCommand; pub use show::ShowCommand; -pub use ticket::TicketCommand; pub use version::VersionCommand; use crate::{docs, Command, CommandGlobalOpts}; -mod addon; -mod create; -mod delete; pub(crate) mod enroll; mod import; mod info; mod list; mod show; -mod ticket; +#[allow(unused)] pub mod util; mod version; +cfg_if::cfg_if! { + if #[cfg(feature = "admin_commands")] { + mod addon; + mod create; + mod delete; + mod ticket; + pub use addon::AddonCommand; + pub use create::CreateCommand; + pub use delete::DeleteCommand; + pub use ticket::TicketCommand; + } +} + const LONG_ABOUT: &str = include_str!("./static/long_about.txt"); -/// Manage Projects in Ockam Orchestrator #[derive(Clone, Debug, Args)] #[command( arg_required_else_help = true, subcommand_required = true, +about = docs::about("Manage Projects in Ockam Orchestrator"), long_about = docs::about(LONG_ABOUT), )] pub struct ProjectCommand { @@ -41,46 +47,61 @@ pub struct ProjectCommand { #[derive(Clone, Debug, Subcommand)] pub enum ProjectSubcommand { - Create(CreateCommand), + Enroll(EnrollCommand), Import(ImportCommand), - Delete(DeleteCommand), List(ListCommand), Show(ShowCommand), Version(VersionCommand), Information(InfoCommand), + + #[cfg(feature = "admin_commands")] Ticket(TicketCommand), + #[cfg(feature = "admin_commands")] + Create(CreateCommand), + #[cfg(feature = "admin_commands")] + Delete(DeleteCommand), + #[cfg(feature = "admin_commands")] Addon(AddonCommand), - Enroll(EnrollCommand), } impl ProjectCommand { pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> { match self.subcommand { - ProjectSubcommand::Create(c) => c.run(opts), + ProjectSubcommand::Enroll(c) => c.run(opts), ProjectSubcommand::Import(c) => c.run(opts), - ProjectSubcommand::Delete(c) => c.run(opts), ProjectSubcommand::List(c) => c.run(opts), ProjectSubcommand::Show(c) => c.run(opts), ProjectSubcommand::Version(c) => c.run(opts), - ProjectSubcommand::Ticket(c) => c.run(opts), ProjectSubcommand::Information(c) => c.run(opts), + + #[cfg(feature = "admin_commands")] + ProjectSubcommand::Ticket(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + ProjectSubcommand::Create(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + ProjectSubcommand::Delete(c) => c.run(opts), + #[cfg(feature = "admin_commands")] ProjectSubcommand::Addon(c) => c.run(opts), - ProjectSubcommand::Enroll(c) => c.run(opts), } } pub fn name(&self) -> String { match &self.subcommand { - ProjectSubcommand::Create(c) => c.name(), - ProjectSubcommand::Delete(c) => c.name(), + ProjectSubcommand::Enroll(c) => c.name(), + ProjectSubcommand::Import(c) => c.name(), ProjectSubcommand::List(c) => c.name(), ProjectSubcommand::Show(c) => c.name(), - ProjectSubcommand::Import(c) => c.name(), ProjectSubcommand::Version(c) => c.name(), ProjectSubcommand::Information(c) => c.name(), + + #[cfg(feature = "admin_commands")] ProjectSubcommand::Ticket(c) => c.name(), + #[cfg(feature = "admin_commands")] + ProjectSubcommand::Create(c) => c.name(), + #[cfg(feature = "admin_commands")] + ProjectSubcommand::Delete(c) => c.name(), + #[cfg(feature = "admin_commands")] ProjectSubcommand::Addon(c) => c.name(), - ProjectSubcommand::Enroll(c) => c.name(), } } } diff --git a/implementations/rust/ockam/ockam_command/src/project_admin/mod.rs b/implementations/rust/ockam/ockam_command/src/project_admin/mod.rs index 98c64d2d907..d9d7951fc2a 100644 --- a/implementations/rust/ockam/ockam_command/src/project_admin/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/project_admin/mod.rs @@ -9,9 +9,9 @@ use crate::project_admin::delete::DeleteCommand; use crate::project_admin::list::ListCommand; use crate::{docs, Command, CommandGlobalOpts}; -/// Manage Project Admins in Ockam Orchestrator #[derive(Clone, Debug, Args)] -#[command(hide = docs::hide(), arg_required_else_help = true, subcommand_required = true)] +#[command(hide = docs::hide(), arg_required_else_help = true, subcommand_required = true, +about = docs::about("Manage Project Admins in Ockam Orchestrator"))] pub struct ProjectAdminCommand { #[command(subcommand)] subcommand: ProjectAdminSubcommand, diff --git a/implementations/rust/ockam/ockam_command/src/reset/mod.rs b/implementations/rust/ockam/ockam_command/src/reset/mod.rs index 6964ee4fd63..afa3cbdbb25 100644 --- a/implementations/rust/ockam/ockam_command/src/reset/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/reset/mod.rs @@ -19,12 +19,12 @@ const LONG_ABOUT: &str = include_str!("./static/long_about.txt"); const AFTER_LONG_HELP: &str = include_str!("./static/after_long_help.txt"); const UNSAFE_TAG: &str = include_str!("../static/unsafe_tag.txt"); -/// Removes the local Ockam configuration including all Identities and Nodes #[derive(Clone, Debug, Args)] #[command( - before_help = docs::before_help(UNSAFE_TAG), - long_about = docs::about(LONG_ABOUT), - after_long_help = docs::after_help(AFTER_LONG_HELP) +before_help = docs::before_help(UNSAFE_TAG), +about = docs::about("Removes the local Ockam configuration including all Identities and Nodes"), +long_about = docs::about(LONG_ABOUT), +after_long_help = docs::after_help(AFTER_LONG_HELP) )] pub struct ResetCommand { /// Confirm the reset without prompting diff --git a/implementations/rust/ockam/ockam_command/src/service/mod.rs b/implementations/rust/ockam/ockam_command/src/service/mod.rs index 6cf4205c12b..c065b091c3f 100644 --- a/implementations/rust/ockam/ockam_command/src/service/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/service/mod.rs @@ -1,41 +1,44 @@ -use clap::{Args, Subcommand}; - -use list::ListCommand; -pub(crate) use start::StartCommand; - -use crate::{docs, CommandGlobalOpts}; - pub(crate) mod config; -pub(crate) mod list; -pub(crate) mod start; -#[derive(Clone, Debug, Args)] -#[command(hide = docs::hide())] -pub struct ServiceCommand { - #[command(subcommand)] - subcommand: ServiceSubcommand, -} +cfg_if::cfg_if! { + if #[cfg(feature = "advanced_commands")] { + pub(crate) mod list; + pub(crate) mod start; -#[derive(Clone, Debug, Subcommand)] -pub enum ServiceSubcommand { - #[command(display_order = 900)] - Start(StartCommand), - #[command(display_order = 901)] - List(ListCommand), -} + use clap::{Args, Subcommand}; + use list::ListCommand; + pub(crate) use start::StartCommand; + use crate::{docs, CommandGlobalOpts}; -impl ServiceCommand { - pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> { - match self.subcommand { - ServiceSubcommand::Start(c) => c.run(opts), - ServiceSubcommand::List(c) => c.run(opts), + #[derive(Clone, Debug, Args)] + #[command(hide = docs::hide())] + pub struct ServiceCommand { + #[command(subcommand)] + subcommand: ServiceSubcommand, } - } - pub fn name(&self) -> String { - match &self.subcommand { - ServiceSubcommand::Start(c) => c.name(), - ServiceSubcommand::List(c) => c.name(), + #[derive(Clone, Debug, Subcommand)] + pub enum ServiceSubcommand { + #[command(display_order = 900)] + Start(StartCommand), + #[command(display_order = 901)] + List(ListCommand), + } + + impl ServiceCommand { + pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> { + match self.subcommand { + ServiceSubcommand::Start(c) => c.run(opts), + ServiceSubcommand::List(c) => c.run(opts), + } + } + + pub fn name(&self) -> String { + match &self.subcommand { + ServiceSubcommand::Start(c) => c.name(), + ServiceSubcommand::List(c) => c.name(), + } + } } } } diff --git a/implementations/rust/ockam/ockam_command/src/share/mod.rs b/implementations/rust/ockam/ockam_command/src/share/mod.rs index 43966ee92d9..f75afd078d1 100644 --- a/implementations/rust/ockam/ockam_command/src/share/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/share/mod.rs @@ -6,7 +6,7 @@ pub use list::ListCommand; pub use service::ServiceCreateCommand; pub use show::ShowCommand; -use crate::CommandGlobalOpts; +use crate::{docs, CommandGlobalOpts}; mod accept; mod create; @@ -14,9 +14,9 @@ mod list; mod service; mod show; -/// Manage sharing invitations in Ockam Orchestrator #[derive(Clone, Debug, Args)] -#[command(arg_required_else_help = true, subcommand_required = true)] +#[command(arg_required_else_help = true, subcommand_required = true, +about=docs::about("Manage sharing invitations in Ockam Orchestrator"))] pub struct ShareCommand { #[command(subcommand)] subcommand: ShareSubcommand, diff --git a/implementations/rust/ockam/ockam_command/src/space/create.rs b/implementations/rust/ockam/ockam_command/src/space/create.rs index 928ae027ce5..f4f67c67d58 100644 --- a/implementations/rust/ockam/ockam_command/src/space/create.rs +++ b/implementations/rust/ockam/ockam_command/src/space/create.rs @@ -23,8 +23,8 @@ long_about = docs::about(LONG_ABOUT), after_long_help = docs::after_help(AFTER_LONG_HELP), )] pub struct CreateCommand { - /// Name of the space - must be unique across all Ockam Orchestrator users. #[arg(display_order = 1001, value_name = "SPACE_NAME", default_value_t = random_name(), hide_default_value = true, value_parser = validate_space_name)] + #[arg(help = docs::about("Name of the `BIN_NAME` space - must be unique across all Ockam Orchestrator users."))] pub name: String, /// Administrators for this space diff --git a/implementations/rust/ockam/ockam_command/src/space/mod.rs b/implementations/rust/ockam/ockam_command/src/space/mod.rs index fd68e62951c..86c0bf0c812 100644 --- a/implementations/rust/ockam/ockam_command/src/space/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/space/mod.rs @@ -1,24 +1,29 @@ use clap::{Args, Subcommand}; -pub use create::CreateCommand; -pub use delete::DeleteCommand; pub use list::ListCommand; pub use show::ShowCommand; use crate::{docs, Command, CommandGlobalOpts}; -mod create; -mod delete; mod list; mod show; +cfg_if::cfg_if! { + if #[cfg(feature = "admin_commands")] { + mod create; + mod delete; + pub use create::CreateCommand; + pub use delete::DeleteCommand; + } +} + const LONG_ABOUT: &str = include_str!("./static/long_about.txt"); -/// Manage Spaces in Ockam Orchestrator #[derive(Clone, Debug, Args)] #[command( arg_required_else_help = true, subcommand_required = true, + about = docs::about("Manage Spaces in Ockam Orchestrator"), long_about = docs::about(LONG_ABOUT), )] pub struct SpaceCommand { @@ -28,32 +33,37 @@ pub struct SpaceCommand { #[derive(Clone, Debug, Subcommand)] pub enum SpaceSubcommand { - #[command(display_order = 800)] - Create(CreateCommand), - #[command(display_order = 800)] - Delete(DeleteCommand), - #[command(display_order = 800)] List(ListCommand), - #[command(display_order = 800)] Show(ShowCommand), + + #[cfg(feature = "admin_commands")] + Create(CreateCommand), + #[cfg(feature = "admin_commands")] + Delete(DeleteCommand), } impl SpaceCommand { pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> { match self.subcommand { - SpaceSubcommand::Create(c) => c.run(opts), - SpaceSubcommand::Delete(c) => c.run(opts), SpaceSubcommand::List(c) => c.run(opts), SpaceSubcommand::Show(c) => c.run(opts), + + #[cfg(feature = "admin_commands")] + SpaceSubcommand::Create(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + SpaceSubcommand::Delete(c) => c.run(opts), } } pub fn name(&self) -> String { match &self.subcommand { - SpaceSubcommand::Create(c) => c.name(), - SpaceSubcommand::Delete(c) => c.name(), SpaceSubcommand::List(c) => c.name(), SpaceSubcommand::Show(c) => c.name(), + + #[cfg(feature = "admin_commands")] + SpaceSubcommand::Create(c) => c.name(), + #[cfg(feature = "admin_commands")] + SpaceSubcommand::Delete(c) => c.name(), } } } diff --git a/implementations/rust/ockam/ockam_command/src/space_admin/mod.rs b/implementations/rust/ockam/ockam_command/src/space_admin/mod.rs index acd3e0715c9..f2fbbecb414 100644 --- a/implementations/rust/ockam/ockam_command/src/space_admin/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/space_admin/mod.rs @@ -7,11 +7,11 @@ use clap::{Args, Subcommand}; use crate::space_admin::add::AddCommand; use crate::space_admin::delete::DeleteCommand; use crate::space_admin::list::ListCommand; -use crate::{Command, CommandGlobalOpts}; +use crate::{docs, Command, CommandGlobalOpts}; -/// Manage Space Admins in Ockam Orchestrator #[derive(Clone, Debug, Args)] -#[command(arg_required_else_help = true, subcommand_required = true)] +#[command(arg_required_else_help = true, subcommand_required = true, +about = docs::about("Manage Space Admins in Ockam Orchestrator"))] pub struct SpaceAdminCommand { #[command(subcommand)] subcommand: SpaceAdminSubcommand, diff --git a/implementations/rust/ockam/ockam_command/src/status/mod.rs b/implementations/rust/ockam/ockam_command/src/status/mod.rs index 77cf3e0badc..16242e33a49 100644 --- a/implementations/rust/ockam/ockam_command/src/status/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/status/mod.rs @@ -27,11 +27,11 @@ use crate::docs; const LONG_ABOUT: &str = include_str!("./static/long_about.txt"); const AFTER_LONG_HELP: &str = include_str!("./static/after_long_help.txt"); -/// Display information about your Ockam instance #[derive(Clone, Debug, Args)] #[command( - long_about = docs::about(LONG_ABOUT), - after_long_help = docs::after_help(AFTER_LONG_HELP) +about = docs::about("Display information about your Ockam instance"), +long_about = docs::about(LONG_ABOUT), +after_long_help = docs::after_help(AFTER_LONG_HELP) )] pub struct StatusCommand { #[command(flatten)] diff --git a/implementations/rust/ockam/ockam_command/src/subcommand.rs b/implementations/rust/ockam/ockam_command/src/subcommand.rs index 5e3e7380fb8..6157a7ce4f0 100644 --- a/implementations/rust/ockam/ockam_command/src/subcommand.rs +++ b/implementations/rust/ockam/ockam_command/src/subcommand.rs @@ -15,177 +15,211 @@ use ockam_api::{fmt_log, fmt_warn, CliState}; use ockam_core::OpenTelemetryContext; use ockam_node::Context; -use crate::admin::AdminCommand; -use crate::authority::{AuthorityCommand, AuthoritySubcommand}; use crate::command_global_opts::CommandGlobalOpts; use crate::completion::CompletionCommand; use crate::credential::CredentialCommand; -use crate::enroll::EnrollCommand; +use crate::docs; use crate::environment::EnvironmentCommand; -use crate::flow_control::FlowControlCommand; use crate::identity::IdentityCommand; use crate::influxdb::inlet::InfluxDBInletCommand; use crate::influxdb::outlet::InfluxDBOutletCommand; -use crate::kafka::consumer::KafkaConsumerCommand; use crate::kafka::inlet::KafkaInletCommand; use crate::kafka::outlet::KafkaOutletCommand; -use crate::kafka::producer::KafkaProducerCommand; -use crate::lease::LeaseCommand; use crate::manpages::ManpagesCommand; -use crate::markdown::MarkdownCommand; -use crate::message::MessageCommand; -use crate::node::NodeCommand; -use crate::node::NodeSubcommand; +use crate::node::{NodeCommand, NodeSubcommand}; use crate::policy::PolicyCommand; use crate::project::ProjectCommand; -use crate::project_admin::ProjectAdminCommand; -use crate::project_member::ProjectMemberCommand; use crate::relay::RelayCommand; use crate::rendezvous::RendezvousCommand; use crate::reset::ResetCommand; use crate::run::RunCommand; -use crate::secure_channel::listener::SecureChannelListenerCommand; -use crate::secure_channel::SecureChannelCommand; -use crate::service::ServiceCommand; -#[cfg(feature = "orchestrator")] -use crate::share::ShareCommand; use crate::shared_args::RetryOpts; -use crate::sidecar::SidecarCommand; use crate::space::SpaceCommand; -use crate::space_admin::SpaceAdminCommand; use crate::status::StatusCommand; -use crate::subscription::SubscriptionCommand; -use crate::tcp::connection::TcpConnectionCommand; use crate::tcp::inlet::TcpInletCommand; -use crate::tcp::listener::TcpListenerCommand; use crate::tcp::outlet::TcpOutletCommand; use crate::util::async_cmd; use crate::vault::VaultCommand; -use crate::worker::WorkerCommand; -use crate::{docs, Error, Result}; +use crate::Error; +use crate::Result; + +cfg_if::cfg_if! { + if #[cfg(feature = "admin_commands")] { + use crate::enroll::EnrollCommand; + use crate::admin::AdminCommand; + use crate::authority::{AuthorityCommand, AuthoritySubcommand}; + use crate::lease::LeaseCommand; + use crate::markdown::MarkdownCommand; + use crate::project_admin::ProjectAdminCommand; + use crate::project_member::ProjectMemberCommand; + use crate::sidecar::SidecarCommand; + use crate::space_admin::SpaceAdminCommand; + use crate::subscription::SubscriptionCommand; + } +} + +cfg_if::cfg_if! { + if #[cfg(feature = "advanced_commands")] { + use crate::flow_control::FlowControlCommand; + use crate::kafka::consumer::KafkaConsumerCommand; + use crate::kafka::producer::KafkaProducerCommand; + use crate::message::MessageCommand; + use crate::secure_channel::listener::SecureChannelListenerCommand; + use crate::secure_channel::SecureChannelCommand; + use crate::service::ServiceCommand; + use crate::share::ShareCommand; + use crate::tcp::listener::TcpListenerCommand; + use crate::tcp::connection::TcpConnectionCommand; + use crate::worker::WorkerCommand; + } +} -/// List of commands which can be executed with `ockam` #[derive(Clone, Debug, Subcommand)] +#[command(about = docs::about("List of commands which can be executed with `ockam`"))] pub enum OckamSubcommand { - #[command(display_order = 800)] - Enroll(EnrollCommand), + Node(NodeCommand), + Vault(VaultCommand), + Identity(IdentityCommand), Space(SpaceCommand), - SpaceAdmin(SpaceAdminCommand), Project(ProjectCommand), - ProjectMember(ProjectMemberCommand), - ProjectAdmin(ProjectAdminCommand), - Sidecar(SidecarCommand), - Admin(AdminCommand), - #[cfg(feature = "orchestrator")] - Share(ShareCommand), - Subscription(SubscriptionCommand), - - Node(Box), - Worker(WorkerCommand), - Service(ServiceCommand), - Message(MessageCommand), + Policy(PolicyCommand), + Credential(CredentialCommand), Relay(RelayCommand), - - TcpListener(TcpListenerCommand), - TcpConnection(TcpConnectionCommand), TcpOutlet(TcpOutletCommand), TcpInlet(TcpInletCommand), - + KafkaInlet(KafkaInletCommand), + KafkaOutlet(KafkaOutletCommand), #[command(name = "influxdb-inlet")] InfluxDBInlet(InfluxDBInletCommand), #[command(name = "influxdb-outlet")] InfluxDBOutlet(InfluxDBOutletCommand), - #[command(hide = docs::hide())] Rendezvous(RendezvousCommand), + Status(StatusCommand), + Reset(ResetCommand), + Run(RunCommand), + Manpages(ManpagesCommand), + Completion(CompletionCommand), + Environment(EnvironmentCommand), - KafkaInlet(KafkaInletCommand), - KafkaOutlet(KafkaOutletCommand), - - KafkaConsumer(KafkaConsumerCommand), - KafkaProducer(KafkaProducerCommand), - - SecureChannelListener(SecureChannelListenerCommand), - SecureChannel(SecureChannelCommand), - - Vault(VaultCommand), - Identity(IdentityCommand), - Credential(CredentialCommand), - - Authority(AuthorityCommand), + #[cfg(feature = "admin_commands")] + Enroll(EnrollCommand), + #[cfg(feature = "admin_commands")] + Admin(AdminCommand), + #[cfg(feature = "admin_commands")] + SpaceAdmin(SpaceAdminCommand), + #[cfg(feature = "admin_commands")] + ProjectAdmin(ProjectAdminCommand), + #[cfg(feature = "admin_commands")] + ProjectMember(ProjectMemberCommand), - Policy(PolicyCommand), + #[cfg(feature = "admin_commands")] + Sidecar(SidecarCommand), + #[cfg(feature = "admin_commands")] + Subscription(SubscriptionCommand), + #[cfg(feature = "admin_commands")] Lease(LeaseCommand), - Run(RunCommand), - Status(StatusCommand), - Reset(ResetCommand), + #[cfg(feature = "admin_commands")] + Authority(AuthorityCommand), - Completion(CompletionCommand), + #[cfg(feature = "admin_commands")] Markdown(MarkdownCommand), - Manpages(ManpagesCommand), - Environment(EnvironmentCommand), + #[cfg(feature = "advanced_commands")] + Worker(WorkerCommand), + #[cfg(feature = "advanced_commands")] + Service(ServiceCommand), + #[cfg(feature = "advanced_commands")] + Message(MessageCommand), + + #[cfg(feature = "advanced_commands")] + SecureChannelListener(SecureChannelListenerCommand), + #[cfg(feature = "advanced_commands")] + SecureChannel(SecureChannelCommand), + #[cfg(feature = "advanced_commands")] + TcpListener(TcpListenerCommand), + #[cfg(feature = "advanced_commands")] + TcpConnection(TcpConnectionCommand), + #[cfg(feature = "advanced_commands")] FlowControl(FlowControlCommand), + + #[cfg(feature = "advanced_commands")] + KafkaConsumer(KafkaConsumerCommand), + #[cfg(feature = "advanced_commands")] + KafkaProducer(KafkaProducerCommand), + #[cfg(feature = "advanced_commands")] + Share(ShareCommand), } impl OckamSubcommand { /// Run the subcommand pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> { match self { - OckamSubcommand::Enroll(c) => c.run(opts), + OckamSubcommand::Node(c) => c.run(opts), + OckamSubcommand::Vault(c) => c.run(opts), + OckamSubcommand::Identity(c) => c.run(opts), OckamSubcommand::Space(c) => c.run(opts), - OckamSubcommand::SpaceAdmin(c) => c.run(opts), OckamSubcommand::Project(c) => c.run(opts), - OckamSubcommand::ProjectMember(c) => c.run(opts), - OckamSubcommand::ProjectAdmin(c) => c.run(opts), - OckamSubcommand::Admin(c) => c.run(opts), - #[cfg(feature = "orchestrator")] - OckamSubcommand::Share(c) => c.run(opts), - OckamSubcommand::Subscription(c) => c.run(opts), - - OckamSubcommand::Node(c) => c.run(opts), - OckamSubcommand::Worker(c) => c.run(opts), - OckamSubcommand::Service(c) => c.run(opts), - OckamSubcommand::Message(c) => c.run(opts), + OckamSubcommand::Policy(c) => c.run(opts), + OckamSubcommand::Credential(c) => c.run(opts), OckamSubcommand::Relay(c) => c.run(opts), - - OckamSubcommand::KafkaOutlet(c) => c.run(opts), - OckamSubcommand::TcpListener(c) => c.run(opts), - OckamSubcommand::TcpConnection(c) => c.run(opts), OckamSubcommand::TcpOutlet(c) => c.run(opts), OckamSubcommand::TcpInlet(c) => c.run(opts), - + OckamSubcommand::KafkaInlet(c) => c.run(opts), + OckamSubcommand::KafkaOutlet(c) => c.run(opts), OckamSubcommand::InfluxDBInlet(c) => c.run(opts), OckamSubcommand::InfluxDBOutlet(c) => c.run(opts), - OckamSubcommand::Rendezvous(c) => c.run(opts), - - OckamSubcommand::KafkaInlet(c) => c.run(opts), - OckamSubcommand::KafkaConsumer(c) => c.run(opts), - OckamSubcommand::KafkaProducer(c) => c.run(opts), - - OckamSubcommand::SecureChannelListener(c) => c.run(opts), - OckamSubcommand::SecureChannel(c) => c.run(opts), - - OckamSubcommand::Vault(c) => c.run(opts), - OckamSubcommand::Identity(c) => c.run(opts), - OckamSubcommand::Credential(c) => c.run(opts), - - OckamSubcommand::Authority(c) => c.run(opts), - - OckamSubcommand::Policy(c) => c.run(opts), - OckamSubcommand::Lease(c) => c.run(opts), - OckamSubcommand::Run(c) => c.run(opts), OckamSubcommand::Status(c) => c.run(opts), OckamSubcommand::Reset(c) => c.run(opts), - - OckamSubcommand::Completion(c) => c.run(), - OckamSubcommand::Markdown(c) => c.run(), + OckamSubcommand::Run(c) => c.run(opts), OckamSubcommand::Manpages(c) => c.run(), + OckamSubcommand::Completion(c) => c.run(), OckamSubcommand::Environment(c) => c.run(), - OckamSubcommand::FlowControl(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Enroll(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Admin(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::SpaceAdmin(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::ProjectAdmin(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::ProjectMember(c) => c.run(opts), + #[cfg(feature = "admin_commands")] OckamSubcommand::Sidecar(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Subscription(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Lease(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Authority(c) => c.run(opts), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Markdown(c) => c.run(), + + #[cfg(feature = "advanced_commands")] + OckamSubcommand::Worker(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::Service(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::Message(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::SecureChannelListener(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::SecureChannel(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::TcpListener(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::TcpConnection(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::FlowControl(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::KafkaConsumer(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::KafkaProducer(c) => c.run(opts), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::Share(c) => c.run(opts), } } @@ -208,6 +242,7 @@ impl OckamSubcommand { NodeSubcommand::Create(cmd) => !cmd.foreground_args.child_process, _ => false, }, + #[cfg(feature = "admin_commands")] OckamSubcommand::Authority(cmd) => match &cmd.subcommand { AuthoritySubcommand::Create(cmd) => !cmd.child_process, }, @@ -222,6 +257,7 @@ impl OckamSubcommand { NodeSubcommand::Create(cmd) => cmd.foreground_args.child_process, _ => false, }, + #[cfg(feature = "admin_commands")] OckamSubcommand::Authority(cmd) => match &cmd.subcommand { AuthoritySubcommand::Create(cmd) => cmd.child_process, }, @@ -242,6 +278,7 @@ impl OckamSubcommand { } _ => None, }, + #[cfg(feature = "admin_commands")] OckamSubcommand::Authority(cmd) => match &cmd.subcommand { AuthoritySubcommand::Create(cmd) => { if cmd.child_process { @@ -268,6 +305,7 @@ impl OckamSubcommand { } _ => None, }, + #[cfg(feature = "admin_commands")] OckamSubcommand::Authority(cmd) => match &cmd.subcommand { AuthoritySubcommand::Create(cmd) => { if cmd.child_process || !cmd.foreground { @@ -285,48 +323,70 @@ impl OckamSubcommand { pub fn name(&self) -> String { match self { OckamSubcommand::Node(c) => c.name(), - OckamSubcommand::Enroll(c) => c.name(), + OckamSubcommand::Vault(c) => c.name(), + OckamSubcommand::Identity(c) => c.name(), OckamSubcommand::Space(c) => c.name(), - OckamSubcommand::SpaceAdmin(c) => c.name(), OckamSubcommand::Project(c) => c.name(), - OckamSubcommand::ProjectMember(c) => c.name(), - OckamSubcommand::ProjectAdmin(c) => c.name(), - OckamSubcommand::Sidecar(c) => c.name(), - OckamSubcommand::Admin(c) => c.name(), - #[cfg(feature = "orchestrator")] - OckamSubcommand::Share(c) => c.name(), - OckamSubcommand::Subscription(c) => c.name(), - OckamSubcommand::Worker(c) => c.name(), - OckamSubcommand::Service(c) => c.name(), - OckamSubcommand::Message(c) => c.name(), + OckamSubcommand::Policy(c) => c.name(), + OckamSubcommand::Credential(c) => c.name(), OckamSubcommand::Relay(c) => c.name(), - OckamSubcommand::TcpListener(c) => c.name(), - OckamSubcommand::TcpConnection(c) => c.name(), OckamSubcommand::TcpOutlet(c) => c.name(), OckamSubcommand::TcpInlet(c) => c.name(), + OckamSubcommand::KafkaInlet(c) => c.name(), + OckamSubcommand::KafkaOutlet(c) => c.name(), OckamSubcommand::InfluxDBInlet(c) => c.name(), OckamSubcommand::InfluxDBOutlet(c) => c.name(), OckamSubcommand::Rendezvous(c) => c.name(), - OckamSubcommand::KafkaInlet(c) => c.name(), - OckamSubcommand::KafkaOutlet(c) => c.name(), - OckamSubcommand::KafkaConsumer(c) => c.name(), - OckamSubcommand::KafkaProducer(c) => c.name(), - OckamSubcommand::SecureChannelListener(c) => c.name(), - OckamSubcommand::SecureChannel(c) => c.name(), - OckamSubcommand::Vault(c) => c.name(), - OckamSubcommand::Identity(c) => c.name(), - OckamSubcommand::Credential(c) => c.name(), - OckamSubcommand::Authority(c) => c.name(), - OckamSubcommand::Policy(c) => c.name(), - OckamSubcommand::Lease(c) => c.name(), - OckamSubcommand::Run(c) => c.name(), OckamSubcommand::Status(c) => c.name(), OckamSubcommand::Reset(c) => c.name(), - OckamSubcommand::Completion(c) => c.name(), - OckamSubcommand::Markdown(c) => c.name(), + OckamSubcommand::Run(c) => c.name(), OckamSubcommand::Manpages(c) => c.name(), + OckamSubcommand::Completion(c) => c.name(), OckamSubcommand::Environment(c) => c.name(), + + #[cfg(feature = "admin_commands")] + OckamSubcommand::Enroll(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Admin(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::SpaceAdmin(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::ProjectAdmin(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::ProjectMember(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Sidecar(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Subscription(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Lease(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Authority(c) => c.name(), + #[cfg(feature = "admin_commands")] + OckamSubcommand::Markdown(c) => c.name(), + + #[cfg(feature = "advanced_commands")] + OckamSubcommand::Worker(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::Service(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::Message(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::SecureChannelListener(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::SecureChannel(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::TcpListener(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::TcpConnection(c) => c.name(), + #[cfg(feature = "advanced_commands")] OckamSubcommand::FlowControl(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::KafkaConsumer(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::KafkaProducer(c) => c.name(), + #[cfg(feature = "advanced_commands")] + OckamSubcommand::Share(c) => c.name(), } } } diff --git a/implementations/rust/ockam/ockam_command/src/tcp/inlet/create.rs b/implementations/rust/ockam/ockam_command/src/tcp/inlet/create.rs index 2b25d03c751..57b5fd17a3b 100644 --- a/implementations/rust/ockam/ockam_command/src/tcp/inlet/create.rs +++ b/implementations/rust/ockam/ockam_command/src/tcp/inlet/create.rs @@ -92,10 +92,10 @@ pub struct CreateCommand { #[arg(long, display_order = 900, id = "ALIAS", value_parser = alias_parser)] pub alias: Option, - /// Policy expression that will be used for access control to the TCP Inlet. - /// If you don't provide it, the policy set for the "tcp-inlet" resource type will be used. - /// - /// You can check the fallback policy with `ockam policy show --resource-type tcp-inlet`. + #[arg(help = docs::about("\ + Policy expression that will be used for access control to the TCP Inlet. \ + If you don't provide it, the policy set for the \"tcp-inlet\" resource type will be used. \ + \n\nYou can check the fallback policy with `ockam policy show --resource-type tcp-inlet`."))] #[arg( long, visible_alias = "expression", @@ -149,7 +149,6 @@ pub struct CreateCommand { #[arg(long, value_name = "BOOL", default_value_t = false, hide = true)] pub tls: bool, - #[arg(long, value_name = "ROUTE", hide = true)] /// Enable TLS for the TCP Inlet using the provided certificate provider. /// Requires `ockam-tls-certificate` credential attribute. #[arg(long, value_name = "ROUTE", hide = true)] diff --git a/implementations/rust/ockam/ockam_command/src/tcp/outlet/create.rs b/implementations/rust/ockam/ockam_command/src/tcp/outlet/create.rs index 4ad4455a287..80f9e1a4065 100644 --- a/implementations/rust/ockam/ockam_command/src/tcp/outlet/create.rs +++ b/implementations/rust/ockam/ockam_command/src/tcp/outlet/create.rs @@ -57,10 +57,10 @@ pub struct CreateCommand { #[arg(long, display_order = 903, id = "NODE_NAME", value_parser = extract_address_value)] pub at: Option, - /// Policy expression that will be used for access control to the TCP Outlet. - /// If you don't provide it, the policy set for the "tcp-outlet" resource type will be used. - /// - /// You can check the fallback policy with `ockam policy show --resource-type tcp-outlet`. + #[arg(help = docs::about("\ + Policy expression that will be used for access control to the TCP Outlet. \ + If you don't provide it, the policy set for the \"tcp-outlet\" resource type will be used. \ + \n\nYou can check the fallback policy with `ockam policy show --resource-type tcp-outlet`"))] #[arg( long, visible_alias = "expression", diff --git a/implementations/rust/ockam/ockam_command/src/util/mod.rs b/implementations/rust/ockam/ockam_command/src/util/mod.rs index 89a3ae9c4d3..c825eba9d26 100644 --- a/implementations/rust/ockam/ockam_command/src/util/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/util/mod.rs @@ -21,10 +21,13 @@ use tracing::{debug, error}; use crate::{CommandGlobalOpts, Result}; +#[allow(unused)] pub mod api; pub mod exitcode; pub mod foreground_args; +#[allow(unused)] pub mod parsers; +#[allow(unused)] pub mod validators; pub fn local_cmd(res: miette::Result<()>) -> miette::Result<()> { diff --git a/implementations/rust/ockam/ockam_command/src/version.rs b/implementations/rust/ockam/ockam_command/src/version.rs index 79735659827..81efc240122 100644 --- a/implementations/rust/ockam/ockam_command/src/version.rs +++ b/implementations/rust/ockam/ockam_command/src/version.rs @@ -6,6 +6,8 @@ use ockam_api::output::Output; use serde::Serialize; use std::fmt::Display; +use crate::BIN_NAME; + #[derive(Debug, Clone, Serialize)] pub(crate) struct Version { #[serde(skip)] @@ -54,7 +56,7 @@ impl Version { color_primary(&self.hash).to_string(), ) }; - let msg = format!("ockam {version}\ncompiled from {hash}"); + let msg = format!("{BIN_NAME} {version}\ncompiled from {hash}"); if self.multiline { Ok(msg) } else {