nixfleet_agent/activation/
realise.rs1use anyhow::{Context, anyhow};
6use tokio::process::Command;
7
8pub enum RealiseError {
9 SignatureMismatch { stderr_tail: String },
10 Other(anyhow::Error),
11}
12
13impl From<anyhow::Error> for RealiseError {
14 fn from(err: anyhow::Error) -> Self {
15 RealiseError::Other(err)
16 }
17}
18
19pub fn looks_like_signature_error(stderr: &str) -> bool {
21 let lower = stderr.to_lowercase();
22 [
23 "lacks a valid signature",
24 "no signature is trusted",
25 "is not signed by any of the keys",
26 "no signatures matched",
27 "signature mismatch",
28 "untrusted signature",
29 ]
30 .iter()
31 .any(|needle| lower.contains(needle))
32}
33
34pub(super) async fn realise(store_path: &str) -> Result<String, RealiseError> {
35 let output = Command::new("nix-store")
36 .arg("--realise")
37 .arg(store_path)
38 .output()
39 .await
40 .with_context(|| format!("spawn nix-store --realise {store_path}"))?;
41
42 if !output.status.success() {
43 let stderr = String::from_utf8_lossy(&output.stderr);
44 if looks_like_signature_error(&stderr) {
45 let tail_start = stderr.len().saturating_sub(500);
46 let tail = stderr[tail_start..].to_string();
47 return Err(RealiseError::SignatureMismatch { stderr_tail: tail });
48 }
49 return Err(anyhow!(
50 "nix-store --realise {store_path} exited {:?}: {stderr}",
51 output.status.code()
52 )
53 .into());
54 }
55
56 let stdout = String::from_utf8(output.stdout)
57 .map_err(|e| anyhow!("nix-store --realise stdout not utf-8: {e}"))?;
58 let line = stdout
59 .lines()
60 .next()
61 .ok_or_else(|| anyhow!("nix-store --realise produced no output"))?;
62 Ok(line.trim().to_string())
63}