Add direct flake evaluation support
This commit is contained in:
parent
1f669d4c78
commit
dc80345dee
2 changed files with 118 additions and 12 deletions
22
src/cli.rs
22
src/cli.rs
|
@ -10,7 +10,7 @@ use env_logger::fmt::WriteStyle;
|
||||||
use crate::{
|
use crate::{
|
||||||
command::{self, apply::DeployOpts},
|
command::{self, apply::DeployOpts},
|
||||||
error::ColmenaResult,
|
error::ColmenaResult,
|
||||||
nix::{Hive, HivePath},
|
nix::{hive::EvaluationMethod, Hive, HivePath},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base URL of the manual, without the trailing slash.
|
/// Base URL of the manual, without the trailing slash.
|
||||||
|
@ -137,6 +137,21 @@ This only works when building locally.
|
||||||
value_names = ["NAME", "VALUE"],
|
value_names = ["NAME", "VALUE"],
|
||||||
)]
|
)]
|
||||||
nix_option: Vec<String>,
|
nix_option: Vec<String>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
default_value_t,
|
||||||
|
help = "Use direct flake evaluation (experimental)",
|
||||||
|
long_help = r#"If enabled, flakes will be evaluated using `nix eval`. This requires the flake to depend on Colmena as an input and expose a compatible `colmenaHive` output:
|
||||||
|
|
||||||
|
outputs = { self, colmena, ... }: {
|
||||||
|
colmenaHive = colmena.lib.makeHive self.outputs.colmena;
|
||||||
|
colmena = ...;
|
||||||
|
};
|
||||||
|
|
||||||
|
This is an experimental feature."#,
|
||||||
|
global = true
|
||||||
|
)]
|
||||||
|
experimental_flake_eval: bool,
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
value_name = "WHEN",
|
value_name = "WHEN",
|
||||||
|
@ -262,6 +277,11 @@ async fn get_hive(opts: &Opts) -> ColmenaResult<Hive> {
|
||||||
hive.set_impure(true);
|
hive.set_impure(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.experimental_flake_eval {
|
||||||
|
log::warn!("Using direct flake evaluation (experimental)");
|
||||||
|
hive.set_evaluation_method(EvaluationMethod::DirectFlakeEval);
|
||||||
|
}
|
||||||
|
|
||||||
for chunks in opts.nix_option.chunks_exact(2) {
|
for chunks in opts.nix_option.chunks_exact(2) {
|
||||||
let [name, value] = chunks else {
|
let [name, value] = chunks else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::convert::AsRef;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use const_format::formatcp;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tokio::sync::OnceCell;
|
use tokio::sync::OnceCell;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
@ -22,6 +23,21 @@ use crate::job::JobHandle;
|
||||||
use crate::util::{CommandExecution, CommandExt};
|
use crate::util::{CommandExecution, CommandExt};
|
||||||
use assets::Assets;
|
use assets::Assets;
|
||||||
|
|
||||||
|
/// The version of the Hive schema we are compatible with.
|
||||||
|
///
|
||||||
|
/// Currently we are tied to one specific version.
|
||||||
|
const HIVE_SCHEMA: &str = "v0.20241006";
|
||||||
|
|
||||||
|
/// The snippet to be used for `nix eval --apply`.
|
||||||
|
const FLAKE_APPLY_SNIPPET: &str = formatcp!(
|
||||||
|
r#"with builtins; hive: assert (hive.__schema == "{}" || throw ''
|
||||||
|
The colmenaHive output (schema ${{hive.__schema}}) isn't compatible with this version of Colmena.
|
||||||
|
|
||||||
|
Hint: Use the same version of Colmena as in the Flake input.
|
||||||
|
''); "#,
|
||||||
|
HIVE_SCHEMA
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum HivePath {
|
pub enum HivePath {
|
||||||
/// A Nix Flake.
|
/// A Nix Flake.
|
||||||
|
@ -63,11 +79,33 @@ impl FromStr for HivePath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum EvaluationMethod {
|
||||||
|
/// Use nix-instantiate and specify the entire Nix expression.
|
||||||
|
///
|
||||||
|
/// This is the default method.
|
||||||
|
///
|
||||||
|
/// For flakes, we use `builtins.getFlakes`. Pure evaluation no longer works
|
||||||
|
/// with this method in Nix 2.21+.
|
||||||
|
NixInstantiate,
|
||||||
|
|
||||||
|
/// Use `nix eval --apply` on top of a flake.
|
||||||
|
///
|
||||||
|
/// This can be activated with --experimental-flake-eval.
|
||||||
|
///
|
||||||
|
/// In this method, we can no longer pull in our bundled assets and
|
||||||
|
/// the flake must expose a compatible `colmenaHive` output.
|
||||||
|
DirectFlakeEval,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Hive {
|
pub struct Hive {
|
||||||
/// Path to the hive.
|
/// Path to the hive.
|
||||||
path: HivePath,
|
path: HivePath,
|
||||||
|
|
||||||
|
/// Method to evaluate the hive with.
|
||||||
|
evaluation_method: EvaluationMethod,
|
||||||
|
|
||||||
/// Path to the context directory.
|
/// Path to the context directory.
|
||||||
///
|
///
|
||||||
/// Normally this is directory containing the "hive.nix"
|
/// Normally this is directory containing the "hive.nix"
|
||||||
|
@ -134,6 +172,7 @@ impl Hive {
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path,
|
path,
|
||||||
|
evaluation_method: EvaluationMethod::NixInstantiate,
|
||||||
context_dir,
|
context_dir,
|
||||||
assets,
|
assets,
|
||||||
show_trace: false,
|
show_trace: false,
|
||||||
|
@ -158,6 +197,14 @@ impl Hive {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_evaluation_method(&mut self, method: EvaluationMethod) {
|
||||||
|
if !self.is_flake() && method == EvaluationMethod::DirectFlakeEval {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.evaluation_method = method;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_show_trace(&mut self, value: bool) {
|
pub fn set_show_trace(&mut self, value: bool) {
|
||||||
self.show_trace = value;
|
self.show_trace = value;
|
||||||
}
|
}
|
||||||
|
@ -421,7 +468,10 @@ impl Hive {
|
||||||
|
|
||||||
/// Returns the base expression from which the evaluated Hive can be used.
|
/// Returns the base expression from which the evaluated Hive can be used.
|
||||||
fn get_base_expression(&self) -> String {
|
fn get_base_expression(&self) -> String {
|
||||||
self.assets.get_base_expression()
|
match self.evaluation_method {
|
||||||
|
EvaluationMethod::NixInstantiate => self.assets.get_base_expression(),
|
||||||
|
EvaluationMethod::DirectFlakeEval => FLAKE_APPLY_SNIPPET.to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether this Hive is a flake.
|
/// Returns whether this Hive is a flake.
|
||||||
|
@ -444,6 +494,11 @@ impl<'hive> NixInstantiate<'hive> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate(&self) -> Command {
|
fn instantiate(&self) -> Command {
|
||||||
|
// TODO: Better error handling
|
||||||
|
if self.hive.evaluation_method == EvaluationMethod::DirectFlakeEval {
|
||||||
|
panic!("Instantiation is not supported with DirectFlakeEval");
|
||||||
|
}
|
||||||
|
|
||||||
let mut command = Command::new("nix-instantiate");
|
let mut command = Command::new("nix-instantiate");
|
||||||
|
|
||||||
if self.hive.is_flake() {
|
if self.hive.is_flake() {
|
||||||
|
@ -462,17 +517,48 @@ impl<'hive> NixInstantiate<'hive> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(self) -> Command {
|
fn eval(self) -> Command {
|
||||||
let mut command = self.instantiate();
|
|
||||||
let flags = self.hive.nix_flags();
|
let flags = self.hive.nix_flags();
|
||||||
command
|
|
||||||
.arg("--eval")
|
match self.hive.evaluation_method {
|
||||||
.arg("--json")
|
EvaluationMethod::NixInstantiate => {
|
||||||
.arg("--strict")
|
let mut command = self.instantiate();
|
||||||
// Ensures the derivations are instantiated
|
|
||||||
// Required for system profile evaluation and IFD
|
command
|
||||||
.arg("--read-write-mode")
|
.arg("--eval")
|
||||||
.args(flags.to_args());
|
.arg("--json")
|
||||||
command
|
.arg("--strict")
|
||||||
|
// Ensures the derivations are instantiated
|
||||||
|
// Required for system profile evaluation and IFD
|
||||||
|
.arg("--read-write-mode")
|
||||||
|
.args(flags.to_args());
|
||||||
|
|
||||||
|
command
|
||||||
|
}
|
||||||
|
EvaluationMethod::DirectFlakeEval => {
|
||||||
|
let mut command = Command::new("nix");
|
||||||
|
let flake = if let HivePath::Flake(flake) = self.hive.path() {
|
||||||
|
flake
|
||||||
|
} else {
|
||||||
|
panic!("The DirectFlakeEval evaluation method only support flakes");
|
||||||
|
};
|
||||||
|
|
||||||
|
let hive_installable = format!("{}#colmenaHive", flake.uri());
|
||||||
|
|
||||||
|
let mut full_expression = self.hive.get_base_expression();
|
||||||
|
full_expression += &self.expression;
|
||||||
|
|
||||||
|
command
|
||||||
|
.arg("eval") // nix eval
|
||||||
|
.args(["--extra-experimental-features", "flakes nix-command"])
|
||||||
|
.arg(hive_installable)
|
||||||
|
.arg("--json")
|
||||||
|
.arg("--apply")
|
||||||
|
.arg(&full_expression)
|
||||||
|
.args(flags.to_args());
|
||||||
|
|
||||||
|
command
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn instantiate_with_builders(self) -> ColmenaResult<Command> {
|
async fn instantiate_with_builders(self) -> ColmenaResult<Command> {
|
||||||
|
|
Loading…
Reference in a new issue