diff --git a/Cargo.lock b/Cargo.lock index f417affe..436735a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,6 +193,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -1191,6 +1192,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "terminal_size" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "tinystr" version = "0.7.6" diff --git a/Cargo.toml b/Cargo.toml index c23670c5..22f348c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] async-stream = "0.3.5" async-trait = "0.1.68" -clap = { version = "4.3", features = ["derive"] } +clap = { version = "4.3", features = ["derive", "wrap_help"] } clap_complete = "4.3" clicolors-control = "1" console = "0.15.5" diff --git a/src/cli.rs b/src/cli.rs index 9068da6a..abb668a8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -91,80 +91,80 @@ impl std::fmt::Display for ColorWhen { } } +/// NixOS deployment tool #[derive(Parser)] #[command( name = "Colmena", bin_name = "colmena", author = "Zhaofeng Li ", version = env!("CARGO_PKG_VERSION"), - about = "NixOS deployment tool", long_about = LONG_ABOUT, + max_term_width = 100, )] struct Opts { + /// Path to a Hive expression, a flake.nix, or a Nix Flake URI #[arg( short = 'f', long, value_name = "CONFIG", - help = "Path to a Hive expression, a flake.nix, or a Nix Flake URI", long_help = CONFIG_HELP, display_order = HELP_ORDER_FIRST, global = true, )] config: Option, - #[arg( - long, - help = "Show debug information for Nix commands", - long_help = "Passes --show-trace to Nix commands", - global = true - )] + + /// Show debug information for Nix commands + /// + /// Passes --show-trace to Nix commands + #[arg(long, global = true)] show_trace: bool, - #[arg( - long, - help = "Allow impure expressions", - long_help = "Passes --impure to Nix commands", - global = true - )] + + /// Allow impure expressions + /// + /// Passes --impure to Nix commands + #[arg(long, global = true)] impure: bool, + + /// Passes an arbitrary option to Nix commands + /// + /// This only works when building locally. #[arg( long, - help = "Passes an arbitrary option to Nix commands", - long_help = r#"Passes arbitrary options to Nix commands - -This only works when building locally. -"#, global = true, num_args = 2, value_names = ["NAME", "VALUE"], )] nix_option: Vec, - #[arg( - long, - default_value_t, - help = "Use direct flake evaluation (experimental)", - long_help = r#"If enabled, flakes will be evaluated using `nix eval`. This requires the flake to depend on Colmena as an input and expose a compatible `colmenaHive` output: - - outputs = { self, colmena, ... }: { - colmenaHive = colmena.lib.makeHive self.outputs.colmena; - colmena = ...; - }; -This is an experimental feature."#, - global = true - )] + /// Use direct flake evaluation (experimental) + /// + /// If enabled, flakes will be evaluated using `nix eval`. This requires the flake to depend on + /// Colmena as an input and expose a compatible `colmenaHive` output: + /// + /// outputs = { self, colmena, ... }: { + /// colmenaHive = colmena.lib.makeHive self.outputs.colmena; + /// colmena = ...; + /// }; + /// + /// This is an experimental feature. + #[arg(long, default_value_t, global = true)] experimental_flake_eval: bool, + + /// When to colorize the output + /// + /// By default, Colmena enables colorized output when the terminal supports it. + /// + /// It's also possible to specify the preference using environment variables. See + /// . #[arg( long, value_name = "WHEN", default_value_t, global = true, display_order = HELP_ORDER_LOW, - help = "When to colorize the output", - long_help = r#"When to colorize the output. By default, Colmena enables colorized output when the terminal supports it. - -It's also possible to specify the preference using environment variables. See . -"#, )] color: ColorWhen, + #[command(subcommand)] command: Command, } @@ -172,45 +172,47 @@ It's also possible to specify the preference using environment variables. See , - #[arg( - long, - value_name = "COMMAND", - hide = true, - help = "Removed: Configure deployment.privilegeEscalationCommand in node configuration" - )] + + /// Removed: Configure deployment.privilegeEscalationCommand in node configuration + #[arg(long, value_name = "COMMAND", hide = true)] sudo_command: Option, } diff --git a/src/command/eval.rs b/src/command/eval.rs index 3a7259f8..4cd9951c 100644 --- a/src/command/eval.rs +++ b/src/command/eval.rs @@ -5,30 +5,26 @@ use clap::Args; use crate::error::ColmenaError; use crate::nix::Hive; +/// Evaluate an expression using the complete configuration +/// +/// Your expression should take an attribute set with keys `pkgs`, `lib` and `nodes` (like a NixOS +/// module) and return a JSON-serializable value. For example, to retrieve the configuration of one +/// node, you may write something like: +/// +/// { nodes, ... }: nodes.node-a.config.networking.hostName #[derive(Debug, Args)] -#[command( - name = "eval", - alias = "introspect", - about = "Evaluate an expression using the complete configuration", - long_about = r#"Evaluate an expression using the complete configuration - -Your expression should take an attribute set with keys `pkgs`, `lib` and `nodes` (like a NixOS module) and return a JSON-serializable value. - -For example, to retrieve the configuration of one node, you may write something like: - - { nodes, ... }: nodes.node-a.config.networking.hostName -"# -)] +#[command(name = "eval", alias = "introspect")] pub struct Opts { - #[arg(short = 'E', value_name = "EXPRESSION", help = "The Nix expression")] + /// The Nix expression + #[arg(short = 'E', value_name = "EXPRESSION")] expression: Option, - #[arg(long, help = "Actually instantiate the expression")] + + /// Actually instantiate the expression + #[arg(long)] instantiate: bool, - #[arg( - value_name = "FILE", - help = "The .nix file containing the expression", - conflicts_with("expression") - )] + + /// The .nix file containing the expression + #[arg(value_name = "FILE", conflicts_with("expression"))] expression_file: Option, } diff --git a/src/command/exec.rs b/src/command/exec.rs index 5f3616a3..59278ac5 100644 --- a/src/command/exec.rs +++ b/src/command/exec.rs @@ -13,42 +13,34 @@ use crate::nix::Hive; use crate::progress::SimpleProgressOutput; use crate::util; +/// Run a command on remote machines #[derive(Debug, Args)] -#[command(name = "exec", about = "Run a command on remote machines")] +#[command(name = "exec")] pub struct Opts { - #[arg( - short, - long, - default_value_t = 0, - value_name = "LIMIT", - help = "Deploy parallelism limit", - long_help = r#"Limits the maximum number of hosts to run the command in parallel. - -In `colmena exec`, the parallelism limit is disabled (0) by default. -"# - )] + /// Deploy parallelism limit + /// + /// Limits the maximum number of hosts to run the command in parallel. + /// + /// In `colmena exec`, the parallelism limit is disabled (0) by default. + #[arg(short, long, default_value_t = 0, value_name = "LIMIT")] parallel: usize, - #[arg( - short, - long, - help = "Be verbose", - long_help = "Deactivates the progress spinner and prints every line of output." - )] + + /// Be verbose + /// + /// Deactivates the progress spinner and prints every line of output. + #[arg(short, long)] verbose: bool, + #[command(flatten)] nodes: NodeFilterOpts, - #[arg( - trailing_var_arg = true, - required = true, - value_name = "COMMAND", - help = "Command", - long_help = r#"Command to run - -It's recommended to use -- to separate Colmena options from the command to run. For example: - - colmena exec --on @routers -- tcpdump -vni any ip[9] == 89 -"# - )] + + /// Command to run + /// + /// It's recommended to use -- to separate Colmena options from the command to run. For + /// example: + /// + /// colmena exec --on @routers -- tcpdump -vni any ip[9] == 89 + #[arg(trailing_var_arg = true, required = true, value_name = "COMMAND")] command: Vec, } diff --git a/src/nix/node_filter.rs b/src/nix/node_filter.rs index 6e459fa2..6dc6a1b1 100644 --- a/src/nix/node_filter.rs +++ b/src/nix/node_filter.rs @@ -12,19 +12,16 @@ use super::{ColmenaError, ColmenaResult, NodeConfig, NodeName}; #[derive(Debug, Default, Args)] pub struct NodeFilterOpts { - #[arg( - long, - value_name = "NODES", - help = "Node selector", - long_help = r#"Select a list of nodes to deploy to. - -The list is comma-separated and globs are supported. To match tags, prepend the filter by @. Valid examples: - -- host1,host2,host3 -- edge-* -- edge-*,core-* -- @a-tag,@tags-can-have-*"# - )] + /// Node selector + /// + /// Select a list of nodes to deploy to. The list is comma-separated and globs are supported. + /// To match tags, prepend the filter by @. Valid examples: + /// + /// - host1,host2,host3 + /// - edge-* + /// - edge-*,core-* + /// - @a-tag,@tags-can-have-* + #[arg(long, value_name = "NODES")] pub on: Option, }