nix/assets: Make assets a flake

Groundwork to make flake evaluation pure. `pure-eval` works when
the git workspace is clean.
This commit is contained in:
Zhaofeng Li 2022-08-16 20:15:43 -06:00
parent a98d1f8963
commit 271d9ae576
5 changed files with 62 additions and 21 deletions

View file

@ -11,8 +11,10 @@ use std::os::unix::fs::OpenOptionsExt;
use tempfile::TempDir; 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 EVAL_NIX: &[u8] = include_bytes!("eval.nix");
const OPTIONS_NIX: &[u8] = include_bytes!("options.nix"); const OPTIONS_NIX: &[u8] = include_bytes!("options.nix");
const MODULES_NIX: &[u8] = include_bytes!("modules.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 { pub(super) struct Assets {
/// Temporary directory holding the files. /// Temporary directory holding the files.
temp_dir: TempDir, temp_dir: TempDir,
/// Locked Flake URI of the assets flake.
assets_flake_uri: Option<String>,
} }
impl Assets { impl Assets {
pub fn new() -> Self { pub async fn new(flake: bool) -> ColmenaResult<Self> {
let temp_dir = TempDir::new().unwrap(); let temp_dir = TempDir::new().unwrap();
create_file(&temp_dir, "eval.nix", false, EVAL_NIX); create_file(&temp_dir, "eval.nix", false, EVAL_NIX)?;
create_file(&temp_dir, "options.nix", false, OPTIONS_NIX); create_file(&temp_dir, "options.nix", false, OPTIONS_NIX)?;
create_file(&temp_dir, "modules.nix", false, MODULES_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. /// Returns the base expression from which the evaluated Hive can be used.
@ -49,11 +71,9 @@ impl Assets {
} }
HivePath::Flake(flake) => { HivePath::Flake(flake) => {
format!( format!(
"with builtins; let eval = import {eval_nix}; hive = eval {{ flakeUri = \"{flake_uri}\"; colmenaOptions = import {options_nix}; colmenaModules = import {modules_nix}; }}; in ", "with builtins; let assets = getFlake \"{assets_flake_uri}\"; hive = assets.lib.colmenaEval {{ flakeUri = \"{flake_uri}\"; }}; in ",
flake_uri = flake.uri(), assets_flake_uri = self.assets_flake_uri.as_ref().expect("The assets flake must have been initialized"),
eval_nix = self.get_path("eval.nix"), flake_uri = flake.locked_uri(),
options_nix = self.get_path("options.nix"),
modules_nix = self.get_path("modules.nix"),
) )
} }
} }
@ -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 mode = if executable { 0o700 } else { 0o600 };
let path = base.path().join(name); let path = base.path().join(name);
let mut f = OpenOptions::new() let mut f = OpenOptions::new()
.create_new(true) .create_new(true)
.write(true) .write(true)
.mode(mode) .mode(mode)
.open(path) .open(path)?;
.unwrap();
f.write_all(contents).unwrap(); f.write_all(contents)?;
Ok(())
} }

15
src/nix/hive/flake.nix Normal file
View file

@ -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;
};
};
}

View file

@ -90,6 +90,10 @@ impl HivePath {
Ok(Self::Legacy(path.canonicalize()?)) Ok(Self::Legacy(path.canonicalize()?))
} }
fn is_flake(&self) -> bool {
matches!(self, Self::Flake(_))
}
fn context_dir(&self) -> Option<PathBuf> { fn context_dir(&self) -> Option<PathBuf> {
match self { match self {
Self::Legacy(p) => p.parent().map(|d| d.to_owned()), Self::Legacy(p) => p.parent().map(|d| d.to_owned()),
@ -99,13 +103,14 @@ impl HivePath {
} }
impl Hive { impl Hive {
pub fn new(path: HivePath) -> ColmenaResult<Self> { pub async fn new(path: HivePath) -> ColmenaResult<Self> {
let context_dir = path.context_dir(); let context_dir = path.context_dir();
let assets = Assets::new(path.is_flake()).await?;
Ok(Self { Ok(Self {
path, path,
context_dir, context_dir,
assets: Assets::new(), assets,
show_trace: false, show_trace: false,
meta_config: OnceCell::new(), meta_config: OnceCell::new(),
}) })

View file

@ -40,7 +40,7 @@ impl TempHive {
temp_file.write_all(text.as_bytes()).unwrap(); temp_file.write_all(text.as_bytes()).unwrap();
let hive_path = block_on(HivePath::from_path(temp_file.path())).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 { Self {
hive, hive,
@ -168,7 +168,7 @@ fn test_parse_flake() {
let flake = block_on(Flake::from_dir(flake_dir)).unwrap(); let flake = block_on(Flake::from_dir(flake_dir)).unwrap();
let hive_path = HivePath::Flake(flake); 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); hive.set_show_trace(true);

View file

@ -244,7 +244,7 @@ pub async fn hive_from_args(args: &ArgMatches) -> ColmenaResult<Hive> {
log::info!("Using flake: {}", flake.uri()); log::info!("Using flake: {}", flake.uri());
let hive_path = HivePath::Flake(flake); 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") { if args.is_present("show-trace") {
hive.set_show_trace(true); hive.set_show_trace(true);
@ -267,7 +267,7 @@ pub async fn hive_from_args(args: &ArgMatches) -> ColmenaResult<Hive> {
} }
} }
let mut hive = Hive::new(hive_path)?; let mut hive = Hive::new(hive_path).await?;
if args.is_present("show-trace") { if args.is_present("show-trace") {
hive.set_show_trace(true); hive.set_show_trace(true);