From 76dbef54af3bbfbf81edd8a45f6ba8dea0db47f9 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 10:48:21 -0700 Subject: stuff --- src/main.rs | 598 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 src/main.rs (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..2184392 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,598 @@ +use clap::Clap; +use merge::Merge; +use std::collections::HashMap; + +use std::borrow::Cow; + +use std::process::Stdio; +use tokio::process::Command; + +use std::path::Path; + +use std::process; + +extern crate pretty_env_logger; +#[macro_use] +extern crate log; + +#[macro_use] +extern crate serde_derive; + +macro_rules! good_panic { + ($($tts:tt)*) => {{ + error!($($tts)*); + process::exit(1); + }} +} + +/// Simple Rust rewrite of a simple Nix Flake deployment tool +#[derive(Clap, Debug)] +#[clap(version = "1.0", author = "notgne2 ")] +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, + /// Prepare server (for first deployments) + #[clap(short, long)] + prime: 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, + + /// Command for bootstrapping + #[clap(short, long)] + bootstrap_cmd: Option, + + /// Auto rollback if failure + #[clap(short, long)] + auto_rollback: bool, +} + +#[derive(Clap, Debug)] +enum SubCommand { + Deploy(DeployOpts), + Activate(ActivateOpts), +} + +#[derive(Deserialize, Debug, Clone, Merge)] +pub struct GenericSettings { + #[serde(rename(deserialize = "sshUser"))] + pub ssh_user: Option, + pub user: Option, + #[serde( + skip_serializing_if = "Vec::is_empty", + default, + rename(deserialize = "sshOpts") + )] + #[merge(strategy = merge::vec::append)] + pub ssh_opts: Vec, + #[serde(rename(deserialize = "fastConnection"))] + pub fast_connection: Option, + #[serde(rename(deserialize = "autoRollback"))] + pub auto_rollback: Option, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct NodeSettings { + pub hostname: String, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct ProfileSettings { + pub path: String, + pub activate: Option, + pub bootstrap: Option, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Profile { + #[serde(flatten)] + pub profile_settings: ProfileSettings, + #[serde(flatten)] + pub generic_settings: GenericSettings, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Node { + #[serde(flatten)] + pub generic_settings: GenericSettings, + #[serde(flatten)] + pub node_settings: NodeSettings, + + pub profiles: HashMap, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Data { + #[serde(flatten)] + pub generic_settings: GenericSettings, + pub nodes: HashMap, +} + +async fn deploy_profile( + profile: &Profile, + profile_name: &str, + node: &Node, + node_name: &str, + top_settings: &GenericSettings, + supports_flakes: bool, + repo: &str, +) -> Result<(), Box> { + info!( + "Deploying profile `{}` for node `{}`", + profile_name, node_name + ); + + let mut merged_settings = top_settings.clone(); + merged_settings.merge(node.generic_settings.clone()); + merged_settings.merge(profile.generic_settings.clone()); + + let ssh_user: Cow = match &merged_settings.ssh_user { + Some(u) => u.into(), + None => whoami::username().into(), + }; + + let profile_user: Cow = match &merged_settings.user { + Some(x) => x.into(), + None => match &merged_settings.ssh_user { + Some(x) => x.into(), + None => good_panic!( + "Neither user nor sshUser set for profile `{}` of node `{}`", + profile_name, + node_name + ), + }, + }; + + let sudo: Option = match merged_settings.user { + Some(ref user) if user != &ssh_user => Some(format!("sudo -u {}", user).into()), + _ => None, + }; + + let profile_path = match &profile_user[..] { + "root" => format!("/nix/var/nix/profiles/{}", profile_name), + _ => format!( + "/nix/var/nix/profiles/per-user/{}/{}", + profile_user, profile_name + ), + }; + + info!( + "Building profile `{}` for node `{}`", + profile_name, node_name + ); + if supports_flakes { + Command::new("nix") + .arg("build") + .arg("--no-link") + .arg(format!( + "{}#deploy.nodes.{}.profiles.{}.path", + repo, node_name, profile_name + )) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()? + .await?; + } else { + Command::new("nix-build") + .arg(&repo) + .arg("-A") + .arg(format!( + "deploy.nodes.{}.profiles.{}.path", + node_name, profile_name + )) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()? + .await?; + } + + let current_exe = std::env::current_exe().expect("Expected to find current executable path"); + + if !current_exe.starts_with("/nix/store/") { + good_panic!("The deploy binary must be in the Nix store"); + } + + if let Ok(local_key) = std::env::var("LOCAL_KEY") { + info!( + "Signing key present! Signing profile `{}` for node `{}`", + profile_name, node_name + ); + + Command::new("nix") + .arg("sign-paths") + .arg("-r") + .arg("-k") + .arg(local_key) + .arg(&profile.profile_settings.path) + .arg(¤t_exe) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()? + .await?; + } + + info!("Copying profile `{} for node `{}`", profile_name, node_name); + + let mut copy_command_ = Command::new("nix"); + let mut copy_command = copy_command_.arg("copy"); + + if let Some(true) = merged_settings.fast_connection { + copy_command = copy_command.arg("--substitute-on-destination"); + } + + let ssh_opts_str = merged_settings + .ssh_opts + // This should provide some extra safety, but it also breaks for some reason, oh well + // .iter() + // .map(|x| format!("'{}'", x)) + // .collect::>() + .join(" "); + + copy_command + .arg("--no-check-sigs") + .arg("--to") + .arg(format!( + "ssh://{}@{}", + ssh_user, node.node_settings.hostname + )) + .arg(&profile.profile_settings.path) + .arg(¤t_exe) + .env("NIX_SSHOPTS", ssh_opts_str) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()? + .await?; + + info!( + "Activating profile `{}` for node `{}`", + profile_name, node_name + ); + + let mut self_activate_command = format!( + "{} activate '{}' '{}'", + current_exe.as_path().to_str().unwrap(), + profile_path, + profile.profile_settings.path, + ); + + if let Some(sudo_cmd) = sudo { + self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); + } + + if let Some(ref bootstrap_cmd) = profile.profile_settings.bootstrap { + self_activate_command = format!( + "{} --bootstrap-cmd '{}'", + self_activate_command, bootstrap_cmd + ); + } + + if let Some(ref activate_cmd) = profile.profile_settings.activate { + self_activate_command = format!( + "{} --activate-cmd '{}'", + self_activate_command, activate_cmd + ); + } + + let mut c = Command::new("ssh"); + let mut ssh_command = c.arg(format!( + "ssh://{}@{}", + ssh_user, node.node_settings.hostname + )); + + for ssh_opt in merged_settings.ssh_opts { + ssh_command = ssh_command.arg(ssh_opt); + } + + ssh_command.arg(self_activate_command).spawn()?.await?; + + Ok(()) +} + +#[inline] +async fn deploy_all_profiles( + node: &Node, + node_name: &str, + supports_flakes: bool, + repo: &str, + top_settings: &GenericSettings, + prime: bool, +) -> Result<(), Box> { + info!("Deploying all profiles for `{}`", node_name); + + if prime { + info!("Bootstrapping {}", node_name); + + let profile = match node.profiles.get("system") { + Some(x) => x, + None => good_panic!("No system profile was found, needed for priming"), + }; + + deploy_profile( + &profile, + "system", + node, + node_name, + top_settings, + supports_flakes, + repo, + ) + .await?; + } + + for (profile_name, profile) in &node.profiles { + // This will have already been deployed + if prime && profile_name == "system" { + continue; + } + + deploy_profile( + &profile, + profile_name, + node, + node_name, + top_settings, + supports_flakes, + repo, + ) + .await?; + } + + Ok(()) +} + +#[tokio::main] + +async fn main() -> Result<(), Box> { + if let Err(_) = std::env::var("DEPLOY_LOG") { + std::env::set_var("DEPLOY_LOG", "info"); + } + + pretty_env_logger::init_custom_env("DEPLOY_LOG"); + + let opts: Opts = Opts::parse(); + + match opts.subcmd { + SubCommand::Deploy(deploy_opts) => { + let flake_fragment_start = deploy_opts.flake.find('#'); + + let (repo, maybe_fragment) = match flake_fragment_start { + Some(s) => (&deploy_opts.flake[..s], Some(&deploy_opts.flake[s + 1..])), + None => (deploy_opts.flake.as_str(), None), + }; + + let (maybe_node, maybe_profile) = match maybe_fragment { + Some(fragment) => { + let fragment_profile_start = fragment.find('.'); + match fragment_profile_start { + Some(s) => (Some(&fragment[..s]), Some(&fragment[s + 1..])), + None => (Some(fragment), None), + } + } + None => (None, None), + }; + + let test_flake_status = Command::new("nix") + .arg("eval") + .arg("--expr") + .arg("builtins.getFlake") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .await?; + + let supports_flakes = test_flake_status.success(); + + let data_json = match supports_flakes { + true => { + let c = Command::new("nix") + .arg("eval") + .arg("--json") + .arg(format!("{}#deploy", repo)) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + // TODO forward input args? + .output() + .await?; + + String::from_utf8(c.stdout)? + } + false => { + let c = Command::new("nix-instanciate") + .arg("--strict") + .arg("--read-write-mode") + .arg("--json") + .arg("--eval") + .arg("--E") + .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo)) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .output() + .await?; + + String::from_utf8(c.stdout)? + } + }; + + let data: Data = serde_json::from_str(&data_json)?; + + match (maybe_node, maybe_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), + }; + + deploy_profile( + &profile, + profile_name, + node, + node_name, + &data.generic_settings, + supports_flakes, + repo, + ) + .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), + }; + + deploy_all_profiles( + node, + node_name, + supports_flakes, + repo, + &data.generic_settings, + deploy_opts.prime, + ) + .await?; + } + (None, None) => { + info!("Deploying all profiles on all nodes"); + + for (node_name, node) in &data.nodes { + deploy_all_profiles( + node, + node_name, + supports_flakes, + repo, + &data.generic_settings, + deploy_opts.prime, + ) + .await?; + } + } + (None, Some(_)) => good_panic!( + "Profile provided without a node, this is not (currently) supported" + ), + }; + } + SubCommand::Activate(activate_opts) => { + info!("Activating profile"); + + Command::new("nix-env") + .arg("-p") + .arg(&activate_opts.profile_path) + .arg("--set") + .arg(&activate_opts.closure) + .stdout(Stdio::null()) + .spawn()? + .await?; + + if let (Some(bootstrap_cmd), false) = ( + activate_opts.bootstrap_cmd, + !Path::new(&activate_opts.profile_path).exists(), + ) { + let bootstrap_status = Command::new("bash") + .arg("-c") + .arg(&bootstrap_cmd) + .env("PROFILE", &activate_opts.profile_path) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .await; + + match bootstrap_status { + Ok(s) if s.success() => (), + _ => { + tokio::fs::remove_file(&activate_opts.profile_path).await?; + good_panic!("Failed to execute bootstrap command"); + } + } + } + + if let Some(activate_cmd) = activate_opts.activate_cmd { + let activate_status = Command::new("bash") + .arg("-c") + .arg(&activate_cmd) + .env("PROFILE", &activate_opts.profile_path) + .status() + .await; + + match activate_status { + Ok(s) if s.success() => (), + _ if activate_opts.auto_rollback => { + Command::new("nix-env") + .arg("-p") + .arg(&activate_opts.profile_path) + .arg("--rollback") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()? + .await?; + + let c = Command::new("nix-env") + .arg("-p") + .arg(&activate_opts.profile_path) + .arg("--list-generations") + .output() + .await?; + let generations_list = String::from_utf8(c.stdout)?; + + let last_generation_line = generations_list + .lines() + .last() + .expect("Expected to find a generation in list"); + + let last_generation_id = last_generation_line + .split_whitespace() + .next() + .expect("Expected to get ID from generation entry"); + + debug!("Removing generation entry {}", last_generation_line); + warn!("Removing generation by ID {}", last_generation_id); + + Command::new("nix-env") + .arg("-p") + .arg(&activate_opts.profile_path) + .arg("--delete-generations") + .arg(last_generation_id) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()? + .await?; + + // TODO: why are we doing this? + // to run the older version as long as the command is the same? + Command::new("bash") + .arg("-c") + .arg(&activate_cmd) + .spawn()? + .await?; + + good_panic!("Failed to execute activation command"); + } + _ => {} + } + } + } + } + + Ok(()) +} -- cgit v1.2.3 From 1b9cb58802cd295bd91b822812f195c02367e350 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 12:17:36 -0700 Subject: add check sigs flag --- src/main.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 2184392..0599b11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,9 @@ struct DeployOpts { /// Prepare server (for first deployments) #[clap(short, long)] prime: bool, + /// Check signatures when using `nix copy` + #[clap(short, long)] + checksigs: bool, } /// Activate a profile on your current machine @@ -135,6 +138,7 @@ async fn deploy_profile( node_name: &str, top_settings: &GenericSettings, supports_flakes: bool, + check_sigs: bool, repo: &str, ) -> Result<(), Box> { info!( @@ -240,6 +244,10 @@ async fn deploy_profile( copy_command = copy_command.arg("--substitute-on-destination"); } + if !check_sigs { + copy_command = copy_command.arg("--no-check-sigs"); + } + let ssh_opts_str = merged_settings .ssh_opts // This should provide some extra safety, but it also breaks for some reason, oh well @@ -249,7 +257,6 @@ async fn deploy_profile( .join(" "); copy_command - .arg("--no-check-sigs") .arg("--to") .arg(format!( "ssh://{}@{}", @@ -316,6 +323,7 @@ async fn deploy_all_profiles( repo: &str, top_settings: &GenericSettings, prime: bool, + check_sigs: bool, ) -> Result<(), Box> { info!("Deploying all profiles for `{}`", node_name); @@ -334,6 +342,7 @@ async fn deploy_all_profiles( node_name, top_settings, supports_flakes, + check_sigs, repo, ) .await?; @@ -352,6 +361,7 @@ async fn deploy_all_profiles( node_name, top_settings, supports_flakes, + check_sigs, repo, ) .await?; @@ -374,7 +384,6 @@ async fn main() -> Result<(), Box> { match opts.subcmd { SubCommand::Deploy(deploy_opts) => { let flake_fragment_start = deploy_opts.flake.find('#'); - let (repo, maybe_fragment) = match flake_fragment_start { Some(s) => (&deploy_opts.flake[..s], Some(&deploy_opts.flake[s + 1..])), None => (deploy_opts.flake.as_str(), None), @@ -453,6 +462,7 @@ async fn main() -> Result<(), Box> { node_name, &data.generic_settings, supports_flakes, + deploy_opts.checksigs, repo, ) .await?; @@ -470,6 +480,7 @@ async fn main() -> Result<(), Box> { repo, &data.generic_settings, deploy_opts.prime, + deploy_opts.checksigs, ) .await?; } @@ -484,6 +495,7 @@ async fn main() -> Result<(), Box> { repo, &data.generic_settings, deploy_opts.prime, + deploy_opts.checksigs, ) .await?; } -- cgit v1.2.3 From 00f75951211bacc5bab4317e56376f03c74dabb7 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 12:25:30 -0700 Subject: Add (untested) profiles order support --- src/main.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 0599b11..9d55efc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -122,6 +122,12 @@ pub struct Node { pub node_settings: NodeSettings, pub profiles: HashMap, + #[serde( + skip_serializing_if = "Vec::is_empty", + default, + rename(deserialize = "profilesOrder") + )] + pub profiles_order: Vec, } #[derive(Deserialize, Debug, Clone)] @@ -348,7 +354,21 @@ async fn deploy_all_profiles( .await?; } - for (profile_name, profile) in &node.profiles { + let mut profiles_list: Vec<&str> = node.profiles_order.iter().map(|x| x.as_ref()).collect(); + + // Add any profiles which weren't in the provided order list + for (profile_name, _) in &node.profiles { + if !profiles_list.contains(&profile_name.as_str()) { + profiles_list.push(&profile_name); + } + } + + for profile_name in profiles_list { + let profile = match node.profiles.get(profile_name) { + Some(x) => x, + None => good_panic!("No profile was found named `{}`", profile_name), + }; + // This will have already been deployed if prime && profile_name == "system" { continue; -- cgit v1.2.3 From a71eaa3cec59e8084f890bf89efbffc5f765fd9e Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 13:37:57 -0700 Subject: Mildly modularize and seperate deploy and push (untested) --- src/main.rs | 164 +++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 118 insertions(+), 46 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 9d55efc..a3c526f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use clap::Clap; use merge::Merge; -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; use std::borrow::Cow; @@ -137,25 +137,19 @@ pub struct Data { pub nodes: HashMap, } -async fn deploy_profile( - profile: &Profile, +struct DeployData<'a> { + pub sudo: Option, + pub ssh_user: Cow<'a, str>, + pub profile_user: Cow<'a, str>, + pub profile_path: String, + pub current_exe: PathBuf, +} + +async fn make_deploy_data<'a>( profile_name: &str, - node: &Node, node_name: &str, - top_settings: &GenericSettings, - supports_flakes: bool, - check_sigs: bool, - repo: &str, -) -> Result<(), Box> { - info!( - "Deploying profile `{}` for node `{}`", - profile_name, node_name - ); - - let mut merged_settings = top_settings.clone(); - merged_settings.merge(node.generic_settings.clone()); - merged_settings.merge(profile.generic_settings.clone()); - + merged_settings: &'a GenericSettings, +) -> Result, Box> { let ssh_user: Cow = match &merged_settings.ssh_user { Some(u) => u.into(), None => whoami::username().into(), @@ -173,11 +167,6 @@ async fn deploy_profile( }, }; - let sudo: Option = match merged_settings.user { - Some(ref user) if user != &ssh_user => Some(format!("sudo -u {}", user).into()), - _ => None, - }; - let profile_path = match &profile_user[..] { "root" => format!("/nix/var/nix/profiles/{}", profile_name), _ => format!( @@ -186,6 +175,42 @@ async fn deploy_profile( ), }; + let sudo: Option = match merged_settings.user { + Some(ref user) if user != &ssh_user => Some(format!("sudo -u {}", user).into()), + _ => None, + }; + + let current_exe = std::env::current_exe().expect("Expected to find current executable path"); + + if !current_exe.starts_with("/nix/store/") { + good_panic!("The deploy binary must be in the Nix store"); + } + + Ok(DeployData { + sudo, + ssh_user, + profile_user, + profile_path, + current_exe, + }) +} + +async fn push_profile( + profile: &Profile, + profile_name: &str, + node: &Node, + node_name: &str, + supports_flakes: bool, + check_sigs: bool, + repo: &str, + merged_settings: &GenericSettings, + deploy_data: &DeployData<'_>, +) -> Result<(), Box> { + info!( + "Deploying profile `{}` for node `{}`", + profile_name, node_name + ); + info!( "Building profile `{}` for node `{}`", profile_name, node_name @@ -216,12 +241,6 @@ async fn deploy_profile( .await?; } - let current_exe = std::env::current_exe().expect("Expected to find current executable path"); - - if !current_exe.starts_with("/nix/store/") { - good_panic!("The deploy binary must be in the Nix store"); - } - if let Ok(local_key) = std::env::var("LOCAL_KEY") { info!( "Signing key present! Signing profile `{}` for node `{}`", @@ -234,7 +253,7 @@ async fn deploy_profile( .arg("-k") .arg(local_key) .arg(&profile.profile_settings.path) - .arg(¤t_exe) + .arg(&deploy_data.current_exe) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn()? @@ -266,16 +285,27 @@ async fn deploy_profile( .arg("--to") .arg(format!( "ssh://{}@{}", - ssh_user, node.node_settings.hostname + deploy_data.ssh_user, node.node_settings.hostname )) .arg(&profile.profile_settings.path) - .arg(¤t_exe) + .arg(&deploy_data.current_exe) .env("NIX_SSHOPTS", ssh_opts_str) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn()? .await?; + Ok(()) +} + +async fn deploy_profile( + profile: &Profile, + profile_name: &str, + node: &Node, + node_name: &str, + merged_settings: &GenericSettings, + deploy_data: &DeployData<'_>, +) -> Result<(), Box> { info!( "Activating profile `{}` for node `{}`", profile_name, node_name @@ -283,12 +313,12 @@ async fn deploy_profile( let mut self_activate_command = format!( "{} activate '{}' '{}'", - current_exe.as_path().to_str().unwrap(), - profile_path, + deploy_data.current_exe.as_path().to_str().unwrap(), + deploy_data.profile_path, profile.profile_settings.path, ); - if let Some(sudo_cmd) = sudo { + if let Some(sudo_cmd) = &deploy_data.sudo { self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); } @@ -309,10 +339,10 @@ async fn deploy_profile( let mut c = Command::new("ssh"); let mut ssh_command = c.arg(format!( "ssh://{}@{}", - ssh_user, node.node_settings.hostname + deploy_data.ssh_user, node.node_settings.hostname )); - for ssh_opt in merged_settings.ssh_opts { + for ssh_opt in &merged_settings.ssh_opts { ssh_command = ssh_command.arg(ssh_opt); } @@ -321,6 +351,48 @@ async fn deploy_profile( Ok(()) } +async fn deploy_profile_todo( + top_settings: &GenericSettings, + profile: &Profile, + profile_name: &str, + node: &Node, + node_name: &str, + supports_flakes: bool, + check_sigs: bool, + repo: &str, +) -> Result<(), Box> { + let mut merged_settings = top_settings.clone(); + merged_settings.merge(node.generic_settings.clone()); + merged_settings.merge(profile.generic_settings.clone()); + + let deploy_data = make_deploy_data(profile_name, node_name, &merged_settings).await?; + + push_profile( + profile, + profile_name, + node, + node_name, + supports_flakes, + check_sigs, + repo, + &merged_settings, + &deploy_data, + ) + .await?; + + deploy_profile( + profile, + profile_name, + node, + node_name, + &merged_settings, + &deploy_data, + ) + .await?; + + Ok(()) +} + #[inline] async fn deploy_all_profiles( node: &Node, @@ -341,12 +413,12 @@ async fn deploy_all_profiles( None => good_panic!("No system profile was found, needed for priming"), }; - deploy_profile( - &profile, + deploy_profile_todo( + top_settings, + profile, "system", node, node_name, - top_settings, supports_flakes, check_sigs, repo, @@ -374,12 +446,12 @@ async fn deploy_all_profiles( continue; } - deploy_profile( - &profile, + deploy_profile_todo( + top_settings, + profile, profile_name, node, node_name, - top_settings, supports_flakes, check_sigs, repo, @@ -475,12 +547,12 @@ async fn main() -> Result<(), Box> { None => good_panic!("No profile was found named `{}`", profile_name), }; - deploy_profile( - &profile, + deploy_profile_todo( + &data.generic_settings, + profile, profile_name, node, node_name, - &data.generic_settings, supports_flakes, deploy_opts.checksigs, repo, -- cgit v1.2.3 From ff347d4204369d40b6b04eedf8e2421bcd7018ab Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 13:55:46 -0700 Subject: Crudely perform pushes before deploys, with little data re-use (untested) --- src/main.rs | 135 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 69 insertions(+), 66 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index a3c526f..59b79a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -351,81 +351,63 @@ async fn deploy_profile( Ok(()) } -async fn deploy_profile_todo( - top_settings: &GenericSettings, - profile: &Profile, - profile_name: &str, - node: &Node, - node_name: &str, - supports_flakes: bool, - check_sigs: bool, - repo: &str, -) -> Result<(), Box> { - let mut merged_settings = top_settings.clone(); - merged_settings.merge(node.generic_settings.clone()); - merged_settings.merge(profile.generic_settings.clone()); - - let deploy_data = make_deploy_data(profile_name, node_name, &merged_settings).await?; - - push_profile( - profile, - profile_name, - node, - node_name, - supports_flakes, - check_sigs, - repo, - &merged_settings, - &deploy_data, - ) - .await?; - - deploy_profile( - profile, - profile_name, - node, - node_name, - &merged_settings, - &deploy_data, - ) - .await?; - - Ok(()) -} - #[inline] -async fn deploy_all_profiles( +async fn push_all_profiles( node: &Node, node_name: &str, supports_flakes: bool, repo: &str, top_settings: &GenericSettings, - prime: bool, check_sigs: bool, ) -> Result<(), Box> { info!("Deploying all profiles for `{}`", node_name); - if prime { - info!("Bootstrapping {}", node_name); + let mut profiles_list: Vec<&str> = node.profiles_order.iter().map(|x| x.as_ref()).collect(); + + // Add any profiles which weren't in the provided order list + for (profile_name, _) in &node.profiles { + if !profiles_list.contains(&profile_name.as_str()) { + profiles_list.push(&profile_name); + } + } - let profile = match node.profiles.get("system") { + for profile_name in profiles_list { + let profile = match node.profiles.get(profile_name) { Some(x) => x, - None => good_panic!("No system profile was found, needed for priming"), + None => good_panic!("No profile was found named `{}`", profile_name), }; - deploy_profile_todo( - top_settings, + let mut merged_settings = top_settings.clone(); + merged_settings.merge(node.generic_settings.clone()); + merged_settings.merge(profile.generic_settings.clone()); + + let deploy_data = make_deploy_data(profile_name, node_name, &merged_settings).await?; + + push_profile( profile, - "system", + profile_name, node, node_name, supports_flakes, check_sigs, repo, + &merged_settings, + &deploy_data, ) .await?; } + Ok(()) +} + +#[inline] +async fn deploy_all_profiles( + node: &Node, + node_name: &str, + top_settings: &GenericSettings, +) -> Result<(), Box> { + info!("Deploying all profiles for `{}`", node_name); + let mut profiles_list: Vec<&str> = node.profiles_order.iter().map(|x| x.as_ref()).collect(); // Add any profiles which weren't in the provided order list @@ -441,20 +423,19 @@ async fn deploy_all_profiles( None => good_panic!("No profile was found named `{}`", profile_name), }; - // This will have already been deployed - if prime && profile_name == "system" { - continue; - } + let mut merged_settings = top_settings.clone(); + merged_settings.merge(node.generic_settings.clone()); + merged_settings.merge(profile.generic_settings.clone()); + + let deploy_data = make_deploy_data(profile_name, node_name, &merged_settings).await?; - deploy_profile_todo( - top_settings, + deploy_profile( profile, profile_name, node, node_name, - supports_flakes, - check_sigs, - repo, + &merged_settings, + &deploy_data, ) .await?; } @@ -547,8 +528,14 @@ async fn main() -> Result<(), Box> { None => good_panic!("No profile was found named `{}`", profile_name), }; - deploy_profile_todo( - &data.generic_settings, + 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 = + make_deploy_data(profile_name, node_name, &merged_settings).await?; + + push_profile( profile, profile_name, node, @@ -556,6 +543,18 @@ async fn main() -> Result<(), Box> { supports_flakes, deploy_opts.checksigs, repo, + &merged_settings, + &deploy_data, + ) + .await?; + + deploy_profile( + profile, + profile_name, + node, + node_name, + &merged_settings, + &deploy_data, ) .await?; } @@ -565,32 +564,36 @@ async fn main() -> Result<(), Box> { None => good_panic!("No node was found named `{}`", node_name), }; - deploy_all_profiles( + push_all_profiles( node, node_name, supports_flakes, repo, &data.generic_settings, - deploy_opts.prime, 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 { - deploy_all_profiles( + push_all_profiles( node, node_name, supports_flakes, repo, &data.generic_settings, - deploy_opts.prime, 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" -- cgit v1.2.3 From 622ae7c1b46b188171121486ef93d582a4180495 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 13:59:49 -0700 Subject: fix some logging --- src/main.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 59b79a5..cd57692 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,9 +43,6 @@ struct DeployOpts { /// The flake to deploy #[clap(default_value = ".")] flake: String, - /// Prepare server (for first deployments) - #[clap(short, long)] - prime: bool, /// Check signatures when using `nix copy` #[clap(short, long)] checksigs: bool, @@ -207,14 +204,15 @@ async fn push_profile( deploy_data: &DeployData<'_>, ) -> Result<(), Box> { info!( - "Deploying profile `{}` for node `{}`", + "Pushing profile `{}` for node `{}`", profile_name, node_name ); - info!( - "Building profile `{}` for node `{}`", + debug!( + "Building profile `{} for node `{}`", profile_name, node_name ); + if supports_flakes { Command::new("nix") .arg("build") @@ -260,7 +258,7 @@ async fn push_profile( .await?; } - info!("Copying profile `{} for node `{}`", profile_name, node_name); + debug!("Copying profile `{} for node `{}`", profile_name, node_name); let mut copy_command_ = Command::new("nix"); let mut copy_command = copy_command_.arg("copy"); @@ -360,7 +358,7 @@ async fn push_all_profiles( top_settings: &GenericSettings, check_sigs: bool, ) -> Result<(), Box> { - info!("Deploying all profiles for `{}`", node_name); + info!("Pushing all profiles for `{}`", node_name); let mut profiles_list: Vec<&str> = node.profiles_order.iter().map(|x| x.as_ref()).collect(); -- cgit v1.2.3 From 294a40a4ac3bb098f5a6d77f1323c1e5eca260d6 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 14:30:54 -0700 Subject: some basic modularization --- src/main.rs | 320 ++++-------------------------------------------------------- 1 file changed, 20 insertions(+), 300 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index cd57692..7e96941 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,11 @@ use clap::Clap; -use merge::Merge; -use std::{collections::HashMap, path::PathBuf}; - -use std::borrow::Cow; use std::process::Stdio; use tokio::process::Command; -use std::path::Path; +use merge::Merge; -use std::process; +use std::path::Path; extern crate pretty_env_logger; #[macro_use] @@ -18,12 +14,10 @@ extern crate log; #[macro_use] extern crate serde_derive; -macro_rules! good_panic { - ($($tts:tt)*) => {{ - error!($($tts)*); - process::exit(1); - }} -} +#[macro_use] +mod utils; + +// use utils::*; /// Simple Rust rewrite of a simple Nix Flake deployment tool #[derive(Clap, Debug)] @@ -73,289 +67,13 @@ enum SubCommand { Activate(ActivateOpts), } -#[derive(Deserialize, Debug, Clone, Merge)] -pub struct GenericSettings { - #[serde(rename(deserialize = "sshUser"))] - pub ssh_user: Option, - pub user: Option, - #[serde( - skip_serializing_if = "Vec::is_empty", - default, - rename(deserialize = "sshOpts") - )] - #[merge(strategy = merge::vec::append)] - pub ssh_opts: Vec, - #[serde(rename(deserialize = "fastConnection"))] - pub fast_connection: Option, - #[serde(rename(deserialize = "autoRollback"))] - pub auto_rollback: Option, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct NodeSettings { - pub hostname: String, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct ProfileSettings { - pub path: String, - pub activate: Option, - pub bootstrap: Option, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct Profile { - #[serde(flatten)] - pub profile_settings: ProfileSettings, - #[serde(flatten)] - pub generic_settings: GenericSettings, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct Node { - #[serde(flatten)] - pub generic_settings: GenericSettings, - #[serde(flatten)] - pub node_settings: NodeSettings, - - pub profiles: HashMap, - #[serde( - skip_serializing_if = "Vec::is_empty", - default, - rename(deserialize = "profilesOrder") - )] - pub profiles_order: Vec, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct Data { - #[serde(flatten)] - pub generic_settings: GenericSettings, - pub nodes: HashMap, -} - -struct DeployData<'a> { - pub sudo: Option, - pub ssh_user: Cow<'a, str>, - pub profile_user: Cow<'a, str>, - pub profile_path: String, - pub current_exe: PathBuf, -} - -async fn make_deploy_data<'a>( - profile_name: &str, - node_name: &str, - merged_settings: &'a GenericSettings, -) -> Result, Box> { - let ssh_user: Cow = match &merged_settings.ssh_user { - Some(u) => u.into(), - None => whoami::username().into(), - }; - - let profile_user: Cow = match &merged_settings.user { - Some(x) => x.into(), - None => match &merged_settings.ssh_user { - Some(x) => x.into(), - None => good_panic!( - "Neither user nor sshUser set for profile `{}` of node `{}`", - profile_name, - node_name - ), - }, - }; - - let profile_path = match &profile_user[..] { - "root" => format!("/nix/var/nix/profiles/{}", profile_name), - _ => format!( - "/nix/var/nix/profiles/per-user/{}/{}", - profile_user, profile_name - ), - }; - - let sudo: Option = match merged_settings.user { - Some(ref user) if user != &ssh_user => Some(format!("sudo -u {}", user).into()), - _ => None, - }; - - let current_exe = std::env::current_exe().expect("Expected to find current executable path"); - - if !current_exe.starts_with("/nix/store/") { - good_panic!("The deploy binary must be in the Nix store"); - } - - Ok(DeployData { - sudo, - ssh_user, - profile_user, - profile_path, - current_exe, - }) -} - -async fn push_profile( - profile: &Profile, - profile_name: &str, - node: &Node, - node_name: &str, - supports_flakes: bool, - check_sigs: bool, - repo: &str, - merged_settings: &GenericSettings, - deploy_data: &DeployData<'_>, -) -> Result<(), Box> { - info!( - "Pushing profile `{}` for node `{}`", - profile_name, node_name - ); - - debug!( - "Building profile `{} for node `{}`", - profile_name, node_name - ); - - if supports_flakes { - Command::new("nix") - .arg("build") - .arg("--no-link") - .arg(format!( - "{}#deploy.nodes.{}.profiles.{}.path", - repo, node_name, profile_name - )) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn()? - .await?; - } else { - Command::new("nix-build") - .arg(&repo) - .arg("-A") - .arg(format!( - "deploy.nodes.{}.profiles.{}.path", - node_name, profile_name - )) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn()? - .await?; - } - - if let Ok(local_key) = std::env::var("LOCAL_KEY") { - info!( - "Signing key present! Signing profile `{}` for node `{}`", - profile_name, node_name - ); - - Command::new("nix") - .arg("sign-paths") - .arg("-r") - .arg("-k") - .arg(local_key) - .arg(&profile.profile_settings.path) - .arg(&deploy_data.current_exe) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn()? - .await?; - } - - debug!("Copying profile `{} for node `{}`", profile_name, node_name); - - let mut copy_command_ = Command::new("nix"); - let mut copy_command = copy_command_.arg("copy"); - - if let Some(true) = merged_settings.fast_connection { - copy_command = copy_command.arg("--substitute-on-destination"); - } - - if !check_sigs { - copy_command = copy_command.arg("--no-check-sigs"); - } - - let ssh_opts_str = merged_settings - .ssh_opts - // This should provide some extra safety, but it also breaks for some reason, oh well - // .iter() - // .map(|x| format!("'{}'", x)) - // .collect::>() - .join(" "); - - copy_command - .arg("--to") - .arg(format!( - "ssh://{}@{}", - deploy_data.ssh_user, node.node_settings.hostname - )) - .arg(&profile.profile_settings.path) - .arg(&deploy_data.current_exe) - .env("NIX_SSHOPTS", ssh_opts_str) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn()? - .await?; - - Ok(()) -} - -async fn deploy_profile( - profile: &Profile, - profile_name: &str, - node: &Node, - node_name: &str, - merged_settings: &GenericSettings, - deploy_data: &DeployData<'_>, -) -> Result<(), Box> { - info!( - "Activating profile `{}` for node `{}`", - profile_name, node_name - ); - - let mut self_activate_command = format!( - "{} activate '{}' '{}'", - deploy_data.current_exe.as_path().to_str().unwrap(), - deploy_data.profile_path, - profile.profile_settings.path, - ); - - if let Some(sudo_cmd) = &deploy_data.sudo { - self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); - } - - if let Some(ref bootstrap_cmd) = profile.profile_settings.bootstrap { - self_activate_command = format!( - "{} --bootstrap-cmd '{}'", - self_activate_command, bootstrap_cmd - ); - } - - if let Some(ref activate_cmd) = profile.profile_settings.activate { - self_activate_command = format!( - "{} --activate-cmd '{}'", - self_activate_command, activate_cmd - ); - } - - let mut c = Command::new("ssh"); - let mut ssh_command = c.arg(format!( - "ssh://{}@{}", - deploy_data.ssh_user, node.node_settings.hostname - )); - - for ssh_opt in &merged_settings.ssh_opts { - ssh_command = ssh_command.arg(ssh_opt); - } - - ssh_command.arg(self_activate_command).spawn()?.await?; - - Ok(()) -} - #[inline] async fn push_all_profiles( - node: &Node, + node: &utils::data::Node, node_name: &str, supports_flakes: bool, repo: &str, - top_settings: &GenericSettings, + top_settings: &utils::data::GenericSettings, check_sigs: bool, ) -> Result<(), Box> { info!("Pushing all profiles for `{}`", node_name); @@ -379,9 +97,10 @@ async fn push_all_profiles( merged_settings.merge(node.generic_settings.clone()); merged_settings.merge(profile.generic_settings.clone()); - let deploy_data = make_deploy_data(profile_name, node_name, &merged_settings).await?; + let deploy_data = + utils::make_deploy_data(profile_name, node_name, &merged_settings).await?; - push_profile( + utils::push::push_profile( profile, profile_name, node, @@ -400,9 +119,9 @@ async fn push_all_profiles( #[inline] async fn deploy_all_profiles( - node: &Node, + node: &utils::data::Node, node_name: &str, - top_settings: &GenericSettings, + top_settings: &utils::data::GenericSettings, ) -> Result<(), Box> { info!("Deploying all profiles for `{}`", node_name); @@ -425,9 +144,10 @@ async fn deploy_all_profiles( merged_settings.merge(node.generic_settings.clone()); merged_settings.merge(profile.generic_settings.clone()); - let deploy_data = make_deploy_data(profile_name, node_name, &merged_settings).await?; + let deploy_data = + utils::make_deploy_data(profile_name, node_name, &merged_settings).await?; - deploy_profile( + utils::deploy::deploy_profile( profile, profile_name, node, @@ -513,7 +233,7 @@ async fn main() -> Result<(), Box> { } }; - let data: Data = serde_json::from_str(&data_json)?; + let data: utils::data::Data = serde_json::from_str(&data_json)?; match (maybe_node, maybe_profile) { (Some(node_name), Some(profile_name)) => { @@ -531,9 +251,9 @@ async fn main() -> Result<(), Box> { merged_settings.merge(profile.generic_settings.clone()); let deploy_data = - make_deploy_data(profile_name, node_name, &merged_settings).await?; + utils::make_deploy_data(profile_name, node_name, &merged_settings).await?; - push_profile( + utils::push::push_profile( profile, profile_name, node, @@ -546,7 +266,7 @@ async fn main() -> Result<(), Box> { ) .await?; - deploy_profile( + utils::deploy::deploy_profile( profile, profile_name, node, -- cgit v1.2.3 From 73b99043a71f27f98bf11510fb8db46fa086383c Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 14:36:48 -0700 Subject: minor patches --- src/main.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 7e96941..74f7475 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,6 @@ extern crate serde_derive; #[macro_use] mod utils; -// use utils::*; - /// Simple Rust rewrite of a simple Nix Flake deployment tool #[derive(Clap, Debug)] #[clap(version = "1.0", author = "notgne2 ")] @@ -81,7 +79,7 @@ async fn push_all_profiles( let mut profiles_list: Vec<&str> = node.profiles_order.iter().map(|x| x.as_ref()).collect(); // Add any profiles which weren't in the provided order list - for (profile_name, _) in &node.profiles { + for profile_name in node.profiles.keys() { if !profiles_list.contains(&profile_name.as_str()) { profiles_list.push(&profile_name); } @@ -128,7 +126,7 @@ async fn deploy_all_profiles( let mut profiles_list: Vec<&str> = node.profiles_order.iter().map(|x| x.as_ref()).collect(); // Add any profiles which weren't in the provided order list - for (profile_name, _) in &node.profiles { + for profile_name in node.profiles.keys() { if !profiles_list.contains(&profile_name.as_str()) { profiles_list.push(&profile_name); } @@ -164,7 +162,7 @@ async fn deploy_all_profiles( #[tokio::main] async fn main() -> Result<(), Box> { - if let Err(_) = std::env::var("DEPLOY_LOG") { + if std::env::var("DEPLOY_LOG").is_err() { std::env::set_var("DEPLOY_LOG", "info"); } @@ -403,8 +401,7 @@ async fn main() -> Result<(), Box> { .spawn()? .await?; - // TODO: why are we doing this? - // to run the older version as long as the command is the same? + // TODO: Find some way to make sure this command never changes, otherwise this will not work Command::new("bash") .arg("-c") .arg(&activate_cmd) -- cgit v1.2.3 From 916631d6319aa3125ededd548b174eb188c43e83 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 14:47:05 -0700 Subject: separate and add tests for flake parsing --- src/main.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 22 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 74f7475..7818652 100644 --- a/src/main.rs +++ b/src/main.rs @@ -159,6 +159,68 @@ async fn deploy_all_profiles( Ok(()) } +#[derive(PartialEq, Debug)] +struct DeployFlake<'a> { + repo: &'a str, + node: Option<&'a str>, + profile: Option<&'a str>, +} + +fn parse_flake(flake: &str) -> DeployFlake { + let flake_fragment_start = flake.find('#'); + let (repo, maybe_fragment) = match flake_fragment_start { + Some(s) => (&flake[..s], Some(&flake[s + 1..])), + None => (flake, None), + }; + + let (node, profile) = match maybe_fragment { + Some(fragment) => { + let fragment_profile_start = fragment.find('.'); + match fragment_profile_start { + Some(s) => (Some(&fragment[..s]), Some(&fragment[s + 1..])), + None => (Some(fragment), None), + } + } + None => (None, None), + }; + + DeployFlake { + repo, + node, + profile, + } +} + +#[test] +fn test_parse_flake() { + assert_eq!( + parse_flake("../deploy/examples/system#example"), + DeployFlake { + repo: "../deploy/examples/system", + node: Some("example"), + profile: None + } + ); + + assert_eq!( + parse_flake("../deploy/examples/system#example.system"), + DeployFlake { + repo: "../deploy/examples/system", + node: Some("example"), + profile: Some("system") + } + ); + + assert_eq!( + parse_flake("../deploy/examples/system"), + DeployFlake { + repo: "../deploy/examples/system", + node: None, + profile: None, + } + ); +} + #[tokio::main] async fn main() -> Result<(), Box> { @@ -172,22 +234,7 @@ async fn main() -> Result<(), Box> { match opts.subcmd { SubCommand::Deploy(deploy_opts) => { - let flake_fragment_start = deploy_opts.flake.find('#'); - let (repo, maybe_fragment) = match flake_fragment_start { - Some(s) => (&deploy_opts.flake[..s], Some(&deploy_opts.flake[s + 1..])), - None => (deploy_opts.flake.as_str(), None), - }; - - let (maybe_node, maybe_profile) = match maybe_fragment { - Some(fragment) => { - let fragment_profile_start = fragment.find('.'); - match fragment_profile_start { - Some(s) => (Some(&fragment[..s]), Some(&fragment[s + 1..])), - None => (Some(fragment), None), - } - } - None => (None, None), - }; + let deploy_flake = parse_flake(deploy_opts.flake.as_str()); let test_flake_status = Command::new("nix") .arg("eval") @@ -205,7 +252,7 @@ async fn main() -> Result<(), Box> { let c = Command::new("nix") .arg("eval") .arg("--json") - .arg(format!("{}#deploy", repo)) + .arg(format!("{}#deploy", deploy_flake.repo)) .stdout(Stdio::null()) .stderr(Stdio::null()) // TODO forward input args? @@ -221,7 +268,7 @@ async fn main() -> Result<(), Box> { .arg("--json") .arg("--eval") .arg("--E") - .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo)) + .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", deploy_flake.repo)) .stdout(Stdio::null()) .stderr(Stdio::null()) .output() @@ -233,7 +280,7 @@ async fn main() -> Result<(), Box> { let data: utils::data::Data = serde_json::from_str(&data_json)?; - match (maybe_node, maybe_profile) { + match (deploy_flake.node, deploy_flake.profile) { (Some(node_name), Some(profile_name)) => { let node = match data.nodes.get(node_name) { Some(x) => x, @@ -258,7 +305,7 @@ async fn main() -> Result<(), Box> { node_name, supports_flakes, deploy_opts.checksigs, - repo, + deploy_flake.repo, &merged_settings, &deploy_data, ) @@ -284,7 +331,7 @@ async fn main() -> Result<(), Box> { node, node_name, supports_flakes, - repo, + deploy_flake.repo, &data.generic_settings, deploy_opts.checksigs, ) @@ -300,7 +347,7 @@ async fn main() -> Result<(), Box> { node, node_name, supports_flakes, - repo, + deploy_flake.repo, &data.generic_settings, deploy_opts.checksigs, ) -- cgit v1.2.3 From 889fb0d3f9eee9085883fe1f31e05b07be0939ec Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 15:00:16 -0700 Subject: separate out activation logic --- src/main.rs | 169 +++--------------------------------------------------------- 1 file changed, 8 insertions(+), 161 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 7818652..cde4fc3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,6 @@ use tokio::process::Command; use merge::Merge; -use std::path::Path; - extern crate pretty_env_logger; #[macro_use] extern crate log; @@ -158,69 +156,6 @@ async fn deploy_all_profiles( Ok(()) } - -#[derive(PartialEq, Debug)] -struct DeployFlake<'a> { - repo: &'a str, - node: Option<&'a str>, - profile: Option<&'a str>, -} - -fn parse_flake(flake: &str) -> DeployFlake { - let flake_fragment_start = flake.find('#'); - let (repo, maybe_fragment) = match flake_fragment_start { - Some(s) => (&flake[..s], Some(&flake[s + 1..])), - None => (flake, None), - }; - - let (node, profile) = match maybe_fragment { - Some(fragment) => { - let fragment_profile_start = fragment.find('.'); - match fragment_profile_start { - Some(s) => (Some(&fragment[..s]), Some(&fragment[s + 1..])), - None => (Some(fragment), None), - } - } - None => (None, None), - }; - - DeployFlake { - repo, - node, - profile, - } -} - -#[test] -fn test_parse_flake() { - assert_eq!( - parse_flake("../deploy/examples/system#example"), - DeployFlake { - repo: "../deploy/examples/system", - node: Some("example"), - profile: None - } - ); - - assert_eq!( - parse_flake("../deploy/examples/system#example.system"), - DeployFlake { - repo: "../deploy/examples/system", - node: Some("example"), - profile: Some("system") - } - ); - - assert_eq!( - parse_flake("../deploy/examples/system"), - DeployFlake { - repo: "../deploy/examples/system", - node: None, - profile: None, - } - ); -} - #[tokio::main] async fn main() -> Result<(), Box> { @@ -234,7 +169,7 @@ async fn main() -> Result<(), Box> { match opts.subcmd { SubCommand::Deploy(deploy_opts) => { - let deploy_flake = parse_flake(deploy_opts.flake.as_str()); + let deploy_flake = utils::parse_flake(deploy_opts.flake.as_str()); let test_flake_status = Command::new("nix") .arg("eval") @@ -364,102 +299,14 @@ async fn main() -> Result<(), Box> { }; } SubCommand::Activate(activate_opts) => { - info!("Activating profile"); - - Command::new("nix-env") - .arg("-p") - .arg(&activate_opts.profile_path) - .arg("--set") - .arg(&activate_opts.closure) - .stdout(Stdio::null()) - .spawn()? - .await?; - - if let (Some(bootstrap_cmd), false) = ( + utils::activate::activate( + activate_opts.profile_path, + activate_opts.closure, + activate_opts.activate_cmd, activate_opts.bootstrap_cmd, - !Path::new(&activate_opts.profile_path).exists(), - ) { - let bootstrap_status = Command::new("bash") - .arg("-c") - .arg(&bootstrap_cmd) - .env("PROFILE", &activate_opts.profile_path) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .await; - - match bootstrap_status { - Ok(s) if s.success() => (), - _ => { - tokio::fs::remove_file(&activate_opts.profile_path).await?; - good_panic!("Failed to execute bootstrap command"); - } - } - } - - if let Some(activate_cmd) = activate_opts.activate_cmd { - let activate_status = Command::new("bash") - .arg("-c") - .arg(&activate_cmd) - .env("PROFILE", &activate_opts.profile_path) - .status() - .await; - - match activate_status { - Ok(s) if s.success() => (), - _ if activate_opts.auto_rollback => { - Command::new("nix-env") - .arg("-p") - .arg(&activate_opts.profile_path) - .arg("--rollback") - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn()? - .await?; - - let c = Command::new("nix-env") - .arg("-p") - .arg(&activate_opts.profile_path) - .arg("--list-generations") - .output() - .await?; - let generations_list = String::from_utf8(c.stdout)?; - - let last_generation_line = generations_list - .lines() - .last() - .expect("Expected to find a generation in list"); - - let last_generation_id = last_generation_line - .split_whitespace() - .next() - .expect("Expected to get ID from generation entry"); - - debug!("Removing generation entry {}", last_generation_line); - warn!("Removing generation by ID {}", last_generation_id); - - Command::new("nix-env") - .arg("-p") - .arg(&activate_opts.profile_path) - .arg("--delete-generations") - .arg(last_generation_id) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn()? - .await?; - - // TODO: Find some way to make sure this command never changes, otherwise this will not work - Command::new("bash") - .arg("-c") - .arg(&activate_cmd) - .spawn()? - .await?; - - good_panic!("Failed to execute activation command"); - } - _ => {} - } - } + activate_opts.auto_rollback, + ) + .await?; } } -- cgit v1.2.3 From a22063343e54da9f589c7235f2f64b57fe5c257b Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 15:12:42 -0700 Subject: More functions --- src/main.rs | 89 ++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 40 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index cde4fc3..e084f0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,64 +156,73 @@ async fn deploy_all_profiles( Ok(()) } -#[tokio::main] - -async fn main() -> Result<(), Box> { - 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(); - - match opts.subcmd { - SubCommand::Deploy(deploy_opts) => { - let deploy_flake = utils::parse_flake(deploy_opts.flake.as_str()); +async fn test_flake_support() -> Result> { + Ok(Command::new("nix") + .arg("eval") + .arg("--expr") + .arg("builtins.getFlake") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .await? + .success()) +} - let test_flake_status = Command::new("nix") +async fn get_deployment_data( + supports_flakes: bool, + repo: &str, +) -> Result> { + let data_json = match supports_flakes { + true => { + let c = Command::new("nix") .arg("eval") - .arg("--expr") - .arg("builtins.getFlake") + .arg("--json") + .arg(format!("{}#deploy", repo)) .stdout(Stdio::null()) .stderr(Stdio::null()) - .status() + // TODO forward input args? + .output() .await?; - let supports_flakes = test_flake_status.success(); - - let data_json = match supports_flakes { - true => { - let c = Command::new("nix") - .arg("eval") - .arg("--json") - .arg(format!("{}#deploy", deploy_flake.repo)) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - // TODO forward input args? - .output() - .await?; - - String::from_utf8(c.stdout)? - } - false => { - let c = Command::new("nix-instanciate") + String::from_utf8(c.stdout)? + } + false => { + let c = Command::new("nix-instanciate") .arg("--strict") .arg("--read-write-mode") .arg("--json") .arg("--eval") .arg("--E") - .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", deploy_flake.repo)) + .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo)) .stdout(Stdio::null()) .stderr(Stdio::null()) .output() .await?; - String::from_utf8(c.stdout)? - } - }; + String::from_utf8(c.stdout)? + } + }; + + Ok(serde_json::from_str(&data_json)?) +} +#[tokio::main] +async fn main() -> Result<(), Box> { + 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(); + + 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: utils::data::Data = serde_json::from_str(&data_json)?; + let data = get_deployment_data(supports_flakes, deploy_flake.repo).await?; match (deploy_flake.node, deploy_flake.profile) { (Some(node_name), Some(profile_name)) => { -- cgit v1.2.3 From 239d0f8999b47e9e76589ee1fa2d9f3459c47335 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 15:45:53 -0700 Subject: use separate binary for activation, more cleanup --- src/main.rs | 220 ++++++++++++++++++++++++------------------------------------ 1 file changed, 87 insertions(+), 133 deletions(-) (limited to 'src/main.rs') 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 ")] 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, - - /// Command for bootstrapping - #[clap(short, long)] - bootstrap_cmd: Option, - - /// 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> { Ok(Command::new("nix") .arg("eval") @@ -169,6 +135,7 @@ async fn test_flake_support() -> Result> { .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> { 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(()) } -- cgit v1.2.3 From edaed825650eea32878441d3b8c7eb40e8877882 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 28 Sep 2020 16:17:31 -0700 Subject: Add examples --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 6dcb885..513fbf2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,6 +123,7 @@ async fn deploy_all_profiles( } /// Returns if the available Nix installation supports flakes +#[inline] async fn test_flake_support() -> Result> { Ok(Command::new("nix") .arg("eval") @@ -136,6 +137,7 @@ async fn test_flake_support() -> Result> { } /// Evaluates the Nix in the given `repo` and return the processed Data from it +#[inline] async fn get_deployment_data( supports_flakes: bool, repo: &str, -- cgit v1.2.3 From 93a04f7e3037f69bdeab777d2fc6c4fb37795f4e Mon Sep 17 00:00:00 2001 From: notgne2 Date: Tue, 29 Sep 2020 12:36:26 -0700 Subject: Pass extra arguments to the Nix build command --- src/main.rs | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 513fbf2..668b697 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,8 @@ struct Opts { /// Check signatures when using `nix copy` #[clap(short, long)] checksigs: bool, + /// Extra arguments to be passed to nix build + extra_build_args: Vec, } #[inline] @@ -141,38 +143,47 @@ async fn test_flake_support() -> Result> { async fn get_deployment_data( supports_flakes: bool, repo: &str, + extra_build_args: Vec, ) -> Result> { - let data_json = match supports_flakes { - true => { - let c = Command::new("nix") - .arg("eval") - .arg("--json") - .arg(format!("{}#deploy", repo)) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - // TODO forward input args? - .output() - .await?; + let mut c = match supports_flakes { + true => Command::new("nix"), + false => Command::new("nix-instanciate"), + }; - String::from_utf8(c.stdout)? + let mut build_command = match supports_flakes { + true => { + c.arg("eval").arg("--json").arg(format!("{}#deploy", repo)) } false => { - let c = Command::new("nix-instanciate") + c .arg("--strict") .arg("--read-write-mode") .arg("--json") .arg("--eval") .arg("--E") .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo)) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .output() - .await?; - - String::from_utf8(c.stdout)? } }; + for extra_arg in extra_build_args { + build_command = build_command.arg(extra_arg); + } + + let build_output = build_command + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .output() + .await?; + + if !build_output.status.success() { + good_panic!( + "Error building deploy props for the provided flake: {}", + repo + ); + } + + let data_json = String::from_utf8(build_output.stdout)?; + Ok(serde_json::from_str(&data_json)?) } #[tokio::main] @@ -189,7 +200,8 @@ async fn main() -> Result<(), Box> { let supports_flakes = test_flake_support().await?; - let data = get_deployment_data(supports_flakes, deploy_flake.repo).await?; + let data = + get_deployment_data(supports_flakes, deploy_flake.repo, opts.extra_build_args).await?; match (deploy_flake.node, deploy_flake.profile) { (Some(node_name), Some(profile_name)) => { -- cgit v1.2.3 From 8d21dd335e5259dadf832a5d1a7c72b9dd1f4400 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Tue, 29 Sep 2020 15:10:06 -0700 Subject: Add license information, reformat Nix files, clean up --- src/main.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 668b697..9e50674 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2020 Serokell +// +// SPDX-License-Identifier: MPL-2.0 + use clap::Clap; use std::process::Stdio; @@ -17,7 +21,7 @@ mod utils; /// Simple Rust rewrite of a simple Nix Flake deployment tool #[derive(Clap, Debug)] -#[clap(version = "1.0", author = "notgne2 ")] +#[clap(version = "1.0", author = "Serokell ")] struct Opts { /// The flake to deploy #[clap(default_value = ".")] -- cgit v1.2.3 From a0328dbcf76b7c551e92fd25060cfc7d7e4d9ebe Mon Sep 17 00:00:00 2001 From: notgne2 Date: Tue, 29 Sep 2020 21:27:49 -0700 Subject: More separation and component testing --- src/main.rs | 57 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 23 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 668b697..f8b03a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -143,7 +143,7 @@ async fn test_flake_support() -> Result> { async fn get_deployment_data( supports_flakes: bool, repo: &str, - extra_build_args: Vec, + extra_build_args: &[String], ) -> Result> { let mut c = match supports_flakes { true => Command::new("nix"), @@ -156,12 +156,12 @@ async fn get_deployment_data( } false => { c - .arg("--strict") - .arg("--read-write-mode") - .arg("--json") - .arg("--eval") - .arg("--E") - .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo)) + .arg("--strict") + .arg("--read-write-mode") + .arg("--json") + .arg("--eval") + .arg("--E") + .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo)) } }; @@ -186,23 +186,13 @@ async fn get_deployment_data( Ok(serde_json::from_str(&data_json)?) } -#[tokio::main] -async fn main() -> Result<(), Box> { - 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(); - - 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, opts.extra_build_args).await?; +async fn run_deploy( + deploy_flake: utils::DeployFlake<'_>, + data: utils::data::Data, + supports_flakes: bool, + opts: &Opts, +) -> Result<(), Box> { match (deploy_flake.node, deploy_flake.profile) { (Some(node_name), Some(profile_name)) => { let node = match data.nodes.get(node_name) { @@ -289,3 +279,24 @@ async fn main() -> Result<(), Box> { Ok(()) } +#[tokio::main] +async fn main() -> Result<(), Box> { + 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(); + + 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, &opts.extra_build_args).await?; + + run_deploy(deploy_flake, data, supports_flakes, &opts).await?; + + Ok(()) +} -- cgit v1.2.3 From ea5aab76849ba3ce9ff2b7eba2a391d4ea33fa3a Mon Sep 17 00:00:00 2001 From: notgne2 Date: Thu, 1 Oct 2020 12:43:33 -0700 Subject: Improve nix copy stuff --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 75842e5..ba87e46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -149,6 +149,8 @@ async fn get_deployment_data( repo: &str, extra_build_args: &[String], ) -> Result> { + info!("Evaluating flake in {}", repo); + let mut c = match supports_flakes { true => Command::new("nix"), false => Command::new("nix-instanciate"), -- cgit v1.2.3 From e14acaf2bdc14bbdc30f3d558b62f64fe33ff5f9 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Thu, 1 Oct 2020 18:21:40 -0700 Subject: Rework system for deploy properties, add CLI override flags --- src/main.rs | 140 +++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 45 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index ba87e46..0d80e42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,25 @@ struct Opts { checksigs: bool, /// Extra arguments to be passed to nix build extra_build_args: Vec, + + /// Override the SSH user with the given value + #[clap(long)] + ssh_user: Option, + /// Override the profile user with the given value + #[clap(long)] + profile_user: Option, + /// Override the SSH options used + #[clap(long)] + ssh_opts: Option, + /// Override if the connecting to the target node should be considered fast + #[clap(long)] + fast_connection: Option, + /// Override if a rollback should be attempted if activation fails + #[clap(long)] + auto_rollback: Option, + /// Override hostname used for the node + #[clap(long)] + hostname: Option, } #[inline] @@ -41,6 +60,7 @@ async fn push_all_profiles( repo: &str, top_settings: &utils::data::GenericSettings, check_sigs: bool, + cmd_overrides: &utils::CmdOverrides, ) -> Result<(), Box> { info!("Pushing all profiles for `{}`", node_name); @@ -63,19 +83,23 @@ async fn push_all_profiles( 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, + let deploy_data = utils::make_deploy_data( + top_settings, node, node_name, + profile, + profile_name, + cmd_overrides, + )?; + + let deploy_defs = deploy_data.defs(); + + utils::push::push_profile( supports_flakes, check_sigs, repo, - &merged_settings, &deploy_data, + &deploy_defs, ) .await?; } @@ -88,6 +112,7 @@ async fn deploy_all_profiles( node: &utils::data::Node, node_name: &str, top_settings: &utils::data::GenericSettings, + cmd_overrides: &utils::CmdOverrides, ) -> Result<(), Box> { info!("Deploying all profiles for `{}`", node_name); @@ -110,19 +135,18 @@ async fn deploy_all_profiles( 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::deploy::deploy_profile( - profile, - profile_name, + let deploy_data = utils::make_deploy_data( + top_settings, node, node_name, - &merged_settings, - &deploy_data, - merged_settings.auto_rollback, - ) - .await?; + profile, + profile_name, + cmd_overrides, + )?; + + let deploy_defs = deploy_data.defs(); + + utils::deploy::deploy_profile(&deploy_data, &deploy_defs).await?; } Ok(()) @@ -197,7 +221,8 @@ async fn run_deploy( deploy_flake: utils::DeployFlake<'_>, data: utils::data::Data, supports_flakes: bool, - opts: &Opts, + check_sigs: bool, + cmd_overrides: utils::CmdOverrides, ) -> Result<(), Box> { match (deploy_flake.node, deploy_flake.profile) { (Some(node_name), Some(profile_name)) => { @@ -210,36 +235,27 @@ async fn run_deploy( 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( + &data.generic_settings, + node, + node_name, + profile, + profile_name, + &cmd_overrides, + )?; - let deploy_data = - utils::make_deploy_data(profile_name, node_name, &merged_settings).await?; + let deploy_defs = deploy_data.defs(); utils::push::push_profile( - profile, - profile_name, - node, - node_name, supports_flakes, - opts.checksigs, + check_sigs, deploy_flake.repo, - &merged_settings, &deploy_data, + &deploy_defs, ) .await?; - utils::deploy::deploy_profile( - profile, - profile_name, - node, - node_name, - &merged_settings, - &deploy_data, - merged_settings.auto_rollback, - ) - .await?; + utils::deploy::deploy_profile(&deploy_data, &deploy_defs).await?; } (Some(node_name), None) => { let node = match data.nodes.get(node_name) { @@ -253,11 +269,12 @@ async fn run_deploy( supports_flakes, deploy_flake.repo, &data.generic_settings, - opts.checksigs, + check_sigs, + &cmd_overrides, ) .await?; - deploy_all_profiles(node, node_name, &data.generic_settings).await?; + deploy_all_profiles(node, node_name, &data.generic_settings, &cmd_overrides).await?; } (None, None) => { info!("Deploying all profiles on all nodes"); @@ -269,13 +286,15 @@ async fn run_deploy( supports_flakes, deploy_flake.repo, &data.generic_settings, - opts.checksigs, + check_sigs, + &cmd_overrides, ) .await?; } for (node_name, node) in &data.nodes { - deploy_all_profiles(node, node_name, &data.generic_settings).await?; + deploy_all_profiles(node, node_name, &data.generic_settings, &cmd_overrides) + .await?; } } (None, Some(_)) => { @@ -285,6 +304,7 @@ async fn run_deploy( Ok(()) } + #[tokio::main] async fn main() -> Result<(), Box> { if std::env::var("DEPLOY_LOG").is_err() { @@ -297,12 +317,42 @@ async fn main() -> Result<(), Box> { let deploy_flake = utils::parse_flake(opts.flake.as_str()); + let cmd_overrides = utils::CmdOverrides { + ssh_user: opts.ssh_user, + profile_user: opts.profile_user, + ssh_opts: opts.ssh_opts, + fast_connection: opts.fast_connection, + auto_rollback: opts.auto_rollback, + hostname: opts.hostname, + }; + + match (cmd_overrides.purity(), deploy_flake.node, deploy_flake.profile) { + (utils::OverridePurity::ErrorProfile, _, None) => good_panic!( + "You have specified an override not suitible for deploying to multiple profiles, please specify your target profile explicitly" + ), + (utils::OverridePurity::Error, None, _) => good_panic!( + "You have specified an override not suitible for deploying to multiple nodes, please specify your target node explicitly" + ), + + (utils::OverridePurity::Warn, None, _) => warn!( + "Certain overrides you have provided might be dangerous when used on multiple nodes or profiles, be cautious" + ), + _ => (), + }; + let supports_flakes = test_flake_support().await?; let data = get_deployment_data(supports_flakes, deploy_flake.repo, &opts.extra_build_args).await?; - run_deploy(deploy_flake, data, supports_flakes, &opts).await?; + run_deploy( + deploy_flake, + data, + supports_flakes, + opts.checksigs, + cmd_overrides, + ) + .await?; Ok(()) } -- cgit v1.2.3 From 7c00fd2761e6efffe763ece5d08d9a6d3fb95092 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 5 Oct 2020 19:46:28 -0700 Subject: Add interface with json schema, fix flake-less issues, put setActivate and jsonSchema check in flake lib --- src/main.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 0d80e42..219c3e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use tokio::process::Command; use merge::Merge; extern crate pretty_env_logger; + #[macro_use] extern crate log; @@ -177,12 +178,14 @@ async fn get_deployment_data( let mut c = match supports_flakes { true => Command::new("nix"), - false => Command::new("nix-instanciate"), + false => Command::new("nix-instantiate"), }; let mut build_command = match supports_flakes { true => { - c.arg("eval").arg("--json").arg(format!("{}#deploy", repo)) + c.arg("eval") + .arg("--json") + .arg(format!("{}#deploy", repo)) } false => { c @@ -190,7 +193,7 @@ async fn get_deployment_data( .arg("--read-write-mode") .arg("--json") .arg("--eval") - .arg("--E") + .arg("-E") .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo)) } }; -- cgit v1.2.3 From aabcf6b77d4159100a49b143cbb8da4bad194f14 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 5 Oct 2020 20:10:41 -0700 Subject: Improve schema a bit, fix flake locks for examples --- src/main.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 219c3e5..fb3fb66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,17 +65,22 @@ async fn push_all_profiles( ) -> Result<(), Box> { info!("Pushing all profiles for `{}`", node_name); - let mut profiles_list: Vec<&str> = node.profiles_order.iter().map(|x| x.as_ref()).collect(); + let mut profiles_list: Vec<&str> = node + .node_settings + .profiles_order + .iter() + .map(|x| x.as_ref()) + .collect(); // Add any profiles which weren't in the provided order list - for profile_name in node.profiles.keys() { + for profile_name in node.node_settings.profiles.keys() { if !profiles_list.contains(&profile_name.as_str()) { profiles_list.push(&profile_name); } } for profile_name in profiles_list { - let profile = match node.profiles.get(profile_name) { + let profile = match node.node_settings.profiles.get(profile_name) { Some(x) => x, None => good_panic!("No profile was found named `{}`", profile_name), }; @@ -117,17 +122,22 @@ async fn deploy_all_profiles( ) -> Result<(), Box> { info!("Deploying all profiles for `{}`", node_name); - let mut profiles_list: Vec<&str> = node.profiles_order.iter().map(|x| x.as_ref()).collect(); + let mut profiles_list: Vec<&str> = node + .node_settings + .profiles_order + .iter() + .map(|x| x.as_ref()) + .collect(); // Add any profiles which weren't in the provided order list - for profile_name in node.profiles.keys() { + for profile_name in node.node_settings.profiles.keys() { if !profiles_list.contains(&profile_name.as_str()) { profiles_list.push(&profile_name); } } for profile_name in profiles_list { - let profile = match node.profiles.get(profile_name) { + let profile = match node.node_settings.profiles.get(profile_name) { Some(x) => x, None => good_panic!("No profile was found named `{}`", profile_name), }; @@ -203,8 +213,8 @@ async fn get_deployment_data( } let build_output = build_command - .stdout(Stdio::null()) - .stderr(Stdio::null()) + // .stdout(Stdio::null()) + // .stderr(Stdio::null()) .output() .await?; @@ -233,7 +243,7 @@ async fn run_deploy( Some(x) => x, None => good_panic!("No node was found named `{}`", node_name), }; - let profile = match node.profiles.get(profile_name) { + let profile = match node.node_settings.profiles.get(profile_name) { Some(x) => x, None => good_panic!("No profile was found named `{}`", profile_name), }; -- cgit v1.2.3 From 3a92593bf9c4ca07a2b09888e4a3f7dff6c9c510 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Tue, 6 Oct 2020 11:08:40 -0700 Subject: Add skip-push flag --- src/main.rs | 61 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 25 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index fb3fb66..04d7868 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,6 +51,9 @@ struct Opts { /// Override hostname used for the node #[clap(long)] hostname: Option, + /// Skip pushing step (useful for local testing) + #[clap(short, long)] + skip_push: bool, } #[inline] @@ -235,6 +238,7 @@ async fn run_deploy( data: utils::data::Data, supports_flakes: bool, check_sigs: bool, + skip_push: bool, cmd_overrides: utils::CmdOverrides, ) -> Result<(), Box> { match (deploy_flake.node, deploy_flake.profile) { @@ -259,14 +263,16 @@ async fn run_deploy( let deploy_defs = deploy_data.defs(); - utils::push::push_profile( - supports_flakes, - check_sigs, - deploy_flake.repo, - &deploy_data, - &deploy_defs, - ) - .await?; + if !skip_push { + utils::push::push_profile( + supports_flakes, + check_sigs, + deploy_flake.repo, + &deploy_data, + &deploy_defs, + ) + .await?; + } utils::deploy::deploy_profile(&deploy_data, &deploy_defs).await?; } @@ -276,23 +282,7 @@ async fn run_deploy( None => good_panic!("No node was found named `{}`", node_name), }; - push_all_profiles( - node, - node_name, - supports_flakes, - deploy_flake.repo, - &data.generic_settings, - check_sigs, - &cmd_overrides, - ) - .await?; - - deploy_all_profiles(node, node_name, &data.generic_settings, &cmd_overrides).await?; - } - (None, None) => { - info!("Deploying all profiles on all nodes"); - - for (node_name, node) in &data.nodes { + if !skip_push { push_all_profiles( node, node_name, @@ -305,6 +295,26 @@ async fn run_deploy( .await?; } + deploy_all_profiles(node, node_name, &data.generic_settings, &cmd_overrides).await?; + } + (None, None) => { + info!("Deploying all profiles on all nodes"); + + if !skip_push { + for (node_name, node) in &data.nodes { + push_all_profiles( + node, + node_name, + supports_flakes, + deploy_flake.repo, + &data.generic_settings, + check_sigs, + &cmd_overrides, + ) + .await?; + } + } + for (node_name, node) in &data.nodes { deploy_all_profiles(node, node_name, &data.generic_settings, &cmd_overrides) .await?; @@ -363,6 +373,7 @@ async fn main() -> Result<(), Box> { data, supports_flakes, opts.checksigs, + opts.skip_push, cmd_overrides, ) .await?; -- cgit v1.2.3 From 518f7f5b4f1db83cab61941ab8887b0df76ce8d8 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Thu, 8 Oct 2020 18:13:26 -0700 Subject: Update documentation --- src/main.rs | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 04d7868..b28a520 100644 --- a/src/main.rs +++ b/src/main.rs @@ -349,20 +349,6 @@ async fn main() -> Result<(), Box> { hostname: opts.hostname, }; - match (cmd_overrides.purity(), deploy_flake.node, deploy_flake.profile) { - (utils::OverridePurity::ErrorProfile, _, None) => good_panic!( - "You have specified an override not suitible for deploying to multiple profiles, please specify your target profile explicitly" - ), - (utils::OverridePurity::Error, None, _) => good_panic!( - "You have specified an override not suitible for deploying to multiple nodes, please specify your target node explicitly" - ), - - (utils::OverridePurity::Warn, None, _) => warn!( - "Certain overrides you have provided might be dangerous when used on multiple nodes or profiles, be cautious" - ), - _ => (), - }; - let supports_flakes = test_flake_support().await?; let data = -- cgit v1.2.3 From ea717911bac5ff29d730d80d4b774fe17ed1e851 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Tue, 13 Oct 2020 19:06:40 -0700 Subject: Clean up some CLI arguments, make magic rollback optional --- src/main.rs | 73 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 36 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index b28a520..cedf684 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,9 +51,15 @@ struct Opts { /// Override hostname used for the node #[clap(long)] hostname: Option, - /// Skip pushing step (useful for local testing) - #[clap(short, long)] - skip_push: bool, + /// Make activation wait for confirmation, or roll back after a period of time + #[clap(long)] + magic_rollback: Option, + /// How long activation should wait for confirmation (if using magic-rollback) + #[clap(long)] + confirm_timeout: Option, + /// Where to store temporary files (only used by magic-rollback) + #[clap(long)] + temp_path: Option, } #[inline] @@ -238,7 +244,6 @@ async fn run_deploy( data: utils::data::Data, supports_flakes: bool, check_sigs: bool, - skip_push: bool, cmd_overrides: utils::CmdOverrides, ) -> Result<(), Box> { match (deploy_flake.node, deploy_flake.profile) { @@ -263,16 +268,14 @@ async fn run_deploy( let deploy_defs = deploy_data.defs(); - if !skip_push { - utils::push::push_profile( - supports_flakes, - check_sigs, - deploy_flake.repo, - &deploy_data, - &deploy_defs, - ) - .await?; - } + utils::push::push_profile( + supports_flakes, + check_sigs, + deploy_flake.repo, + &deploy_data, + &deploy_defs, + ) + .await?; utils::deploy::deploy_profile(&deploy_data, &deploy_defs).await?; } @@ -282,7 +285,23 @@ async fn run_deploy( None => good_panic!("No node was found named `{}`", node_name), }; - if !skip_push { + push_all_profiles( + node, + node_name, + supports_flakes, + deploy_flake.repo, + &data.generic_settings, + check_sigs, + &cmd_overrides, + ) + .await?; + + deploy_all_profiles(node, node_name, &data.generic_settings, &cmd_overrides).await?; + } + (None, None) => { + info!("Deploying all profiles on all nodes"); + + for (node_name, node) in &data.nodes { push_all_profiles( node, node_name, @@ -295,26 +314,6 @@ async fn run_deploy( .await?; } - deploy_all_profiles(node, node_name, &data.generic_settings, &cmd_overrides).await?; - } - (None, None) => { - info!("Deploying all profiles on all nodes"); - - if !skip_push { - for (node_name, node) in &data.nodes { - push_all_profiles( - node, - node_name, - supports_flakes, - deploy_flake.repo, - &data.generic_settings, - check_sigs, - &cmd_overrides, - ) - .await?; - } - } - for (node_name, node) in &data.nodes { deploy_all_profiles(node, node_name, &data.generic_settings, &cmd_overrides) .await?; @@ -347,6 +346,9 @@ async fn main() -> Result<(), Box> { fast_connection: opts.fast_connection, auto_rollback: opts.auto_rollback, hostname: opts.hostname, + magic_rollback: opts.magic_rollback, + temp_path: opts.temp_path, + confirm_timeout: opts.confirm_timeout, }; let supports_flakes = test_flake_support().await?; @@ -359,7 +361,6 @@ async fn main() -> Result<(), Box> { data, supports_flakes, opts.checksigs, - opts.skip_push, cmd_overrides, ) .await?; -- cgit v1.2.3 From 47e94f8dfd2ec9c6aacc6ba8d74f629e70433567 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Fri, 23 Oct 2020 21:54:13 -0700 Subject: Warn when flakes are not available, forward stderr of Nix evaluation --- src/main.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index cedf684..5d76e3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -221,11 +221,11 @@ async fn get_deployment_data( build_command = build_command.arg(extra_arg); } - let build_output = build_command - // .stdout(Stdio::null()) - // .stderr(Stdio::null()) - .output() - .await?; + let build_child = build_command + .stdout(Stdio::piped()) + .spawn()?; + + let build_output = build_child.wait_with_output().await?; if !build_output.status.success() { good_panic!( @@ -353,6 +353,10 @@ async fn main() -> Result<(), Box> { let supports_flakes = test_flake_support().await?; + if !supports_flakes { + warn!("A Nix version without flakes support was detected, support for this is work in progress"); + } + let data = get_deployment_data(supports_flakes, deploy_flake.repo, &opts.extra_build_args).await?; -- cgit v1.2.3 From 72b066b293befec048f6a1b2f8d7a4b103ae4edf Mon Sep 17 00:00:00 2001 From: notgne2 Date: Fri, 23 Oct 2020 22:44:52 -0700 Subject: Add an option to keep build results --- src/main.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 5d76e3d..1358a83 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,13 @@ struct Opts { /// Extra arguments to be passed to nix build extra_build_args: Vec, + /// Keep the build outputs of each built profile + #[clap(short, long)] + keep_result: bool, + /// Location to keep outputs from built profiles in + #[clap(short, long)] + result_path: Option, + /// Override the SSH user with the given value #[clap(long)] ssh_user: Option, @@ -71,6 +78,8 @@ async fn push_all_profiles( top_settings: &utils::data::GenericSettings, check_sigs: bool, cmd_overrides: &utils::CmdOverrides, + keep_result: bool, + result_path: Option<&str>, ) -> Result<(), Box> { info!("Pushing all profiles for `{}`", node_name); @@ -115,6 +124,8 @@ async fn push_all_profiles( repo, &deploy_data, &deploy_defs, + keep_result, + result_path, ) .await?; } @@ -221,9 +232,7 @@ async fn get_deployment_data( build_command = build_command.arg(extra_arg); } - let build_child = build_command - .stdout(Stdio::piped()) - .spawn()?; + let build_child = build_command.stdout(Stdio::piped()).spawn()?; let build_output = build_child.wait_with_output().await?; @@ -245,6 +254,8 @@ async fn run_deploy( supports_flakes: bool, check_sigs: bool, cmd_overrides: utils::CmdOverrides, + keep_result: bool, + result_path: Option<&str>, ) -> Result<(), Box> { match (deploy_flake.node, deploy_flake.profile) { (Some(node_name), Some(profile_name)) => { @@ -274,6 +285,8 @@ async fn run_deploy( deploy_flake.repo, &deploy_data, &deploy_defs, + keep_result, + result_path, ) .await?; @@ -293,6 +306,8 @@ async fn run_deploy( &data.generic_settings, check_sigs, &cmd_overrides, + keep_result, + result_path, ) .await?; @@ -310,6 +325,8 @@ async fn run_deploy( &data.generic_settings, check_sigs, &cmd_overrides, + keep_result, + result_path, ) .await?; } @@ -360,12 +377,16 @@ async fn main() -> Result<(), Box> { let data = get_deployment_data(supports_flakes, deploy_flake.repo, &opts.extra_build_args).await?; + let result_path = opts.result_path.as_deref(); + run_deploy( deploy_flake, data, supports_flakes, opts.checksigs, cmd_overrides, + opts.keep_result, + result_path, ) .await?; -- cgit v1.2.3 From 426fb3c489dcbb4ccbf98a3ab6a7fe25e71b95ca Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 26 Oct 2020 12:24:57 -0700 Subject: Automatically run checks when deploying --- src/main.rs | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 1358a83..48fb482 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,6 +40,10 @@ struct Opts { #[clap(short, long)] result_path: Option, + /// Skip the automatic pre-build checks + #[clap(short, long)] + skip_checks: bool, + /// Override the SSH user with the given value #[clap(long)] ssh_user: Option, @@ -197,8 +201,42 @@ async fn test_flake_support() -> Result> { .success()) } +async fn check_deployment(supports_flakes: bool, repo: &str, extra_build_args: &[String]) -> () { + let mut c = match supports_flakes { + true => Command::new("nix"), + false => Command::new("nix-build"), + }; + + let mut check_command = match supports_flakes { + true => { + c.arg("flake") + .arg("check") + .arg(repo) + } + false => { + c.arg("-E") + .arg("--no-out-link") + .arg(format!("let r = import {}/.; in (if builtins.isFunction r then (r {{}}) else r).checks.${{builtins.currentSystem}}", repo)) + } + }; + + for extra_arg in extra_build_args { + check_command = check_command.arg(extra_arg); + } + + let check_status = match check_command.status().await { + Ok(x) => x, + Err(err) => good_panic!("Error running checks for the given flake repo: {:?}", err), + }; + + if !check_status.success() { + good_panic!("Checks failed for the given flake repo"); + } + + () +} + /// Evaluates the Nix in the given `repo` and return the processed Data from it -#[inline] async fn get_deployment_data( supports_flakes: bool, repo: &str, @@ -374,6 +412,10 @@ async fn main() -> Result<(), Box> { warn!("A Nix version without flakes support was detected, support for this is work in progress"); } + if !opts.skip_checks { + check_deployment(supports_flakes, deploy_flake.repo, &opts.extra_build_args).await; + } + let data = get_deployment_data(supports_flakes, deploy_flake.repo, &opts.extra_build_args).await?; -- cgit v1.2.3 From df002c31a64409350a3cb8825364542c65a4d00a Mon Sep 17 00:00:00 2001 From: notgne2 Date: Mon, 26 Oct 2020 12:44:19 -0700 Subject: Add more debug logs --- src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 48fb482..5dc6bb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,7 +73,6 @@ struct Opts { temp_path: Option, } -#[inline] async fn push_all_profiles( node: &utils::data::Node, node_name: &str, @@ -190,6 +189,8 @@ async fn deploy_all_profiles( /// Returns if the available Nix installation supports flakes #[inline] async fn test_flake_support() -> Result> { + debug!("Checking for flake support"); + Ok(Command::new("nix") .arg("eval") .arg("--expr") @@ -202,6 +203,8 @@ async fn test_flake_support() -> Result> { } async fn check_deployment(supports_flakes: bool, repo: &str, extra_build_args: &[String]) -> () { + info!("Running checks for flake in {}", repo); + let mut c = match supports_flakes { true => Command::new("nix"), false => Command::new("nix-build"), -- cgit v1.2.3