aboutsummaryrefslogtreecommitdiff
path: root/src/utils/activate.rs
blob: 33774fde0fd24013a4a9f756ab1036eda1abee54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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(())
}