Initial support for clap derive up to the top

This commit is contained in:
i1i1 2023-08-10 17:57:59 +03:00 committed by Zhaofeng Li
parent 1ad9301c62
commit 935aa77e53
14 changed files with 239 additions and 382 deletions

21
Cargo.lock generated
View file

@ -166,9 +166,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.2.7" version = "4.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -177,31 +177,30 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.2.7" version = "4.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"bitflags",
"clap_lex", "clap_lex",
"strsim", "strsim",
] ]
[[package]] [[package]]
name = "clap_complete" name = "clap_complete"
version = "4.2.3" version = "4.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1594fe2312ec4abf402076e407628f5c313e54c32ade058521df4ee34ecac8a8" checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce"
dependencies = [ dependencies = [
"clap", "clap",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.2.0" version = "4.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -211,9 +210,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.4.1" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]] [[package]]
name = "clicolors-control" name = "clicolors-control"

View file

@ -10,8 +10,8 @@ edition = "2021"
async-stream = "0.3.5" async-stream = "0.3.5"
async-trait = "0.1.68" async-trait = "0.1.68"
atty = "0.2" atty = "0.2"
clap = { version = "4.2.7", features = ["derive"] } clap = { version = "4.3", features = ["derive"] }
clap_complete = "4.2.3" clap_complete = "4.3"
clicolors-control = "1" clicolors-control = "1"
console = "0.15.5" console = "0.15.5"
const_format = "0.2.30" const_format = "0.2.30"

View file

@ -2,15 +2,16 @@
use std::env; use std::env;
use clap::{ use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
builder::PossibleValue, value_parser, Arg, ArgAction, ArgMatches, ColorChoice,
Command as ClapCommand, ValueEnum,
};
use clap_complete::Shell; use clap_complete::Shell;
use const_format::{concatcp, formatcp}; use const_format::{concatcp, formatcp};
use env_logger::fmt::WriteStyle; use env_logger::fmt::WriteStyle;
use crate::{command, nix::HivePath}; use crate::{
command,
error::ColmenaResult,
nix::{Hive, HivePath},
};
/// Base URL of the manual, without the trailing slash. /// Base URL of the manual, without the trailing slash.
const MANUAL_URL_BASE: &str = "https://colmena.cli.rs"; const MANUAL_URL_BASE: &str = "https://colmena.cli.rs";
@ -66,33 +67,11 @@ const HELP_ORDER_FIRST: usize = 100;
/// Display order in `--help` for arguments that are not very important. /// Display order in `--help` for arguments that are not very important.
const HELP_ORDER_LOW: usize = 2000; const HELP_ORDER_LOW: usize = 2000;
macro_rules! register_command {
($module:ident, $app:ident) => {
$app = $app.subcommand(command::$module::subcommand());
};
}
macro_rules! handle_command {
($module:ident, $matches:ident) => {
if let Some(sub_matches) = $matches.subcommand_matches(stringify!($module)) {
crate::troubleshooter::run_wrapped(&$matches, &sub_matches, command::$module::run)
.await;
return;
}
};
($name:expr, $module:ident, $matches:ident) => {
if let Some(sub_matches) = $matches.subcommand_matches($name) {
crate::troubleshooter::run_wrapped(&$matches, &sub_matches, command::$module::run)
.await;
return;
}
};
}
/// When to display color. /// When to display color.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, ValueEnum)]
enum ColorWhen { enum ColorWhen {
/// Detect automatically. /// Detect automatically.
#[default]
Auto, Auto,
/// Always display colors. /// Always display colors.
@ -102,149 +81,199 @@ enum ColorWhen {
Never, Never,
} }
impl ValueEnum for ColorWhen { impl std::fmt::Display for ColorWhen {
fn value_variants<'a>() -> &'a [Self] { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
&[Self::Auto, Self::Always, Self::Never] f.write_str(match self {
} Self::Auto => "auto",
Self::Always => "always",
fn to_possible_value<'a>(&self) -> Option<PossibleValue> { Self::Never => "never",
match self { })
Self::Auto => Some(PossibleValue::new("auto")),
Self::Always => Some(PossibleValue::new("always")),
Self::Never => Some(PossibleValue::new("never")),
}
} }
} }
pub fn build_cli(include_internal: bool) -> ClapCommand { #[derive(Parser)]
let version = env!("CARGO_PKG_VERSION"); #[command(
let mut app = ClapCommand::new("Colmena") name = "Colmena",
.bin_name("colmena") bin_name = "colmena",
.version(version) author = "Zhaofeng Li <hello@zhaofeng.li>",
.author("Zhaofeng Li <hello@zhaofeng.li>") version = env!("CARGO_PKG_VERSION"),
.about("NixOS deployment tool") about = "NixOS deployment tool",
.long_about(LONG_ABOUT) long_about = LONG_ABOUT,
.arg_required_else_help(true) )]
.arg(Arg::new("config") struct Opts {
.short('f') #[arg(
.long("config") short = 'f',
.value_name("CONFIG") long,
.help("Path to a Hive expression, a flake.nix, or a Nix Flake URI") value_name = "CONFIG",
.long_help(Some(CONFIG_HELP)) help = "Path to a Hive expression, a flake.nix, or a Nix Flake URI",
.display_order(HELP_ORDER_FIRST) long_help = CONFIG_HELP,
.global(true) display_order = HELP_ORDER_FIRST,
.value_parser(value_parser!(HivePath))) global = true,
.arg(Arg::new("show-trace") )]
.long("show-trace") config: Option<HivePath>,
.help("Show debug information for Nix commands") #[arg(
.long_help("Passes --show-trace to Nix commands") long,
.global(true) help = "Show debug information for Nix commands",
.num_args(0)) long_help = "Passes --show-trace to Nix commands",
.arg(Arg::new("impure") global = true
.long("impure") )]
.help("Allow impure expressions") show_trace: bool,
.long_help("Passes --impure to Nix commands") #[arg(
.global(true) long,
.num_args(0)) help = "Allow impure expressions",
.arg(Arg::new("nix-option") long_help = "Passes --impure to Nix commands",
.long("nix-option") global = true
.help("Passes an arbitrary option to Nix commands") )]
.long_help(r#"Passes arbitrary options to Nix commands impure: bool,
#[arg(
long,
value_parser = crate::util::parse_key_val::<String, String>,
help = "Passes an arbitrary option to Nix commands",
long_help = r#"Passes arbitrary options to Nix commands
This only works when building locally. This only works when building locally.
"#) "#,
.global(true) global = true,
.num_args(2) num_args = 2,
.value_names(["NAME", "VALUE"]) value_names = ["NAME, VALUE"],
.action(ArgAction::Append)) )]
.arg(Arg::new("color") nix_option: Vec<(String, String)>,
.long("color") #[arg(
.help("When to colorize the output") long,
.long_help(r#"When to colorize the output. By default, Colmena enables colorized output when the terminal supports it. 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 <https://bixense.com/clicolors>. It's also possible to specify the preference using environment variables. See <https://bixense.com/clicolors>.
"#) "#,
.display_order(HELP_ORDER_LOW) )]
.value_name("WHEN") color: ColorWhen,
.value_parser(value_parser!(ColorWhen)) #[command(subcommand)]
.default_value("auto") command: Command,
.global(true)); }
if include_internal { #[derive(Subcommand)]
app = app.subcommand( enum Command {
ClapCommand::new("gen-completions") Apply(command::apply::Opts),
.about("Generate shell auto-completion files (Internal)")
.hide(true)
.arg(
Arg::new("shell")
.index(1)
.value_parser(value_parser!(Shell))
.required(true)
.num_args(1),
),
);
// TODO: handle deprecated alias
#[cfg(debug_assertions)]
register_command!(test_progress, app);
}
register_command!(apply, app);
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
register_command!(apply_local, app); ApplyLocal(command::apply_local::Opts),
register_command!(build, app); Build(command::build::Opts),
register_command!(eval, app); Eval(command::eval::Opts),
register_command!(upload_keys, app); UploadKeys(command::upload_keys::Opts),
register_command!(exec, app); Exec(command::exec::Opts),
register_command!(repl, app); Repl(command::repl::Opts),
register_command!(nix_info, app); NixInfo(command::nix_info::Opts),
#[cfg(debug_assertions)]
#[command(about = "Run progress spinner tests", hide = true)]
TestProgress,
#[command(about = "Generate shell auto-completion files (Internal)", hide = true)]
GenCompletions {
shell: Shell,
},
}
// This does _not_ take the --color flag into account (haven't async fn get_hive(opts: &Opts) -> ColmenaResult<Hive> {
// parsed yet), only the CLICOLOR environment variable. let path = match &opts.config {
if clicolors_control::colors_enabled() { Some(path) => path.clone(),
app.color(ColorChoice::Always) None => {
} else { // traverse upwards until we find hive.nix
app let mut cur = std::env::current_dir()?;
let mut file_path = None;
loop {
let flake = cur.join("flake.nix");
if flake.is_file() {
file_path = Some(flake);
break;
}
let legacy = cur.join("hive.nix");
if legacy.is_file() {
file_path = Some(legacy);
break;
}
match cur.parent() {
Some(parent) => {
cur = parent.to_owned();
}
None => {
break;
}
}
}
if file_path.is_none() {
log::error!(
"Could not find `hive.nix` or `flake.nix` in {:?} or any parent directory",
std::env::current_dir()?
);
}
HivePath::from_path(file_path.unwrap()).await?
}
};
match &path {
HivePath::Legacy(p) => {
log::info!("Using configuration: {}", p.to_string_lossy());
}
HivePath::Flake(flake) => {
log::info!("Using flake: {}", flake.uri());
}
} }
let mut hive = Hive::new(path).await?;
if opts.show_trace {
hive.set_show_trace(true);
}
if opts.impure {
hive.set_impure(true);
}
for (name, value) in opts.nix_option.iter().cloned() {
hive.add_nix_option(name, value);
}
Ok(hive)
} }
pub async fn run() { pub async fn run() {
let mut app = build_cli(true); let opts = Opts::parse();
let matches = app.clone().get_matches();
set_color_pref(matches.get_one("color").unwrap()); set_color_pref(&opts.color);
init_logging(); init_logging();
handle_command!(apply, matches); let hive = get_hive(&opts).await.expect("Failed to get flake or hive");
#[cfg(target_os = "linux")]
handle_command!("apply-local", apply_local, matches);
handle_command!(build, matches);
handle_command!(eval, matches);
handle_command!("upload-keys", upload_keys, matches);
handle_command!(exec, matches);
handle_command!(repl, matches);
handle_command!("nix-info", nix_info, matches);
#[cfg(debug_assertions)] use crate::troubleshooter::run_wrapped as r;
handle_command!("test-progress", test_progress, matches);
if let Some(args) = matches.subcommand_matches("gen-completions") { match opts.command {
return gen_completions(args); Command::Apply(args) => r(command::apply::run(hive, args)).await,
Command::ApplyLocal(args) => r(command::apply_local::run(hive, args)).await,
Command::Eval(args) => r(command::eval::run(hive, args)).await,
Command::Exec(args) => r(command::exec::run(hive, args)).await,
Command::NixInfo(args) => r(command::nix_info::run(args)).await,
Command::Repl(args) => r(command::repl::run(hive, args)).await,
Command::TestProgress => r(command::test_progress::run()).await,
Command::Build(_args) => todo!("This is an alias for `colmena apply build`"),
Command::UploadKeys(_opts) => todo!("This is an alias for `colmena apply upload-keys`"),
Command::GenCompletions { shell } => print_completions(shell, &mut Opts::command()),
} }
// deprecated alias
handle_command!("introspect", eval, matches);
app.print_long_help().unwrap();
println!();
} }
fn gen_completions(args: &ArgMatches) { fn print_completions(shell: Shell, cmd: &mut clap::Command) {
let mut app = build_cli(false); clap_complete::generate(
let shell = args.get_one::<Shell>("shell").unwrap().to_owned(); shell,
cmd,
clap_complete::generate(shell, &mut app, "colmena", &mut std::io::stdout()); cmd.get_name().to_string(),
&mut std::io::stdout(),
);
} }
fn set_color_pref(when: &ColorWhen) { fn set_color_pref(when: &ColorWhen) {
@ -273,13 +302,3 @@ fn init_logging() {
.write_style(style) .write_style(style)
.init(); .init();
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cli_debug_assert() {
build_cli(true).debug_assert()
}
}

View file

@ -1,16 +1,16 @@
use std::env; use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use clap::{builder::ArgPredicate, ArgMatches, Args, Command as ClapCommand, FromArgMatches}; use clap::{builder::ArgPredicate, Args};
use crate::error::ColmenaError;
use crate::nix::{ use crate::nix::{
deployment::{Deployment, EvaluationNodeLimit, EvaluatorType, Goal, Options, ParallelismLimit}, deployment::{Deployment, EvaluationNodeLimit, EvaluatorType, Goal, Options, ParallelismLimit},
node_filter::NodeFilterOpts, node_filter::NodeFilterOpts,
Hive,
}; };
use crate::progress::SimpleProgressOutput; use crate::progress::SimpleProgressOutput;
use crate::{error::ColmenaError, nix::hive::HiveArgs};
#[derive(Debug, Args)] #[derive(Debug, Args)]
pub struct DeployOpts { pub struct DeployOpts {
#[arg( #[arg(
@ -117,7 +117,7 @@ This is an experimental feature."#
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(name = "apply", about = "Apply configurations on remote machines")] #[command(name = "apply", about = "Apply configurations on remote machines")]
struct Opts { pub struct Opts {
#[arg( #[arg(
help = "Deployment goal", help = "Deployment goal",
long_help = r#"The goal of the deployment. long_help = r#"The goal of the deployment.
@ -139,17 +139,7 @@ Same as the targets for switch-to-configuration, with the following extra pseudo
node_filter: NodeFilterOpts, node_filter: NodeFilterOpts,
} }
pub fn subcommand() -> ClapCommand { pub async fn run(hive: Hive, opts: Opts) -> Result<(), ColmenaError> {
Opts::augment_args(ClapCommand::new("apply"))
}
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
let hive = HiveArgs::from_arg_matches(local_args)
.unwrap()
.into_hive()
.await
.unwrap();
let ssh_config = env::var("SSH_CONFIG_FILE").ok().map(PathBuf::from); let ssh_config = env::var("SSH_CONFIG_FILE").ok().map(PathBuf::from);
let Opts { let Opts {
@ -170,7 +160,7 @@ pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(
evaluator, evaluator,
}, },
node_filter, node_filter,
} = Opts::from_arg_matches(local_args).expect("Failed to parse `apply` args"); } = opts;
if node_filter.on.is_none() && goal != Goal::Build { if node_filter.on.is_none() && goal != Goal::Build {
// User did not specify node, we should check meta and see rules // User did not specify node, we should check meta and see rules

View file

@ -1,13 +1,13 @@
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use clap::{ArgMatches, Args, Command as ClapCommand, FromArgMatches}; use clap::Args;
use tokio::fs; use tokio::fs;
use crate::error::ColmenaError; use crate::error::ColmenaError;
use crate::nix::deployment::{Deployment, Goal, Options, TargetNode}; use crate::nix::deployment::{Deployment, Goal, Options, TargetNode};
use crate::nix::hive::HiveArgs; use crate::nix::Hive;
use crate::nix::{host::Local as LocalHost, NodeName}; use crate::nix::{host::Local as LocalHost, NodeName};
use crate::progress::SimpleProgressOutput; use crate::progress::SimpleProgressOutput;
@ -51,25 +51,19 @@ By default, Colmena will deploy keys set in `deployment.keys` before activating
help = "Removed: Configure deployment.privilegeEscalationCommand in node configuration" help = "Removed: Configure deployment.privilegeEscalationCommand in node configuration"
)] )]
sudo_command: Option<String>, sudo_command: Option<String>,
#[command(flatten)]
hive_args: HiveArgs,
} }
pub fn subcommand() -> ClapCommand { pub async fn run(
Opts::augment_args(ClapCommand::new("apply-local")) hive: Hive,
} Opts {
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
let Opts {
goal, goal,
sudo, sudo,
verbose, verbose,
no_keys, no_keys,
node, node,
sudo_command, sudo_command,
hive_args, }: Opts,
} = Opts::from_arg_matches(local_args).expect("Failed to parse `apply-local` options."); ) -> Result<(), ColmenaError> {
if sudo_command.is_some() { if sudo_command.is_some() {
log::error!("--sudo-command has been removed. Please configure it in deployment.privilegeEscalationCommand in the node configuration."); log::error!("--sudo-command has been removed. Please configure it in deployment.privilegeEscalationCommand in the node configuration.");
quit::with_code(1); quit::with_code(1);
@ -97,10 +91,6 @@ pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(
} }
} }
let hive = hive_args
.into_hive()
.await
.expect("Failed to get hive from arguments");
let hostname = NodeName::new(node.unwrap_or_else(|| { let hostname = NodeName::new(node.unwrap_or_else(|| {
hostname::get() hostname::get()
.expect("Could not get hostname") .expect("Could not get hostname")

View file

@ -1,4 +1,4 @@
use clap::{Args, Command as ClapCommand}; use clap::Args;
use crate::nix::Goal; use crate::nix::Goal;
@ -19,7 +19,3 @@ pub struct Opts {
#[arg(hide = true, default_value_t = Goal::Build)] #[arg(hide = true, default_value_t = Goal::Build)]
goal: Goal, goal: Goal,
} }
pub fn subcommand() -> ClapCommand {
Opts::augment_args(ClapCommand::new("build"))
}

View file

@ -1,9 +1,9 @@
use std::path::PathBuf; use std::path::PathBuf;
use clap::{ArgMatches, Args, Command as ClapCommand, FromArgMatches}; use clap::Args;
use crate::error::ColmenaError; use crate::error::ColmenaError;
use crate::nix::hive::HiveArgs; use crate::nix::Hive;
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command( #[command(
@ -32,29 +32,15 @@ pub struct Opts {
expression_file: Option<PathBuf>, expression_file: Option<PathBuf>,
} }
pub fn subcommand() -> ClapCommand { pub async fn run(
Opts::augment_args(ClapCommand::new("eval")) hive: Hive,
} Opts {
pub async fn run(global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
if let Some("introspect") = global_args.subcommand_name() {
log::warn!(
"`colmena introspect` has been renamed to `colmena eval`. Please update your scripts."
);
}
let hive = HiveArgs::from_arg_matches(local_args)
.unwrap()
.into_hive()
.await
.unwrap();
let Opts {
instantiate,
expression, expression,
instantiate,
expression_file, expression_file,
} = Opts::from_arg_matches(local_args).expect("Failed to parse args"); }: Opts,
) -> Result<(), ColmenaError> {
// TODO: check for deprecated alias
let expression = expression_file let expression = expression_file
.map(|path| { .map(|path| {
format!( format!(

View file

@ -2,20 +2,20 @@ use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use clap::{ArgMatches, Args, Command as ClapCommand, FromArgMatches}; use clap::Args;
use futures::future::join_all; use futures::future::join_all;
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
use crate::error::ColmenaError; use crate::error::ColmenaError;
use crate::job::{JobMonitor, JobState, JobType}; use crate::job::{JobMonitor, JobState, JobType};
use crate::nix::hive::HiveArgs;
use crate::nix::node_filter::NodeFilterOpts; use crate::nix::node_filter::NodeFilterOpts;
use crate::nix::Hive;
use crate::progress::SimpleProgressOutput; use crate::progress::SimpleProgressOutput;
use crate::util; use crate::util;
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(name = "exec", about = "Run a command on remote machines")] #[command(name = "exec", about = "Run a command on remote machines")]
struct Opts { pub struct Opts {
#[arg( #[arg(
short, short,
long, long,
@ -52,24 +52,16 @@ It's recommended to use -- to separate Colmena options from the command to run.
command: Vec<String>, command: Vec<String>,
} }
pub fn subcommand() -> ClapCommand { pub async fn run(
Opts::augment_args(ClapCommand::new("exec")) hive: Hive,
} Opts {
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
let hive = HiveArgs::from_arg_matches(local_args)
.unwrap()
.into_hive()
.await
.unwrap();
let ssh_config = env::var("SSH_CONFIG_FILE").ok().map(PathBuf::from);
let Opts {
parallel, parallel,
verbose, verbose,
nodes, nodes,
command, command,
} = Opts::from_arg_matches(local_args).unwrap(); }: Opts,
) -> Result<(), ColmenaError> {
let ssh_config = env::var("SSH_CONFIG_FILE").ok().map(PathBuf::from);
let mut targets = hive.select_nodes(nodes.on, ssh_config, true).await?; let mut targets = hive.select_nodes(nodes.on, ssh_config, true).await?;

View file

@ -1,4 +1,4 @@
use clap::{ArgMatches, Args, Command, FromArgMatches}; use clap::Args;
use crate::error::ColmenaError; use crate::error::ColmenaError;
use crate::nix::evaluator::nix_eval_jobs::get_pinned_nix_eval_jobs; use crate::nix::evaluator::nix_eval_jobs::get_pinned_nix_eval_jobs;
@ -11,12 +11,7 @@ use crate::nix::NixCheck;
)] )]
pub struct Opts {} pub struct Opts {}
pub fn subcommand() -> Command { pub async fn run(_: Opts) -> Result<(), ColmenaError> {
Opts::augment_args(Command::new("nix-info"))
}
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
let Opts {} = Opts::from_arg_matches(local_args).unwrap();
let check = NixCheck::detect().await; let check = NixCheck::detect().await;
check.print_version_info(); check.print_version_info();
check.print_flakes_info(false); check.print_flakes_info(false);

View file

@ -1,12 +1,12 @@
use std::io::Write; use std::io::Write;
use clap::{ArgMatches, Args, Command as ClapCommand, FromArgMatches}; use clap::Args;
use tempfile::Builder as TempFileBuilder; use tempfile::Builder as TempFileBuilder;
use tokio::process::Command; use tokio::process::Command;
use crate::error::ColmenaError; use crate::error::ColmenaError;
use crate::nix::hive::HiveArgs;
use crate::nix::info::NixCheck; use crate::nix::info::NixCheck;
use crate::nix::Hive;
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command( #[command(
@ -20,22 +20,10 @@ attribute set."#
)] )]
pub struct Opts {} pub struct Opts {}
pub fn subcommand() -> ClapCommand { pub async fn run(hive: Hive, _: Opts) -> Result<(), ColmenaError> {
Opts::augment_args(ClapCommand::new("repl"))
}
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
let Opts {} = Opts::from_arg_matches(local_args).unwrap();
let nix_check = NixCheck::detect().await; let nix_check = NixCheck::detect().await;
let nix_version = nix_check.version().expect("Could not detect Nix version"); let nix_version = nix_check.version().expect("Could not detect Nix version");
let hive = HiveArgs::from_arg_matches(local_args)
.unwrap()
.into_hive()
.await
.unwrap();
let expr = hive.get_repl_expression(); let expr = hive.get_repl_expression();
let mut expr_file = TempFileBuilder::new() let mut expr_file = TempFileBuilder::new()

View file

@ -1,6 +1,5 @@
use std::time::Duration; use std::time::Duration;
use clap::{ArgMatches, Command as ClapCommand};
use tokio::time; use tokio::time;
use crate::error::{ColmenaError, ColmenaResult}; use crate::error::{ColmenaError, ColmenaResult};
@ -14,13 +13,7 @@ macro_rules! node {
}; };
} }
pub fn subcommand() -> ClapCommand { pub async fn run() -> Result<(), ColmenaError> {
ClapCommand::new("test-progress")
.about("Run progress spinner tests")
.hide(true)
}
pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), ColmenaError> {
let mut output = SpinnerOutput::new(); let mut output = SpinnerOutput::new();
let (monitor, meta) = JobMonitor::new(output.get_sender()); let (monitor, meta) = JobMonitor::new(output.get_sender());

View file

@ -1,4 +1,4 @@
use clap::{Args, Command as ClapCommand}; use clap::Args;
use crate::nix::Goal; use crate::nix::Goal;
@ -19,7 +19,3 @@ pub struct Opts {
#[arg(hide = true, default_value_t = Goal::Build)] #[arg(hide = true, default_value_t = Goal::Build)]
goal: Goal, goal: Goal,
} }
pub fn subcommand() -> ClapCommand {
Opts::augment_args(ClapCommand::new("upload-keys"))
}

View file

@ -8,7 +8,6 @@ use std::convert::AsRef;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use clap::Args;
use tokio::process::Command; use tokio::process::Command;
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
use validator::Validate; use validator::Validate;
@ -23,88 +22,6 @@ use crate::job::JobHandle;
use crate::util::{CommandExecution, CommandExt}; use crate::util::{CommandExecution, CommandExt};
use assets::Assets; use assets::Assets;
#[derive(Debug, Args)]
pub struct HiveArgs {
#[arg(short = 'f', long, value_name = "CONFIG")]
config: Option<HivePath>,
#[arg(long)]
show_trace: bool,
#[arg(long)]
impure: bool,
#[arg(long, value_parser = crate::util::parse_key_val::<String, String>)]
nix_option: Vec<(String, String)>,
}
impl HiveArgs {
pub async fn into_hive(self) -> ColmenaResult<Hive> {
let path = match self.config {
Some(path) => path,
None => {
// traverse upwards until we find hive.nix
let mut cur = std::env::current_dir()?;
let mut file_path = None;
loop {
let flake = cur.join("flake.nix");
if flake.is_file() {
file_path = Some(flake);
break;
}
let legacy = cur.join("hive.nix");
if legacy.is_file() {
file_path = Some(legacy);
break;
}
match cur.parent() {
Some(parent) => {
cur = parent.to_owned();
}
None => {
break;
}
}
}
if file_path.is_none() {
log::error!(
"Could not find `hive.nix` or `flake.nix` in {:?} or any parent directory",
std::env::current_dir()?
);
}
HivePath::from_path(file_path.unwrap()).await?
}
};
match &path {
HivePath::Legacy(p) => {
log::info!("Using configuration: {}", p.to_string_lossy());
}
HivePath::Flake(flake) => {
log::info!("Using flake: {}", flake.uri());
}
}
let mut hive = Hive::new(path).await?;
if self.show_trace {
hive.set_show_trace(true);
}
if self.impure {
hive.set_impure(true);
}
for (name, value) in self.nix_option {
hive.add_nix_option(name, value);
}
Ok(hive)
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum HivePath { pub enum HivePath {
/// A Nix Flake. /// A Nix Flake.

View file

@ -11,27 +11,23 @@ use snafu::ErrorCompat;
use crate::error::ColmenaError; use crate::error::ColmenaError;
/// Runs a closure and tries to troubleshoot if it returns an error. /// Runs a closure and tries to troubleshoot if it returns an error.
pub async fn run_wrapped<'a, F, U, T>( pub async fn run_wrapped<'a, F, T>(f: F) -> T
global_args: &'a ArgMatches,
local_args: &'a ArgMatches,
f: U,
) -> T
where where
U: FnOnce(&'a ArgMatches, &'a ArgMatches) -> F,
F: Future<Output = Result<T, ColmenaError>>, F: Future<Output = Result<T, ColmenaError>>,
{ {
match f(global_args, local_args).await { match f.await {
Ok(r) => r, Ok(r) => r,
Err(error) => { Err(error) => {
log::error!("-----"); log::error!("-----");
log::error!("Operation failed with error: {}", error); log::error!("Operation failed with error: {}", error);
if let Err(own_error) = troubleshoot(global_args, local_args, &error) { // TODO: support troubleshooting
log::error!( // if let Err(own_error) = troubleshoot(hive, &error) {
"Error occurred while trying to troubleshoot another error: {}", // log::error!(
own_error // "Error occurred while trying to troubleshoot another error: {}",
); // own_error
} // );
// }
// Ensure we exit with a code // Ensure we exit with a code
quit::with_code(1); quit::with_code(1);