1use std::{2 collections::HashMap,3 ffi::OsStr,4 path::PathBuf,5 process::{Command, Stdio},6};78use anyhow::Result;9use serde::de::DeserializeOwned;1011use crate::command::CommandOutput;1213pub const HOSTS_ATTRIBUTE: &str = ".#fleetConfigurations.default.configuredHosts";14pub const SECRETS_ATTRIBUTE: &str = ".#fleetConfigurations.default.configuredSecrets";15pub const SYSTEMS_ATTRIBUTE: &str = ".#fleetConfigurations.default.configuredSystems";1617pub struct NixCopy {18 closure: PathBuf,19}20impl NixCopy {21 pub fn new(closure: PathBuf) -> Self {22 Self { closure }23 }24 fn run_internal(&self, f: impl Fn(&mut Command)) -> Result<CommandOutput> {25 let mut cmd = Command::new("nix");26 cmd.stderr(Stdio::inherit())27 .arg("copy")28 .arg("--substitute-on-destination")29 .arg(&self.closure);30 f(&mut cmd);3132 let out = cmd.output()?;33 if !out.status.success() {34 anyhow::bail!("nix copy failed");35 }36 Ok(CommandOutput(out.stdout))37 }38 pub fn from(&self, from: impl AsRef<OsStr>) -> Result<()> {39 let from = from.as_ref();40 self.run_internal(|cmd| {41 cmd.arg("--from").arg(from);42 })?;43 Ok(())44 }45 pub fn to(&self, to: impl AsRef<OsStr>) -> Result<()> {46 let to = to.as_ref();47 self.run_internal(|cmd| {48 cmd.arg("--to").arg(to);49 })?;50 Ok(())51 }52}5354pub struct NixBuild {55 attribute: String,56 impure: bool,57 env: HashMap<String, String>,58}5960impl NixBuild {61 pub fn new(attribute: String) -> Self {62 Self {63 attribute,64 impure: false,65 env: HashMap::new(),66 }67 }68 pub fn env(&mut self, name: String, value: String) -> &mut Self {69 self.impure = true;70 self.env.insert(name, value);71 self72 }73 pub fn run(&self) -> Result<tempfile::TempDir> {74 let dir = tempfile::tempdir()?;75 std::fs::remove_dir(dir.path())?;76 let mut cmd = Command::new("nix");77 cmd.stderr(Stdio::inherit())78 .arg("build")79 .arg(&self.attribute)80 .arg("--no-link")81 .arg("--out-link")82 .arg(dir.path());83 if self.impure {84 cmd.arg("--impure");85 }86 if !self.env.is_empty() {87 cmd.envs(&self.env);88 }8990 let out = cmd.output()?;91 if !out.status.success() {92 anyhow::bail!("nix eval failed");93 }94 Ok(dir)95 }96}9798#[derive(Default)]99pub struct NixEval {100 attribute: String,101 impure: bool,102 apply: Option<String>,103 env: HashMap<String, String>,104}105106impl NixEval {107 pub fn new(attribute: String) -> Self {108 Self {109 attribute,110 ..Default::default()111 }112 }113 pub fn impure(&mut self) -> &mut Self {114 self.impure = true;115 self116 }117 118 119 120 pub fn env(&mut self, name: String, value: String) -> &mut Self {121 self.impure = true;122 self.env.insert(name, value);123 self124 }125 pub fn apply(&mut self, apply: String) -> &mut Self {126 self.apply = Some(apply);127 self128 }129 fn run_internal(&self, f: impl Fn(&mut Command)) -> Result<CommandOutput> {130 let mut cmd = Command::new("nix");131 cmd.stderr(Stdio::inherit())132 .arg("eval")133 .arg("--show-trace")134 .arg(&self.attribute);135 if let Some(apply) = &self.apply {136 cmd.arg("--apply").arg(apply);137 };138 if self.impure {139 cmd.arg("--impure");140 }141 if !self.env.is_empty() {142 cmd.envs(&self.env);143 }144 f(&mut cmd);145146 let out = cmd.output()?;147 if !out.status.success() {148 anyhow::bail!("nix eval failed");149 }150 Ok(CommandOutput(out.stdout))151 }152 pub fn run(&self) -> Result<String> {153 Ok(self.run_internal(|_cmd| {})?.as_str()?.to_owned())154 }155 pub fn run_json<T: DeserializeOwned>(&self) -> Result<T> {156 Ok(serde_json::from_slice(157 &self158 .run_internal(|cmd| {159 cmd.arg("--json");160 })?161 .0,162 )?)163 }164 pub fn run_raw(&self) -> Result<String> {165 Ok(self166 .run_internal(|cmd| {167 cmd.arg("--raw");168 })?169 .as_str()?170 .to_owned())171 }172}