difftreelog
feat ensure lazy trees are enabled for everyone
in: trunk
4 files changed
crates/fleet-base/src/opts.rsdiffbeforeafterboth1use std::{2 collections::BTreeMap,3 env::current_dir,4 ffi::OsString,5 str::FromStr,6 sync::{Arc, Mutex},7};89use anyhow::{Context, Result, bail};10use nix_eval::{FetchSettings, FlakeReference, FlakeSettings, Value, nix_go, util::assert_warn};11use nom::{12 Parser,13 bytes::complete::take_while1,14 character::complete::char,15 combinator::{map, opt},16 multi::separated_list1,17 sequence::{preceded, separated_pair},18};1920use crate::{21 fleetdata::FleetData,22 host::{Config, ConfigHost, FleetConfigInternals},23};2425#[derive(Clone)]26pub enum HostItem {27 Host {28 name: String,29 attrs: BTreeMap<String, String>,30 },31 Tag {32 name: String,33 attrs: BTreeMap<String, String>,34 },35}36fn host_item_parser(input: &str) -> Result<HostItem, String> {37 fn err_to_string(err: nom::Err<nom::error::Error<&str>>) -> String {38 err.to_string()39 }4041 let (input, is_tag) = map(opt(char('@')), |c| c.is_some())42 .parse_complete(input)43 .map_err(err_to_string)?;44 let (input, name) = map(45 take_while1(|v| v != ',' && v != '?' && v != '@'),46 str::to_owned,47 )48 .parse_complete(input)49 .map_err(err_to_string)?;5051 let kw_item = separated_pair(52 map(take_while1(|v| v != '&' && v != '='), str::to_owned),53 char('='),54 map(take_while1(|v| v != '&'), str::to_owned),55 );56 let kw = map(separated_list1(char('&'), kw_item), |vec| {57 vec.into_iter().collect::<BTreeMap<_, _>>()58 });59 let mut opt_kw = map(opt(preceded(char('?'), kw)), Option::unwrap_or_default);6061 let (input, attrs) = opt_kw.parse_complete(input).map_err(err_to_string)?;6263 if !input.is_empty() {64 return Err(format!("unexpected trailing input: {input:?}"));65 }66 Ok(if is_tag {67 HostItem::Tag { name, attrs }68 } else {69 HostItem::Host { name, attrs }70 })71}7273// TODO: Rename to HostSelector74#[derive(clap::Parser, Clone)]75pub struct FleetOpts {76 /// All hosts except those would be skipped77 #[clap(long, number_of_values = 1, value_parser = host_item_parser)]78 pub only: Vec<HostItem>,7980 /// Hosts to skip81 #[clap(long, number_of_values = 1)]82 pub skip: Vec<String>,8384 /// Host, which should be threaten as current machine85 // TODO: Replace with connectivity refactor86 #[clap(long, default_value_t = hostname::get().expect("unknown hostname").to_str().expect("hostname is not utf-8").to_owned())]87 pub localhost: String,8889 /// Override detected system for host, to perform builds via90 /// binfmt-declared qemu instead of trying to crosscompile91 #[clap(long, default_value = env!("NIX_SYSTEM"))]92 pub local_system: String,9394 /// By default fleet continues on single derivation build failure95 /// this flag makes command fail immediately96 ///97 /// Opposite of Nix's --keep-going98 #[clap(long)]99 pub fail_fast: bool,100}101102impl FleetOpts {103 pub async fn filter_skipped(104 &self,105 hosts: impl IntoIterator<Item = ConfigHost>,106 ) -> Result<Vec<ConfigHost>> {107 let mut out = Vec::new();108 for host in hosts {109 if self.should_skip(&host).await? {110 continue;111 }112 out.push(host);113 }114 Ok(out)115 }116 pub async fn should_skip(&self, host: &ConfigHost) -> Result<bool> {117 if self.skip.iter().any(|h| h as &str == host.name) {118 return Ok(true);119 }120 if self.only.is_empty() {121 return Ok(false);122 }123 let mut have_group_matches = false;124 for item in self.only.iter() {125 match item {126 HostItem::Host { name, .. } if *name == host.name => {127 return Ok(false);128 }129 HostItem::Tag { .. } => {130 have_group_matches = true;131 }132 _ => {}133 }134 }135 if have_group_matches {136 let host_tags = host.tags().await?;137 for item in self.only.iter() {138 match item {139 HostItem::Tag { name, .. } if host_tags.contains(name) => {140 return Ok(false);141 }142 _ => {}143 }144 }145 }146 Ok(true)147 }148 pub async fn action_attr<T: FromStr>(&self, host: &ConfigHost, attr: &str) -> Result<Option<T>>149 where150 T::Err: Sync,151 anyhow::Error: From<T::Err>,152 {153 let str = self.action_attr_str(host, attr).await?;154 Ok(str.map(|v| T::from_str(&v)).transpose()?)155 }156 pub async fn action_attr_str(&self, host: &ConfigHost, attr: &str) -> Result<Option<String>> {157 if self.only.is_empty() {158 return Ok(None);159 }160 let mut have_group_matches = false;161 for item in self.only.iter() {162 match item {163 HostItem::Host { name, attrs }164 if *name == host.name && attrs.contains_key(attr) =>165 {166 return Ok(attrs.get(attr).cloned());167 }168 HostItem::Tag { attrs, .. } if attrs.contains_key(attr) => {169 have_group_matches = true;170 }171 _ => {}172 }173 }174 if have_group_matches {175 let host_tags = host.tags().await?;176 for item in self.only.iter() {177 match item {178 HostItem::Tag { name, attrs }179 if host_tags.contains(name) && attrs.contains_key(attr) =>180 {181 return Ok(attrs.get(attr).cloned());182 }183 _ => {}184 }185 }186 }187 Ok(None)188 }189 pub fn is_local(&self, host: &str) -> bool {190 self.localhost == host191 }192193 // TODO: Config should be detached from opts.194 pub async fn build(&self, nix_args: Vec<OsString>, assert: bool) -> Result<Config> {195 let cwd = current_dir()?;196 let mut directory = cwd.clone();197 let mut fleet_data_path = directory.join("fleet.nix");198 while !fleet_data_path.is_file() {199 // fleet.nix200 fleet_data_path.pop();201 if !directory.pop() || !fleet_data_path.pop() {202 bail!(203 "fleet.nix not found at {} or any of the parent directories",204 cwd.display()205 );206 }207 fleet_data_path.push("fleet.nix");208 }209 let bytes =210 std::fs::read_to_string(&fleet_data_path).context("reading fleet state (fleet.nix)")?;211 let data: Mutex<FleetData> = nixlike::parse_str(&bytes)?;212213 let mut fetch_settings = FetchSettings::new();214 fetch_settings.set(c"warn-dirty", c"false");215216 // TODO: use correct directory, not cwd217 let (mut flake, _) = FlakeReference::new(218 directory219 .to_str()220 .ok_or_else(|| anyhow::anyhow!("fleet dir should have utf-8 path"))?,221 &fetch_settings,222 )?;223 let flake = flake.lock(&fetch_settings)?;224225 let mut flake_settings = FlakeSettings::new()?;226 let flake = flake.get_attrs(&mut flake_settings)?;227228 let builtins_field = Value::eval("builtins")?;229230 let fleet_root = flake.get_field("fleetConfigurations")?;231 let data_val = Value::serialized(&data)?;232 let fleet_field = nix_go!(fleet_root.default(data_val));233234 let config_field = nix_go!(fleet_field.config);235236 if assert {237 assert_warn("fleet config evaluation", &config_field)238 .await239 .context("failed to verify assertions")?;240 }241242 let import = nix_go!(builtins_field.import);243 let overlays = nix_go!(config_field.nixpkgs.overlays);244 let nixpkgs = nix_go!(config_field.nixpkgs.buildUsing);245 let nixpkgs_imported = nix_go!(import(nixpkgs));246247 let default_pkgs = nix_go!(nixpkgs_imported(Obj {248 overlays,249 system: self.local_system.clone(),250 }));251252 Ok(Config(Arc::new(FleetConfigInternals {253 directory,254 data,255 local_system: self.local_system.clone(),256 nix_args,257 config_field,258 default_pkgs,259 nixpkgs,260 localhost: self.localhost.to_owned(),261 })))262 }263}crates/nix-eval/src/lib.rsdiffbeforeafterboth--- a/crates/nix-eval/src/lib.rs
+++ b/crates/nix-eval/src/lib.rs
@@ -11,6 +11,7 @@
use serde::de::DeserializeOwned;
pub use anyhow::Result;
+use tracing::instrument;
use self::logging::nix_logging_cxx;
use self::nix_cxx::set_fetcher_setting;
@@ -18,11 +19,12 @@
alloc_value, c_context, c_context_create, err_code, err_info_msg, eval_state_build,
eval_state_builder_new, expr_eval_from_string, fetchers_settings, fetchers_settings_free,
fetchers_settings_new, flake_lock, flake_lock_flags, flake_lock_flags_free,
- flake_lock_flags_new, flake_reference_parse_flags, flake_reference_parse_flags_free,
- flake_reference_parse_flags_new, flake_reference_parse_flags_set_base_directory,
- flake_settings, flake_settings_free, flake_settings_new, init_bool, init_int, init_string,
- locked_flake_free, locked_flake_get_output_attrs, set_err_msg, setting_set, state_free,
- value_decref, value_incref,
+ flake_lock_flags_new, flake_lock_flags_set_mode_virtual, flake_reference_parse_flags,
+ flake_reference_parse_flags_free, flake_reference_parse_flags_new,
+ flake_reference_parse_flags_set_base_directory, flake_settings, flake_settings_free,
+ flake_settings_new, init_bool, init_int, init_string, locked_flake_free,
+ locked_flake_get_output_attrs, set_err_msg, setting_set, state_free, value_decref,
+ value_incref,
};
// Contains macros helpers
@@ -94,7 +96,7 @@
#[derive(Debug)]
#[repr(i32)]
-enum NixErrorKind {
+pub enum NixErrorKind {
Unknown = 1,
Overflow = 2,
Key = 3,
@@ -136,6 +138,7 @@
pub struct ThreadRegisterGuard {}
impl ThreadRegisterGuard {
+ #[allow(clippy::new_without_default)]
pub fn new() -> Self {
gc_register_my_thread();
Self {}
@@ -200,6 +203,12 @@
Ok(o)
}
}
+
+impl Default for NixContext {
+ fn default() -> Self {
+ Self::new()
+ }
+}
impl Drop for NixContext {
fn drop(&mut self) {
unsafe {
@@ -232,6 +241,17 @@
}
// eval_s
})?;
+ ctx.run_in_context(|c| {
+ unsafe {
+ nix_raw::eval_state_builder_set_eval_setting(
+ c,
+ builder,
+ c"lazy-locks".as_ptr(),
+ c"true".as_ptr(),
+ )
+ }
+ // eval_s
+ })?;
let state = ctx
.run_in_context(|c| unsafe { eval_state_build(c, builder) })
.map(EvalState)?;
@@ -319,7 +339,7 @@
pub struct FlakeReferenceParseFlags(*mut flake_reference_parse_flags);
impl FlakeReferenceParseFlags {
- pub fn new(settings: &mut FlakeSettings) -> Result<Self> {
+ pub fn new(settings: &FlakeSettings) -> Result<Self> {
with_default_context(|c, _| unsafe { flake_reference_parse_flags_new(c, settings.0) })
.map(Self)
}
@@ -346,7 +366,11 @@
struct FlakeLockFlags(*mut flake_lock_flags);
impl FlakeLockFlags {
fn new(settings: &mut FlakeSettings) -> Result<Self> {
- with_default_context(|c, _| unsafe { flake_lock_flags_new(c, settings.0) }).map(Self)
+ let o = with_default_context(|c, _| unsafe { flake_lock_flags_new(c, settings.0) })
+ .map(Self)?;
+ with_default_context(|c, _| unsafe { flake_lock_flags_set_mode_virtual(c, o.0) })?;
+
+ Ok(o)
}
}
impl Drop for FlakeLockFlags {
@@ -381,10 +405,13 @@
pub struct FlakeReference(*mut nix_raw::flake_reference);
impl FlakeReference {
- pub fn new(s: &str, fetch: &FetchSettings) -> Result<(Self, String)> {
- let mut flake_settings = FlakeSettings::new()?;
- let parse_flags = FlakeReferenceParseFlags::new(&mut flake_settings)?;
-
+ #[instrument(name = "new-flake-reference", skip(flake, parse, fetch))]
+ pub fn new(
+ s: &str,
+ flake: &FlakeSettings,
+ parse: &FlakeReferenceParseFlags,
+ fetch: &FetchSettings,
+ ) -> Result<(Self, String)> {
let mut out = null_mut();
let mut fragment = String::new();
// let fetch_settings = fetcher_settings;
@@ -392,8 +419,8 @@
nix_raw::flake_reference_and_fragment_from_string(
c,
fetch.0,
- flake_settings.0,
- parse_flags.0,
+ flake.0,
+ parse.0,
s.as_ptr().cast(),
s.len(),
&mut out,
@@ -405,6 +432,7 @@
Ok((Self(out), fragment))
}
+ #[instrument(name = "lock-flake", skip(self, fetch))]
pub fn lock(&mut self, fetch: &FetchSettings) -> Result<LockedFlake> {
let mut settings = FlakeSettings::new()?;
let lock_flags = FlakeLockFlags::new(&mut settings)?;
crates/nix-eval/src/logging.ccdiffbeforeafterboth--- a/crates/nix-eval/src/logging.cc
+++ b/crates/nix-eval/src/logging.cc
@@ -8,19 +8,6 @@
TracingLogger() {}
bool isVerbose() override { return true; }
- // void addFields(nlohmann::json & json, const Fields & fields)
- // {
- // if (fields.empty())
- // return;
- // auto & arr = json["fields"] = nlohmann::json::array();
- // for (auto & f : fields)
- // if (f.type == Logger::Field::tInt)
- // arr.push_back(f.i);
- // else if (f.type == Logger::Field::tString)
- // arr.push_back(f.s);
- // else
- // unreachable();
- // }
void log(Verbosity lvl, std::string_view s) override {
rust::Str str(s.data(), s.size());
emit_log(lvl, str);
@@ -60,16 +47,18 @@
};
void writeToStdout(std::string_view s) override {
- printf("writeToStdout() called\n");
+ emit_warn("writeToStdout() called, but unsupported");
}
void warn(const std::string &msg) override { emit_warn(msg); }
virtual std::optional<char> ask(std::string_view s) {
- printf("ask() called\n");
+ emit_warn("ask() called, but unsupported");
return {};
}
};
extern "C" {
-void apply_tracing_logger() { logger = std::make_unique<TracingLogger>(); }
+void apply_tracing_logger() {
+ logger = std::make_unique<TracingLogger>();
+}
}
flake.lockdiffbeforeafterboth--- a/flake.lock
+++ b/flake.lock
@@ -105,7 +105,6 @@
},
"locked": {
"lastModified": 1757000273,
- "narHash": "sha256-9AKhwsSlegWnNFy8++OMNctrxJUUIE7nG4s4ZHmFPic=",
"owner": "deltarocks",
"repo": "nix",
"rev": "eba1f549ec21208cf98343f1351a95e2e6eb3fbb",