forked from DGNum/colmena
Initial support for clap derive up to the top
This commit is contained in:
parent
1ad9301c62
commit
935aa77e53
14 changed files with 239 additions and 382 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
339
src/cli.rs
339
src/cli.rs
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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"))
|
|
||||||
}
|
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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?;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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"))
|
|
||||||
}
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue