diff options
-rw-r--r-- | Cargo.toml | 8 | ||||
-rw-r--r-- | flake.nix | 2 | ||||
-rw-r--r-- | src/activate.rs | 54 | ||||
-rw-r--r-- | src/main.rs | 220 | ||||
-rw-r--r-- | src/utils/data.rs | 10 | ||||
-rw-r--r-- | src/utils/deploy.rs | 17 | ||||
-rw-r--r-- | src/utils/push.rs | 2 |
7 files changed, 172 insertions, 141 deletions
@@ -16,3 +16,11 @@ merge = "0.1.0" whoami = "0.9.0" log = "0.4" pretty_env_logger = "0.4" + +[[bin]] +name = "deploy" +path = "src/main.rs" + +[[bin]] +name = "activate" +path = "src/activate.rs" @@ -16,7 +16,7 @@ defaultApp = { type = "app"; - program = "${self.defaultPackage."${system}"}/bin/deploy-rs"; + program = "${self.defaultPackage."${system}"}/bin/deploy"; }; }); } diff --git a/src/activate.rs b/src/activate.rs new file mode 100644 index 0000000..c446ee0 --- /dev/null +++ b/src/activate.rs @@ -0,0 +1,54 @@ +use clap::Clap; + +extern crate pretty_env_logger; +#[macro_use] +extern crate log; + +#[macro_use] +extern crate serde_derive; + +#[macro_use] +mod utils; + +/// Activation portion of the simple Rust Nix deploy tool +#[derive(Clap, Debug)] +#[clap(version = "1.0", author = "notgne2 <gen2@gen2.space>")] +struct Opts { + profile_path: String, + closure: String, + + /// Command for activating the given profile + #[clap(long)] + activate_cmd: Option<String>, + + /// Command for bootstrapping + #[clap(long)] + bootstrap_cmd: Option<String>, + + /// Auto rollback if failure + #[clap(long)] + auto_rollback: bool, +} + + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + if std::env::var("DEPLOY_LOG").is_err() { + std::env::set_var("DEPLOY_LOG", "info"); + } + + pretty_env_logger::init_custom_env("DEPLOY_LOG"); + + let opts: Opts = Opts::parse(); + + utils::activate::activate( + opts.profile_path, + opts.closure, + opts.activate_cmd, + opts.bootstrap_cmd, + opts.auto_rollback, + ) + .await?; + + Ok(()) +}
\ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e084f0f..6dcb885 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,17 +19,6 @@ mod utils; #[derive(Clap, Debug)] #[clap(version = "1.0", author = "notgne2 <gen2@gen2.space>")] struct Opts { - /// Log verbosity - #[clap(short, long, parse(from_occurrences))] - verbose: i32, - - #[clap(subcommand)] - subcmd: SubCommand, -} - -/// Deploy profiles -#[derive(Clap, Debug)] -struct DeployOpts { /// The flake to deploy #[clap(default_value = ".")] flake: String, @@ -38,31 +27,6 @@ struct DeployOpts { checksigs: bool, } -/// Activate a profile on your current machine -#[derive(Clap, Debug)] -struct ActivateOpts { - profile_path: String, - closure: String, - - /// Command for activating the given profile - #[clap(short, long)] - activate_cmd: Option<String>, - - /// Command for bootstrapping - #[clap(short, long)] - bootstrap_cmd: Option<String>, - - /// Auto rollback if failure - #[clap(short, long)] - auto_rollback: bool, -} - -#[derive(Clap, Debug)] -enum SubCommand { - Deploy(DeployOpts), - Activate(ActivateOpts), -} - #[inline] async fn push_all_profiles( node: &utils::data::Node, @@ -150,6 +114,7 @@ async fn deploy_all_profiles( node_name, &merged_settings, &deploy_data, + merged_settings.auto_rollback, ) .await?; } @@ -157,6 +122,7 @@ async fn deploy_all_profiles( Ok(()) } +/// Returns if the available Nix installation supports flakes async fn test_flake_support() -> Result<bool, Box<dyn std::error::Error>> { Ok(Command::new("nix") .arg("eval") @@ -169,6 +135,7 @@ async fn test_flake_support() -> Result<bool, Box<dyn std::error::Error>> { .success()) } +/// Evaluates the Nix in the given `repo` and return the processed Data from it async fn get_deployment_data( supports_flakes: bool, repo: &str, @@ -216,108 +183,95 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { let opts: Opts = Opts::parse(); - match opts.subcmd { - SubCommand::Deploy(deploy_opts) => { - let deploy_flake = utils::parse_flake(deploy_opts.flake.as_str()); - - let supports_flakes = test_flake_support().await?; - - let data = get_deployment_data(supports_flakes, deploy_flake.repo).await?; - - match (deploy_flake.node, deploy_flake.profile) { - (Some(node_name), Some(profile_name)) => { - let node = match data.nodes.get(node_name) { - Some(x) => x, - None => good_panic!("No node was found named `{}`", node_name), - }; - let profile = match node.profiles.get(profile_name) { - Some(x) => x, - None => good_panic!("No profile was found named `{}`", profile_name), - }; - - let mut merged_settings = data.generic_settings.clone(); - merged_settings.merge(node.generic_settings.clone()); - merged_settings.merge(profile.generic_settings.clone()); - - let deploy_data = - utils::make_deploy_data(profile_name, node_name, &merged_settings).await?; - - utils::push::push_profile( - profile, - profile_name, - node, - node_name, - supports_flakes, - deploy_opts.checksigs, - deploy_flake.repo, - &merged_settings, - &deploy_data, - ) - .await?; - - utils::deploy::deploy_profile( - profile, - profile_name, - node, - node_name, - &merged_settings, - &deploy_data, - ) - .await?; - } - (Some(node_name), None) => { - let node = match data.nodes.get(node_name) { - Some(x) => x, - None => good_panic!("No node was found named `{}`", node_name), - }; - - push_all_profiles( - node, - node_name, - supports_flakes, - deploy_flake.repo, - &data.generic_settings, - deploy_opts.checksigs, - ) - .await?; - - deploy_all_profiles(node, node_name, &data.generic_settings).await?; - } - (None, None) => { - info!("Deploying all profiles on all nodes"); - - for (node_name, node) in &data.nodes { - push_all_profiles( - node, - node_name, - supports_flakes, - deploy_flake.repo, - &data.generic_settings, - deploy_opts.checksigs, - ) - .await?; - } - - for (node_name, node) in &data.nodes { - deploy_all_profiles(node, node_name, &data.generic_settings).await?; - } - } - (None, Some(_)) => good_panic!( - "Profile provided without a node, this is not (currently) supported" - ), + let deploy_flake = utils::parse_flake(opts.flake.as_str()); + + let supports_flakes = test_flake_support().await?; + + let data = get_deployment_data(supports_flakes, deploy_flake.repo).await?; + + match (deploy_flake.node, deploy_flake.profile) { + (Some(node_name), Some(profile_name)) => { + let node = match data.nodes.get(node_name) { + Some(x) => x, + None => good_panic!("No node was found named `{}`", node_name), + }; + let profile = match node.profiles.get(profile_name) { + Some(x) => x, + None => good_panic!("No profile was found named `{}`", profile_name), }; + + let mut merged_settings = data.generic_settings.clone(); + merged_settings.merge(node.generic_settings.clone()); + merged_settings.merge(profile.generic_settings.clone()); + + let deploy_data = + utils::make_deploy_data(profile_name, node_name, &merged_settings).await?; + + utils::push::push_profile( + profile, + profile_name, + node, + node_name, + supports_flakes, + opts.checksigs, + deploy_flake.repo, + &merged_settings, + &deploy_data, + ) + .await?; + + utils::deploy::deploy_profile( + profile, + profile_name, + node, + node_name, + &merged_settings, + &deploy_data, + merged_settings.auto_rollback, + ) + .await?; } - SubCommand::Activate(activate_opts) => { - utils::activate::activate( - activate_opts.profile_path, - activate_opts.closure, - activate_opts.activate_cmd, - activate_opts.bootstrap_cmd, - activate_opts.auto_rollback, + (Some(node_name), None) => { + let node = match data.nodes.get(node_name) { + Some(x) => x, + None => good_panic!("No node was found named `{}`", node_name), + }; + + push_all_profiles( + node, + node_name, + supports_flakes, + deploy_flake.repo, + &data.generic_settings, + opts.checksigs, ) .await?; + + deploy_all_profiles(node, node_name, &data.generic_settings).await?; } - } + (None, None) => { + info!("Deploying all profiles on all nodes"); + + for (node_name, node) in &data.nodes { + push_all_profiles( + node, + node_name, + supports_flakes, + deploy_flake.repo, + &data.generic_settings, + opts.checksigs, + ) + .await?; + } + + for (node_name, node) in &data.nodes { + deploy_all_profiles(node, node_name, &data.generic_settings).await?; + } + } + (None, Some(_)) => { + good_panic!("Profile provided without a node, this is not (currently) supported") + } + }; Ok(()) } diff --git a/src/utils/data.rs b/src/utils/data.rs index b28b6cd..0753508 100644 --- a/src/utils/data.rs +++ b/src/utils/data.rs @@ -14,10 +14,12 @@ pub struct GenericSettings { )] #[merge(strategy = merge::vec::append)] pub ssh_opts: Vec<String>, - #[serde(rename(deserialize = "fastConnection"))] - pub fast_connection: Option<bool>, - #[serde(rename(deserialize = "autoRollback"))] - pub auto_rollback: Option<String>, + #[serde(rename(deserialize = "fastConnection"), default)] + #[merge(strategy = merge::bool::overwrite_false)] + pub fast_connection: bool, + #[serde(rename(deserialize = "autoRollback"), default)] + #[merge(strategy = merge::bool::overwrite_false)] + pub auto_rollback: bool, } #[derive(Deserialize, Debug, Clone)] diff --git a/src/utils/deploy.rs b/src/utils/deploy.rs index 900743c..42bd0b4 100644 --- a/src/utils/deploy.rs +++ b/src/utils/deploy.rs @@ -9,6 +9,7 @@ pub async fn deploy_profile( node_name: &str, merged_settings: &data::GenericSettings, deploy_data: &super::DeployData<'_>, + auto_rollback: bool, ) -> Result<(), Box<dyn std::error::Error>> { info!( "Activating profile `{}` for node `{}`", @@ -16,8 +17,16 @@ pub async fn deploy_profile( ); let mut self_activate_command = format!( - "{} activate '{}' '{}'", - deploy_data.current_exe.as_path().to_str().unwrap(), + "{} '{}' '{}'", + deploy_data + .current_exe + .as_path() + .parent() + .unwrap() + .to_str() + .unwrap() + .to_owned() + + "/activate", deploy_data.profile_path, profile.profile_settings.path, ); @@ -40,6 +49,10 @@ pub async fn deploy_profile( ); } + if auto_rollback { + self_activate_command = format!("{} --auto-rollback", self_activate_command); + } + let mut c = Command::new("ssh"); let mut ssh_command = c.arg(format!( "ssh://{}@{}", diff --git a/src/utils/push.rs b/src/utils/push.rs index 54ae013..0e1b9ba 100644 --- a/src/utils/push.rs +++ b/src/utils/push.rs @@ -74,7 +74,7 @@ pub async fn push_profile( let mut copy_command_ = Command::new("nix"); let mut copy_command = copy_command_.arg("copy"); - if let Some(true) = merged_settings.fast_connection { + if merged_settings.fast_connection { copy_command = copy_command.arg("--substitute-on-destination"); } |