--- a/cmds/fleet/src/better_nix_eval.rs +++ b/cmds/fleet/src/better_nix_eval.rs @@ -16,7 +16,7 @@ use tokio::select; use tokio::sync::{mpsc, oneshot}; use tokio_util::codec::{FramedRead, LinesCodec}; -use tracing::{debug, error, warn}; +use tracing::{debug, error, warn, Level}; use crate::command::{ClonableHandler, Handler, NixHandler, NoopHandler}; @@ -247,6 +247,10 @@ Ok(()) } async fn send_command(&mut self, cmd: impl AsRef<[u8]>) -> Result<()> { + if tracing::enabled!(Level::DEBUG) { + let cmd_str = String::from_utf8_lossy(cmd.as_ref()); + tracing::debug!("{cmd_str}"); + }; self.stdin.write_all(cmd.as_ref()).await?; self.stdin.write_all(b"\n").await?; Ok(()) @@ -420,7 +424,7 @@ } pub fn apply(v: impl Serialize) -> Self { let serialized = nixlike::serialize(v).expect("invalid value for apply"); - Self::Apply(serialized) + Self::Apply(serialized.trim_end().to_owned()) } } impl Display for Index { @@ -434,8 +438,7 @@ write!(f, ".{v}") } Index::Apply(o) => { - let v = nixlike::serialize(o).map_err(|_| fmt::Error)?; - write!(f, "({v})") + write!(f, "({o})") } Index::Idx(i) => { write!(f, "[{i}]") @@ -451,7 +454,6 @@ struct PathDisplay<'i>(&'i [Index]); impl Display for PathDisplay<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "flake")?; for i in self.0 { write!(f, "{i}")?; } @@ -508,8 +510,8 @@ query.push_str(escaped.trim()); } Index::Apply(a) => { - query.push(' '); - query.push_str(&a); + // In cases like `a {}.b` first `{}.b` will be evaluated, so `a {}` should be encased in `()` + query = format!("({query} {a})"); } Index::Idx(idx) => { query = format!("builtins.elemAt ({query}) {idx}"); @@ -560,7 +562,7 @@ .await .execute_expression_raw(&format!(":b sess_field_{id}"), &mut NixHandler::default()) .await?; - ensure!(!vid.is_empty(), "build failed"); + ensure!(!vid.is_empty(), "build failed: {}", PathDisplay(&self.full_path)); let Some(vid) = vid.strip_prefix("This derivation produced the following outputs:\n") else { panic!("unexpected build output: {vid:?}"); --- a/cmds/fleet/src/cmds/build_systems.rs +++ b/cmds/fleet/src/cmds/build_systems.rs @@ -5,7 +5,7 @@ use crate::command::MyCommand; use crate::host::Config; use crate::nix_path; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Result, Context}; use clap::Parser; use itertools::Itertools; use tokio::{task::LocalSet, time::sleep}; @@ -292,13 +292,14 @@ let action = Action::from(self.subcommand.clone()); let drv = config .fleet_field - .select(nix_path!(.buildSystems.{action.build_attr()}.{&host})) - .await?; + .select(nix_path!(.buildSystems((serde_json::json!({ + "localSystem": config.local_system.clone(), + }))).{action.build_attr()}.{&host})) + .await.context("system attribute")?; let outputs = drv.build().await.map_err(|e| { if action.build_attr() == "sdImage" { info!("sd-image build failed"); info!("Make sure you have imported modulesPath/installer/sd-card/sd-image-[-installer].nix (For installer, you may want to check config)"); - info!("This module was automatically imported before, but was removed for better customization") } e })?; @@ -311,6 +312,10 @@ if !config.is_local(&host) { info!("uploading system closure"); { + // Alternatively, nix store make-content-addressed can be used, + // at least for the first deployment, to provide trusted store key. + // + // It is much slower, yet doesn't require root on the deployer machine. let mut sign = MyCommand::new("nix"); // Private key for host machine is registered in nix-sign.nix sign.arg("store") --- a/cmds/fleet/src/host.rs +++ b/cmds/fleet/src/host.rs @@ -13,7 +13,7 @@ use tempfile::NamedTempFile; use crate::{ - better_nix_eval::{Field, Index, NixSessionPool}, + better_nix_eval::{Field, NixSessionPool}, command::MyCommand, fleetdata::{FleetData, FleetSecret, FleetSharedSecret}, nix_path, @@ -250,7 +250,6 @@ #[clap(long)] pub localhost: Option, - // TODO: unhardcode x86_64-linux /// Override detected system for host, to perform builds via /// binfmt-declared qemu instead of trying to crosscompile #[clap(long, default_value = "detect")] @@ -280,7 +279,7 @@ let fleet_root = Field::field(root_field, "fleetConfigurations").await?; let fleet_field = fleet_root - .select(nix_path!(.default.{&local_system})) + .select(nix_path!(.default)) .await?; let config_field = fleet_field .select(nix_path!(.configUnchecked)) --- a/cmds/fleet/src/main.rs +++ b/cmds/fleet/src/main.rs @@ -24,7 +24,7 @@ use host::{Config, FleetOpts}; use human_repr::HumanCount; use indicatif::{ProgressState, ProgressStyle}; -use tracing::{info, metadata::LevelFilter}; +use tracing::info; use tracing::{info_span, Instrument}; use tracing_indicatif::IndicatifLayer; use tracing_subscriber::{prelude::*, EnvFilter}; @@ -99,27 +99,6 @@ Ok(()) } -// fn main() -> Result<()> { -// let pool = r2d2::Builder::::new() -// .min_idle(Some(1)) -// .max_lifetime(Some(Duration::from_secs(10))) -// .build(NixSessionPool { -// flake: ".".to_owned(), -// nix_args: vec![], -// })?; -// let conn = pool.get()?; -// let field = Field::root(conn); -// // let builtins = field.get_field("builtins")?; -// let cur_sys: String = field.get_field("builtins")?.as_json()?; -// eprintln!("current system = {cur_sys}"); -// let v = field.get_field("fleetConfigurations")?; -// eprintln!("configs = {:?}", v.list_fields()?); -// let d = v.get_field("default")?; -// dbg!(d.list_fields()); -// Ok(()) -// } -// - fn setup_logging() { let indicatif_layer = IndicatifLayer::new().with_progress_style( ProgressStyle::with_template( @@ -157,7 +136,7 @@ ), ); - let filter = EnvFilter::from_default_env().add_directive(LevelFilter::INFO.into()); + let filter = EnvFilter::from_default_env(); tracing_subscriber::registry() .with( --- a/lib/default.nix +++ b/lib/default.nix @@ -11,8 +11,7 @@ inherit nixpkgs hostNames; }; in - # Top-level arg is the builder system (not the target system!) - nixpkgs.lib.genAttrs flake-utils.lib.defaultSystems (system: let + let withData = data: rec { root = nixpkgs.lib.evalModules { modules = (import ../modules/fleet/_modules.nix) ++ [config data]; @@ -36,21 +35,7 @@ inherit name; value = nixpkgs.lib.nixosSystem { system = configuredHosts.${name}.system; - modules = - configuredHosts.${name}.modules - ++ extraModules - ++ [ - ({...}: { - nixpkgs.system = system; - nixpkgs.localSystem.system = system; - nixpkgs.crossSystem = - if system == configuredHosts.${name}.system - then null - else { - system = configuredHosts.${name}.system; - }; - }) - ]; + modules = configuredHosts.${name}.modules ++ extraModules; specialArgs = { inherit fleetLib; fleet = fleetLib.hostsToAttrs (host: configuredSystems.${host}.config); @@ -60,19 +45,28 @@ ) (builtins.attrNames rootAssertWarn.config.hosts) ); - buildSystems = { + buildSystems = {localSystem}: let + buildConfigurationModule = {config, ...}: { + # Equivalent to nixpkgs.localSystem + # nixpkgs.system = localSystem; + nixpkgs.buildPlatform.system = localSystem; + }; + in { toplevel = builtins.mapAttrs (_name: value: value.config.system.build.toplevel) (configuredSystemsWithExtraModules [ + buildConfigurationModule ({...}: { buildTarget = "toplevel"; }) ]); sdImage = builtins.mapAttrs (_name: value: value.config.system.build.sdImage) (configuredSystemsWithExtraModules [ + buildConfigurationModule #(nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix") ({...}: { buildTarget = "sd-image"; }) ]); installationCd = builtins.mapAttrs (_name: value: value.config.system.build.isoImage) (configuredSystemsWithExtraModules [ + buildConfigurationModule (nixpkgs + "/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix") ({lib, ...}: { buildTarget = "installation-cd"; @@ -91,5 +85,5 @@ in { inherit (injectedData) configuredHosts configuredSecrets configuredSystems buildSystems configUnchecked; }; - }); + }; }