1use std::{
2 env,
3 process::{Command, ExitStatus, Stdio},
4 sync::mpsc::Sender,
5};
6use users::get_current_username;
7
8use crate::{exec, Extension};
9use anyhow::Error;
10use fluentci_types::{Output, nix::NixArgs};
11
12#[derive(Default, Clone)]
13pub struct Nix {
14 pub args: NixArgs
15}
16
17impl Nix {
18 pub fn new(args: NixArgs) -> Self {
19 Self { args }
20 }
21
22 pub fn impure(&mut self) -> &mut Self {
23 self.args.impure = true;
24 self
25 }
26
27 pub fn build_args(&self) -> String {
28 let mut args = vec![];
29 if self.args.impure {
30 args.push("--impure".to_string());
31 }
32 args.join(" ")
33 }
34}
35
36impl Extension for Nix {
37 fn exec(
38 &mut self,
39 cmd: &str,
40 tx: Sender<String>,
41 out: Output,
42 last_cmd: bool,
43 work_dir: &str,
44 ) -> Result<ExitStatus, Error> {
45 self.setup()?;
46
47 if cmd.is_empty() {
48 return Ok(ExitStatus::default());
49 }
50
51 let args = self.build_args();
52
53 Command::new("bash")
54 .arg("-c")
55 .arg(&format!("[ -f flake.nix ] || nix flake init {}", args))
56 .current_dir(work_dir)
57 .stdout(Stdio::inherit())
58 .stderr(Stdio::inherit())
59 .spawn()?
60 .wait()?;
61
62 let cmd = format!("nix develop {} -c {}", args, cmd);
63 exec(&cmd, tx, out, last_cmd, work_dir)
64 }
65
66 fn setup(&self) -> Result<(), Error> {
67 let user = match get_current_username() {
68 Some(user) => user.to_string_lossy().to_string(),
69 None => "root".to_string(),
70 };
71
72 env::set_var("USER", user);
73 env::set_var("SHELL", "/bin/bash");
74
75 let home = match env::var("HOME") {
76 Ok(home) => home,
77 Err(_) => "/root".to_string(),
78 };
79 let nix_path = format!("{}/.nix-profile/bin", home);
80 env::set_var(
81 "PATH",
82 format!(
83 "{}:{}:{}",
84 env::var("PATH")?,
85 "/nix/var/nix/profiles/default/bin",
86 nix_path
87 ),
88 );
89
90 let mut child = Command::new("sh")
91 .arg("-c")
92 .arg("type systemctl > /dev/null 2> /dev/null")
93 .spawn()?;
94 let status = child.wait()?;
95 let init = match status.code() {
96 Some(0) => "",
97 _ => "--init none",
98 };
99
100 let linux = match std::env::consts::OS {
101 "linux" => format!("linux --extra-conf 'sandbox = false' {}", init),
102 _ => "".to_string(),
103 };
104 let mut child = Command::new("bash")
105 .arg("-c")
106 .arg(format!("type nix > /dev/null || curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install {}", linux))
107
108 .spawn()?;
109 child.wait()?;
110
111 let mut child = Command::new("bash")
112 .arg("-c")
113 .arg(format!("type nix > /dev/null || curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install {} --no-confirm", linux))
114
115 .spawn()?;
116 child.wait()?;
117 Ok(())
118 }
119
120 fn format_command(&self, cmd: &str) -> String {
121 let args = self.build_args();
122 format!(
123 "[ -f flake.nix ] || nix flake init {} ; nix develop {} -c {}",
124 args, args, cmd
125 )
126 }
127}