aboutsummaryrefslogtreecommitdiff
path: root/src/utils
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/utils/activate.rs108
-rw-r--r--src/utils/mod.rs72
2 files changed, 176 insertions, 4 deletions
diff --git a/src/utils/activate.rs b/src/utils/activate.rs
new file mode 100644
index 0000000..33774fd
--- /dev/null
+++ b/src/utils/activate.rs
@@ -0,0 +1,108 @@
+use std::process::Stdio;
+use tokio::process::Command;
+
+use std::path::Path;
+
+pub async fn activate(
+ profile_path: String,
+ closure: String,
+ activate_cmd: Option<String>,
+ bootstrap_cmd: Option<String>,
+ auto_rollback: bool,
+) -> Result<(), Box<dyn std::error::Error>> {
+ info!("Activating profile");
+
+ Command::new("nix-env")
+ .arg("-p")
+ .arg(&profile_path)
+ .arg("--set")
+ .arg(&closure)
+ .stdout(Stdio::null())
+ .spawn()?
+ .await?;
+
+ if let (Some(bootstrap_cmd), false) = (bootstrap_cmd, !Path::new(&profile_path).exists()) {
+ let bootstrap_status = Command::new("bash")
+ .arg("-c")
+ .arg(&bootstrap_cmd)
+ .env("PROFILE", &profile_path)
+ .stdout(Stdio::null())
+ .stderr(Stdio::null())
+ .status()
+ .await;
+
+ match bootstrap_status {
+ Ok(s) if s.success() => (),
+ _ => {
+ tokio::fs::remove_file(&profile_path).await?;
+ good_panic!("Failed to execute bootstrap command");
+ }
+ }
+ }
+
+ if let Some(activate_cmd) = activate_cmd {
+ let activate_status = Command::new("bash")
+ .arg("-c")
+ .arg(&activate_cmd)
+ .env("PROFILE", &profile_path)
+ .status()
+ .await;
+
+ match activate_status {
+ Ok(s) if s.success() => (),
+ _ if auto_rollback => {
+ Command::new("nix-env")
+ .arg("-p")
+ .arg(&profile_path)
+ .arg("--rollback")
+ .stdout(Stdio::null())
+ .stderr(Stdio::null())
+ .spawn()?
+ .await?;
+
+ let c = Command::new("nix-env")
+ .arg("-p")
+ .arg(&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(&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");
+ }
+ _ => {}
+ }
+ }
+
+ Ok(())
+}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 764e2e9..935f470 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -1,10 +1,7 @@
use std::borrow::Cow;
use std::path::PathBuf;
-pub mod data;
-pub mod deploy;
-pub mod push;
-
+#[macro_export]
macro_rules! good_panic {
($($tts:tt)*) => {{
error!($($tts)*);
@@ -12,6 +9,73 @@ macro_rules! good_panic {
}}
}
+pub mod activate;
+pub mod data;
+pub mod deploy;
+pub mod push;
+
+#[derive(PartialEq, Debug)]
+pub struct DeployFlake<'a> {
+ pub repo: &'a str,
+ pub node: Option<&'a str>,
+ pub profile: Option<&'a str>,
+}
+
+pub 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,
+ }
+ );
+}
+
pub struct DeployData<'a> {
pub sudo: Option<String>,
pub ssh_user: Cow<'a, str>,