forked from DGNum/colmena
Make flake resolution (slightly) less terrible
Instead of using `path:` which always copies the entire directory, we now try to resolve the Flake URI using `nix flake metadata` which may give us a `git+file:`.
This commit is contained in:
parent
b48753239a
commit
0e0a1e84f0
10 changed files with 137 additions and 48 deletions
|
@ -115,7 +115,7 @@ pub fn subcommand() -> App<'static, 'static> {
|
|||
}
|
||||
|
||||
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||
let hive = util::hive_from_args(local_args).unwrap();
|
||||
let hive = util::hive_from_args(local_args).await.unwrap();
|
||||
|
||||
log::info!("Enumerating nodes...");
|
||||
let all_nodes = hive.deployment_info().await.unwrap();
|
||||
|
|
|
@ -89,7 +89,7 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
let hive = util::hive_from_args(local_args).unwrap();
|
||||
let hive = util::hive_from_args(local_args).await.unwrap();
|
||||
let hostname = if local_args.is_present("node") {
|
||||
local_args.value_of("node").unwrap().to_owned()
|
||||
} else {
|
||||
|
|
|
@ -55,7 +55,7 @@ It's recommended to use -- to separate Colmena options from the command to run.
|
|||
}
|
||||
|
||||
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||
let hive = util::hive_from_args(local_args).unwrap();
|
||||
let hive = util::hive_from_args(local_args).await.unwrap();
|
||||
|
||||
log::info!("Enumerating nodes...");
|
||||
let all_nodes = hive.deployment_info().await.unwrap();
|
||||
|
|
|
@ -28,7 +28,7 @@ For example, to retrieve the configuration of one node, you may write something
|
|||
}
|
||||
|
||||
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||
let hive = util::hive_from_args(local_args).unwrap();
|
||||
let hive = util::hive_from_args(local_args).await.unwrap();
|
||||
|
||||
if !(local_args.is_present("expression") ^ local_args.is_present("expression_file")) {
|
||||
log::error!("Either an expression (-E) or a .nix file containing an expression should be specified, not both.");
|
||||
|
|
92
src/nix/flake.rs
Normal file
92
src/nix/flake.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
//! Nix Flake utilities.
|
||||
|
||||
use std::convert::AsRef;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Stdio;
|
||||
|
||||
use serde::Deserialize;
|
||||
use tokio::process::Command;
|
||||
|
||||
use super::{NixCheck, NixError, NixResult};
|
||||
|
||||
/// A Nix Flake.
|
||||
#[derive(Debug)]
|
||||
pub struct Flake {
|
||||
/// The Flake URI.
|
||||
uri: String,
|
||||
|
||||
/// The directory the flake lives in, if it's a local flake.
|
||||
local_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Flake {
|
||||
/// Creates a flake from the given directory.
|
||||
///
|
||||
/// This will try to retrieve the resolved URL of the local flake
|
||||
/// in the specified directory.
|
||||
pub async fn from_dir<P: AsRef<Path>>(dir: P) -> NixResult<Self> {
|
||||
NixCheck::require_flake_support().await?;
|
||||
|
||||
let flake = dir.as_ref().as_os_str().to_str()
|
||||
.expect("Flake directory path contains non-UTF-8 characters");
|
||||
|
||||
let info = FlakeMetadata::resolve(flake).await?;
|
||||
|
||||
Ok(Self {
|
||||
uri: info.resolved_url,
|
||||
local_dir: Some(dir.as_ref().to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a flake from a Flake URI.
|
||||
pub async fn from_uri(uri: String) -> NixResult<Self> {
|
||||
NixCheck::require_flake_support().await?;
|
||||
|
||||
Ok(Self {
|
||||
uri,
|
||||
local_dir: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the URI.
|
||||
pub fn uri(&self) -> &str {
|
||||
&self.uri
|
||||
}
|
||||
|
||||
/// Returns the local directory, if it exists.
|
||||
pub fn local_dir(&self) -> Option<&Path> {
|
||||
self.local_dir.as_ref().map(|d| d.as_path())
|
||||
}
|
||||
}
|
||||
|
||||
/// A `nix flake metadata --json` invocation.
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct FlakeMetadata {
|
||||
/// The resolved URL of the flake.
|
||||
#[serde(rename = "resolvedUrl")]
|
||||
resolved_url: String,
|
||||
}
|
||||
|
||||
impl FlakeMetadata {
|
||||
/// Resolves a flake.
|
||||
async fn resolve(flake: &str) -> NixResult<Self> {
|
||||
let child = Command::new("nix")
|
||||
.args(&["flake", "metadata", "--json"])
|
||||
.args(&["--experimental-features", "nix-command flakes"])
|
||||
.arg(flake)
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let output = child.wait_with_output().await?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(output.status.into());
|
||||
}
|
||||
|
||||
serde_json::from_slice::<FlakeMetadata>(&output.stdout)
|
||||
.map_err(|_| {
|
||||
let output = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
NixError::BadOutput { output }
|
||||
})
|
||||
}
|
||||
}
|
|
@ -10,13 +10,13 @@ use serde::Serialize;
|
|||
use validator::Validate;
|
||||
|
||||
use super::{
|
||||
Flake,
|
||||
StoreDerivation,
|
||||
NixResult,
|
||||
NixError,
|
||||
NodeConfig,
|
||||
ProfileMap,
|
||||
};
|
||||
use super::{NixCommand, NixCheck};
|
||||
use super::NixCommand;
|
||||
use crate::util::CommandExecution;
|
||||
use crate::progress::TaskProgress;
|
||||
|
||||
|
@ -24,49 +24,38 @@ const HIVE_EVAL: &'static [u8] = include_bytes!("eval.nix");
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum HivePath {
|
||||
/// A Nix Flake URI.
|
||||
/// A Nix Flake.
|
||||
///
|
||||
/// The flake must contain the `colmena` output.
|
||||
Flake(String),
|
||||
Flake(Flake),
|
||||
|
||||
/// A regular .nix file
|
||||
Legacy(PathBuf),
|
||||
}
|
||||
|
||||
impl HivePath {
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> Self {
|
||||
pub async fn from_path<P: AsRef<Path>>(path: P) -> NixResult<Self> {
|
||||
let path = path.as_ref();
|
||||
|
||||
if let Some(osstr) = path.file_name() {
|
||||
if osstr == "flake.nix" {
|
||||
let parent = path.parent().unwrap().canonicalize().unwrap();
|
||||
let parent = parent.to_str().unwrap();
|
||||
let uri = format!("path:{}", parent);
|
||||
|
||||
return Self::Flake(uri);
|
||||
let parent = path.parent().unwrap();
|
||||
let flake = Flake::from_dir(parent).await?;
|
||||
return Ok(Self::Flake(flake));
|
||||
}
|
||||
}
|
||||
|
||||
Self::Legacy(path.to_owned())
|
||||
Ok(Self::Legacy(path.to_owned()))
|
||||
}
|
||||
|
||||
fn context_dir(&self) -> Option<PathBuf> {
|
||||
match self {
|
||||
Self::Legacy(p) => {
|
||||
if let Some(parent) = p.parent() {
|
||||
return Some(parent.to_owned());
|
||||
}
|
||||
None
|
||||
p.parent().map(|d| d.to_owned())
|
||||
}
|
||||
Self::Flake(flake) => {
|
||||
flake.local_dir().map(|d| d.to_owned())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_flake(&self) -> bool {
|
||||
if let Self::Flake(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,13 +117,6 @@ impl Hive {
|
|||
|
||||
/// Retrieve deployment info for all nodes.
|
||||
pub async fn deployment_info(&self) -> NixResult<HashMap<String, NodeConfig>> {
|
||||
let nix_check = NixCheck::detect().await;
|
||||
|
||||
if self.path.is_flake() && !nix_check.flakes_supported() {
|
||||
nix_check.print_flakes_info(true);
|
||||
return Err(NixError::NoFlakesSupport);
|
||||
}
|
||||
|
||||
// FIXME: Really ugly :(
|
||||
let s: String = self.nix_instantiate("hive.deploymentConfigJson").eval_with_builders().await?
|
||||
.capture_json().await?;
|
||||
|
@ -281,7 +263,7 @@ impl<'hive> NixInstantiate<'hive> {
|
|||
self.expression,
|
||||
));
|
||||
}
|
||||
HivePath::Flake(uri) => {
|
||||
HivePath::Flake(flake) => {
|
||||
command
|
||||
.args(&["--experimental-features", "flakes"])
|
||||
.arg("--no-gc-warning")
|
||||
|
@ -289,7 +271,7 @@ impl<'hive> NixInstantiate<'hive> {
|
|||
.arg(format!(
|
||||
"with builtins; let eval = import {}; hive = eval {{ flakeUri = \"{}\"; }}; in {}",
|
||||
self.hive.eval_nix.to_str().unwrap(),
|
||||
&uri,
|
||||
flake.uri(),
|
||||
self.expression,
|
||||
));
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@ use std::process::Stdio;
|
|||
|
||||
use log::Level;
|
||||
use regex::Regex;
|
||||
|
||||
use tokio::process::Command;
|
||||
|
||||
use super::{NixError, NixResult};
|
||||
|
||||
struct NixVersion {
|
||||
major: usize,
|
||||
minor: usize,
|
||||
|
@ -88,6 +89,17 @@ impl NixCheck {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn require_flake_support() -> NixResult<()> {
|
||||
let check = Self::detect().await;
|
||||
|
||||
if !check.flakes_supported() {
|
||||
check.print_flakes_info(true);
|
||||
Err(NixError::NoFlakesSupport)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_version_info(&self) {
|
||||
if let Some(v) = &self.version {
|
||||
log::info!("Nix Version: {}", v);
|
||||
|
|
|
@ -36,6 +36,9 @@ pub use deployment::{Goal, Target, Deployment};
|
|||
pub mod info;
|
||||
pub use info::NixCheck;
|
||||
|
||||
pub mod flake;
|
||||
pub use flake::Flake;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ impl TempHive {
|
|||
let mut temp_file = NamedTempFile::new().unwrap();
|
||||
temp_file.write_all(text.as_bytes()).unwrap();
|
||||
|
||||
let hive_path = HivePath::from_path(temp_file.path());
|
||||
let hive_path = block_on(HivePath::from_path(temp_file.path())).unwrap();
|
||||
let hive = Hive::new(hive_path).unwrap();
|
||||
|
||||
Self {
|
||||
|
@ -149,11 +149,10 @@ fn test_parse_simple() {
|
|||
|
||||
#[test]
|
||||
fn test_parse_flake() {
|
||||
let flake_path = {
|
||||
let p = PathBuf::from("./src/nix/tests/simple-flake");
|
||||
p.canonicalize().unwrap()
|
||||
};
|
||||
let hive_path = HivePath::Flake(format!("path:{}", flake_path.to_str().unwrap()));
|
||||
let flake_dir = PathBuf::from("./src/nix/tests/simple-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();
|
||||
|
||||
hive.set_show_trace(true);
|
||||
|
|
|
@ -9,7 +9,7 @@ use glob::Pattern as GlobPattern;
|
|||
use tokio::io::{AsyncRead, AsyncBufReadExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
|
||||
use super::nix::{NodeConfig, Hive, HivePath, NixResult};
|
||||
use super::nix::{Flake, NodeConfig, Hive, HivePath, NixResult};
|
||||
use super::progress::TaskProgress;
|
||||
|
||||
enum NodeFilter {
|
||||
|
@ -79,7 +79,7 @@ impl CommandExecution {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn hive_from_args(args: &ArgMatches<'_>) -> NixResult<Hive> {
|
||||
pub async fn hive_from_args(args: &ArgMatches<'_>) -> NixResult<Hive> {
|
||||
let path = match args.occurrences_of("config") {
|
||||
0 => {
|
||||
// traverse upwards until we find hive.nix
|
||||
|
@ -142,7 +142,8 @@ pub fn hive_from_args(args: &ArgMatches<'_>) -> NixResult<Hive> {
|
|||
|
||||
if !fpath.exists() && path.contains(":") {
|
||||
// Treat as flake URI
|
||||
let hive_path = HivePath::Flake(path);
|
||||
let flake = Flake::from_uri(path).await?;
|
||||
let hive_path = HivePath::Flake(flake);
|
||||
let mut hive = Hive::new(hive_path)?;
|
||||
|
||||
if args.is_present("show-trace") {
|
||||
|
@ -156,7 +157,7 @@ pub fn hive_from_args(args: &ArgMatches<'_>) -> NixResult<Hive> {
|
|||
}
|
||||
};
|
||||
|
||||
let hive_path = HivePath::from_path(path);
|
||||
let hive_path = HivePath::from_path(path).await?;
|
||||
let mut hive = Hive::new(hive_path)?;
|
||||
|
||||
if args.is_present("show-trace") {
|
||||
|
|
Loading…
Reference in a new issue