diff --git a/Cargo.lock b/Cargo.lock index 530b3aa..a9b85c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -262,6 +271,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "serde", + "winapi", +] + [[package]] name = "clap" version = "4.0.10" @@ -299,6 +321,16 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "color-eyre" version = "0.6.2" @@ -335,6 +367,12 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "crossbeam-utils" version = "0.8.11" @@ -381,6 +419,85 @@ dependencies = [ "syn", ] +[[package]] +name = "cxx" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "encoding_rs" version = "0.8.31" @@ -640,6 +757,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "serde_with", "tar", "target-lexicon", "tempdir", @@ -677,6 +795,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.8" @@ -748,6 +872,36 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.3.0" @@ -772,6 +926,7 @@ checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", + "serde", ] [[package]] @@ -850,6 +1005,15 @@ version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "lock_api" version = "0.4.8" @@ -935,6 +1099,25 @@ dependencies = [ "libc", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -945,6 +1128,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "object" version = "0.29.0" @@ -1286,6 +1478,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "sct" version = "0.7.0" @@ -1339,6 +1537,34 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f2d60d049ea019a84dcd6687b0d1e0030fe663ae105039bdf967ed5e6a9a7" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ccadfacf6cf10faad22bbadf55986bdd0856edfb5d9210aa1dcf1f516e84e93" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -1501,6 +1727,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1728,6 +1966,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + [[package]] name = "untrusted" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index b0c1ce6..be804fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,3 +35,4 @@ serde = { version = "1.0.144", features = ["derive"] } url = { version = "2.3.1", features = ["serde"] } serde_json = "1.0.85" typetag = "0.2.3" +serde_with = "2.0.1" diff --git a/src/cli/arg/mod.rs b/src/cli/arg/mod.rs index 5e74712..4827e09 100644 --- a/src/cli/arg/mod.rs +++ b/src/cli/arg/mod.rs @@ -1,4 +1,6 @@ mod instrumentation; -pub use instrumentation::Instrumentation; +pub(crate) use instrumentation::Instrumentation; mod channel_value; -pub use channel_value::ChannelValue; +pub(crate) use channel_value::ChannelValue; +mod plan_options; +pub(crate) use plan_options::PlanOptions; diff --git a/src/cli/arg/plan_options.rs b/src/cli/arg/plan_options.rs new file mode 100644 index 0000000..e96f214 --- /dev/null +++ b/src/cli/arg/plan_options.rs @@ -0,0 +1,41 @@ +use clap::{ArgAction, Parser}; + +/// Plan an install that can be repeated on an identical host later +#[derive(Debug, Parser)] +pub(crate) struct PlanOptions { + /// Channel(s) to add by default, pass multiple times for multiple channels + #[clap( + long, + value_parser, + action = clap::ArgAction::Append, + env = "HARMONIC_CHANNEL", + default_value = "nixpkgs=https://nixos.org/channels/nixpkgs-unstable", + group = "plan_options" + )] + pub(crate) channel: Vec, + /// Don't modify the user profile to automatically load nix + #[clap( + long, + action(ArgAction::SetTrue), + default_value = "false", + global = true, + group = "plan_options" + )] + pub(crate) no_modify_profile: bool, + /// Number of build users to create + #[clap( + long, + default_value = "32", + env = "HARMONIC_NIX_DAEMON_USER_COUNT", + group = "plan_options" + )] + pub(crate) daemon_user_count: usize, + #[clap( + long, + action(ArgAction::SetTrue), + default_value = "false", + global = true, + group = "plan_options" + )] + pub(crate) force: bool, +} diff --git a/src/cli/subcommand/install.rs b/src/cli/subcommand/install.rs index b23ec42..6180ff1 100644 --- a/src/cli/subcommand/install.rs +++ b/src/cli/subcommand/install.rs @@ -5,7 +5,10 @@ use eyre::{eyre, WrapErr}; use harmonic::{InstallPlan, InstallSettings}; use crate::{ - cli::{arg::ChannelValue, CommandExecute}, + cli::{ + arg::{ChannelValue, PlanOptions}, + CommandExecute, + }, interaction, }; @@ -19,26 +22,8 @@ pub(crate) struct Install { global = true )] no_confirm: bool, - /// Channel(s) to add by default, pass multiple times for multiple channels - #[clap( - long, - value_parser, - action = clap::ArgAction::Append, - env = "HARMONIC_CHANNEL", - default_value = "nixpkgs=https://nixos.org/channels/nixpkgs-unstable" - )] - pub(crate) channel: Vec, - /// Don't modify the user profile to automatically load nix - #[clap( - long, - action(ArgAction::SetTrue), - default_value = "false", - global = true - )] - pub(crate) no_modify_profile: bool, - /// Number of build users to create - #[clap(long, default_value = "32", env = "HARMONIC_NIX_DAEMON_USER_COUNT")] - pub(crate) daemon_user_count: usize, + #[clap(flatten)] + plan_options: PlanOptions, #[clap( long, action(ArgAction::SetTrue), @@ -47,14 +32,7 @@ pub(crate) struct Install { )] pub(crate) explain: bool, #[clap( - long, - action(ArgAction::SetTrue), - default_value = "false", - global = true - )] - pub(crate) force: bool, - #[clap( - conflicts_with_all = [ "no_modify_profile", "daemon_user_count", "channel" ], + conflicts_with_all = [ "plan_options" ], env = "HARMONIC_PLAN", )] plan: Option, @@ -67,11 +45,8 @@ impl CommandExecute for Install { let Self { no_confirm, plan, + plan_options, explain, - channel, - no_modify_profile, - daemon_user_count, - force, } = self; let mut plan = match &plan { @@ -84,14 +59,15 @@ impl CommandExecute for Install { None => { let mut settings = InstallSettings::default()?; - settings.force(force); - settings.daemon_user_count(daemon_user_count); + settings.force(plan_options.force); + settings.daemon_user_count(plan_options.daemon_user_count); settings.channels( - channel + plan_options + .channel .into_iter() .map(|ChannelValue(name, url)| (name, url)), ); - settings.modify_profile(!no_modify_profile); + settings.modify_profile(!plan_options.no_modify_profile); InstallPlan::new(settings).await? }, diff --git a/src/cli/subcommand/plan.rs b/src/cli/subcommand/plan.rs index 9ab035f..5a4ef21 100644 --- a/src/cli/subcommand/plan.rs +++ b/src/cli/subcommand/plan.rs @@ -1,59 +1,40 @@ use std::{path::PathBuf, process::ExitCode}; -use clap::{ArgAction, Parser}; +use clap::Parser; use harmonic::{InstallPlan, InstallSettings}; use eyre::WrapErr; -use crate::cli::{arg::ChannelValue, CommandExecute}; +use crate::cli::{ + arg::{ChannelValue, PlanOptions}, + CommandExecute, +}; /// Plan an install that can be repeated on an identical host later #[derive(Debug, Parser)] pub(crate) struct Plan { - /// Channel(s) to add by default, pass multiple times for multiple channels - #[clap( - long, - value_parser, - action = clap::ArgAction::Append, - env = "HARMONIC_CHANNEL", - default_value = "nixpkgs=https://nixos.org/channels/nixpkgs-unstable" - )] - channel: Vec, - /// Don't modify the user profile to automatically load nix - #[clap( - long, - action(ArgAction::SetTrue), - default_value = "false", - global = true - )] - no_modify_profile: bool, - /// Number of build users to create - #[clap(long, default_value = "32", env = "HARMONIC_NIX_DAEMON_USER_COUNT")] - daemon_user_count: usize, - #[clap( - long, - action(ArgAction::SetTrue), - default_value = "false", - global = true - )] - force: bool, + #[clap(flatten)] + plan_options: PlanOptions, #[clap(default_value = "/dev/stdout")] - plan: PathBuf, + pub(crate) plan: PathBuf, } #[async_trait::async_trait] impl CommandExecute for Plan { #[tracing::instrument(skip_all, fields( - channels = %self.channel.iter().map(|ChannelValue(name, url)| format!("{name} {url}")).collect::>().join(", "), - daemon_user_count = %self.daemon_user_count, - no_modify_profile = %self.no_modify_profile, + channels = %self.plan_options.channel.iter().map(|ChannelValue(name, url)| format!("{name} {url}")).collect::>().join(", "), + daemon_user_count = %self.plan_options.daemon_user_count, + no_modify_profile = %self.plan_options.no_modify_profile, ))] async fn execute(self) -> eyre::Result { let Self { - channel, - no_modify_profile, - daemon_user_count, - force, + plan_options: + PlanOptions { + channel, + no_modify_profile, + daemon_user_count, + force, + }, plan, } = self; diff --git a/src/settings.rs b/src/settings.rs index a601ead..f532f17 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,5 +1,7 @@ +use target_lexicon::Triple; use url::Url; +#[serde_with::serde_as] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct InstallSettings { pub(crate) daemon_user_count: usize, @@ -12,6 +14,8 @@ pub struct InstallSettings { pub(crate) nix_package_url: Url, pub(crate) extra_conf: Option, pub(crate) force: bool, + #[serde_as(as = "serde_with::DisplayFromStr")] + pub(crate) triple: Triple, } impl InstallSettings { @@ -40,6 +44,7 @@ impl InstallSettings { }; Ok(Self { + triple: target_lexicon::HOST, daemon_user_count: Default::default(), channels: Default::default(), modify_profile: Default::default(),