diff --git a/src/nix/hive/assets.rs b/src/nix/hive/assets.rs index a341695..2d4fb52 100644 --- a/src/nix/hive/assets.rs +++ b/src/nix/hive/assets.rs @@ -11,8 +11,10 @@ use std::os::unix::fs::OpenOptionsExt; use tempfile::TempDir; -use super::HivePath; +use super::{Flake, HivePath}; +use crate::error::ColmenaResult; +const FLAKE_NIX: &[u8] = include_bytes!("flake.nix"); const EVAL_NIX: &[u8] = include_bytes!("eval.nix"); const OPTIONS_NIX: &[u8] = include_bytes!("options.nix"); const MODULES_NIX: &[u8] = include_bytes!("modules.nix"); @@ -22,17 +24,37 @@ const MODULES_NIX: &[u8] = include_bytes!("modules.nix"); pub(super) struct Assets { /// Temporary directory holding the files. temp_dir: TempDir, + + /// Locked Flake URI of the assets flake. + assets_flake_uri: Option, } impl Assets { - pub fn new() -> Self { + pub async fn new(flake: bool) -> ColmenaResult { let temp_dir = TempDir::new().unwrap(); - create_file(&temp_dir, "eval.nix", false, EVAL_NIX); - create_file(&temp_dir, "options.nix", false, OPTIONS_NIX); - create_file(&temp_dir, "modules.nix", false, MODULES_NIX); + create_file(&temp_dir, "eval.nix", false, EVAL_NIX)?; + create_file(&temp_dir, "options.nix", false, OPTIONS_NIX)?; + create_file(&temp_dir, "modules.nix", false, MODULES_NIX)?; - Self { temp_dir } + let mut assets_flake_uri = None; + + if flake { + // Emit a temporary flake, then resolve the locked URI + create_file(&temp_dir, "flake.nix", false, FLAKE_NIX)?; + + // We explicitly specify `path:` instead of letting Nix resolve + // automatically, which would involve checking parent directories + // for a git repository. + let uri = format!("path:{}", temp_dir.path().to_str().unwrap()); + let assets_flake = Flake::from_uri(uri).await?; + assets_flake_uri = Some(assets_flake.locked_uri().to_owned()); + } + + Ok(Self { + temp_dir, + assets_flake_uri, + }) } /// Returns the base expression from which the evaluated Hive can be used. @@ -49,11 +71,9 @@ impl Assets { } HivePath::Flake(flake) => { format!( - "with builtins; let eval = import {eval_nix}; hive = eval {{ flakeUri = \"{flake_uri}\"; colmenaOptions = import {options_nix}; colmenaModules = import {modules_nix}; }}; in ", - flake_uri = flake.uri(), - eval_nix = self.get_path("eval.nix"), - options_nix = self.get_path("options.nix"), - modules_nix = self.get_path("modules.nix"), + "with builtins; let assets = getFlake \"{assets_flake_uri}\"; hive = assets.lib.colmenaEval {{ flakeUri = \"{flake_uri}\"; }}; in ", + assets_flake_uri = self.assets_flake_uri.as_ref().expect("The assets flake must have been initialized"), + flake_uri = flake.locked_uri(), ) } } @@ -69,15 +89,16 @@ impl Assets { } } -fn create_file(base: &TempDir, name: &str, executable: bool, contents: &[u8]) { +fn create_file(base: &TempDir, name: &str, executable: bool, contents: &[u8]) -> ColmenaResult<()> { let mode = if executable { 0o700 } else { 0o600 }; let path = base.path().join(name); let mut f = OpenOptions::new() .create_new(true) .write(true) .mode(mode) - .open(path) - .unwrap(); + .open(path)?; - f.write_all(contents).unwrap(); + f.write_all(contents)?; + + Ok(()) } diff --git a/src/nix/hive/flake.nix b/src/nix/hive/flake.nix new file mode 100644 index 0000000..f56f37e --- /dev/null +++ b/src/nix/hive/flake.nix @@ -0,0 +1,15 @@ +{ + description = "Internal Colmena expressions"; + + outputs = { ... }: { + lib.colmenaEval = { + rawHive ? null, + flakeUri ? null, + hermetic ? flakeUri != null, + }: import ./eval.nix { + inherit rawHive flakeUri hermetic; + colmenaOptions = import ./options.nix; + colmenaModules = import ./modules.nix; + }; + }; +} diff --git a/src/nix/hive/mod.rs b/src/nix/hive/mod.rs index 2d2c3da..3636989 100644 --- a/src/nix/hive/mod.rs +++ b/src/nix/hive/mod.rs @@ -90,6 +90,10 @@ impl HivePath { Ok(Self::Legacy(path.canonicalize()?)) } + fn is_flake(&self) -> bool { + matches!(self, Self::Flake(_)) + } + fn context_dir(&self) -> Option { match self { Self::Legacy(p) => p.parent().map(|d| d.to_owned()), @@ -99,13 +103,14 @@ impl HivePath { } impl Hive { - pub fn new(path: HivePath) -> ColmenaResult { + pub async fn new(path: HivePath) -> ColmenaResult { let context_dir = path.context_dir(); + let assets = Assets::new(path.is_flake()).await?; Ok(Self { path, context_dir, - assets: Assets::new(), + assets, show_trace: false, meta_config: OnceCell::new(), }) diff --git a/src/nix/hive/tests/mod.rs b/src/nix/hive/tests/mod.rs index 858170f..9d987be 100644 --- a/src/nix/hive/tests/mod.rs +++ b/src/nix/hive/tests/mod.rs @@ -40,7 +40,7 @@ impl TempHive { temp_file.write_all(text.as_bytes()).unwrap(); let hive_path = block_on(HivePath::from_path(temp_file.path())).unwrap(); - let hive = Hive::new(hive_path).unwrap(); + let hive = block_on(Hive::new(hive_path)).unwrap(); Self { hive, @@ -168,7 +168,7 @@ fn test_parse_flake() { let flake = block_on(Flake::from_dir(flake_dir)).unwrap(); let hive_path = HivePath::Flake(flake); - let mut hive = Hive::new(hive_path).unwrap(); + let mut hive = block_on(Hive::new(hive_path)).unwrap(); hive.set_show_trace(true); diff --git a/src/util.rs b/src/util.rs index cb45fbf..ff40436 100644 --- a/src/util.rs +++ b/src/util.rs @@ -244,7 +244,7 @@ pub async fn hive_from_args(args: &ArgMatches) -> ColmenaResult { log::info!("Using flake: {}", flake.uri()); let hive_path = HivePath::Flake(flake); - let mut hive = Hive::new(hive_path)?; + let mut hive = Hive::new(hive_path).await?; if args.is_present("show-trace") { hive.set_show_trace(true); @@ -267,7 +267,7 @@ pub async fn hive_from_args(args: &ArgMatches) -> ColmenaResult { } } - let mut hive = Hive::new(hive_path)?; + let mut hive = Hive::new(hive_path).await?; if args.is_present("show-trace") { hive.set_show_trace(true);