diff --git a/Cargo.lock b/Cargo.lock index b8ba41d..f96e0b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -941,6 +941,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nix-config-parser" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a94767d6f881412ae02a87a4b6136b4079cbda9d3eab8f4ae272c9db740902c" +dependencies = [ + "serde", + "thiserror", +] + [[package]] name = "nix-installer" version = "0.12.1-unreleased" @@ -955,6 +965,7 @@ dependencies = [ "glob", "is_ci", "nix", + "nix-config-parser", "os-release", "owo-colors", "plist", diff --git a/Cargo.toml b/Cargo.toml index 076050f..ae9f438 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ uuid = { version = "1.2.2", features = ["serde"] } os-release = { version = "0.1.0", default-features = false } is_ci = { version = "1.1.1", default-features = false, optional = true } strum = { version = "0.25.0", features = ["derive"] } +nix-config-parser = { version = "0.1.2", features = ["serde"] } which = "4.4.0" sysctl = "0.5.4" walkdir = "2.3.3" diff --git a/src/action/base/create_file.rs b/src/action/base/create_file.rs index 88183a3..18667ff 100644 --- a/src/action/base/create_file.rs +++ b/src/action/base/create_file.rs @@ -27,7 +27,7 @@ pub struct CreateFile { group: Option, mode: Option, buf: String, - replace: bool, + force: bool, } impl CreateFile { @@ -38,7 +38,7 @@ impl CreateFile { group: impl Into>, mode: impl Into>, buf: String, - replace: bool, + force: bool, ) -> Result, ActionError> { let path = path.as_ref().to_path_buf(); let mode = mode.into(); @@ -50,7 +50,7 @@ impl CreateFile { group, mode, buf, - replace, + force, }; if this.path.exists() { @@ -128,7 +128,7 @@ impl CreateFile { .map_err(|e| ActionErrorKind::Read(this.path.clone(), e)) .map_err(Self::error)?; - if discovered_buf != this.buf && !this.replace { + if discovered_buf != this.buf { return Err(Self::error(ActionErrorKind::DifferentContent( this.path.clone(), ))); @@ -183,8 +183,7 @@ impl Action for CreateFile { group, mode, buf, - // If `replace` was not set, and the file existed, the `plan` step would have errored - replace: _, + force: _, } = self; if tracing::enabled!(tracing::Level::TRACE) { @@ -248,7 +247,7 @@ impl Action for CreateFile { group: _, mode: _, buf: _, - replace: _, + force: _, } = &self; vec![ActionDescription::new( @@ -265,7 +264,7 @@ impl Action for CreateFile { group: _, mode: _, buf: _, - replace: _, + force: _, } = self; // The user already deleted it if !path.exists() { diff --git a/src/action/base/create_or_insert_into_file.rs b/src/action/base/create_or_insert_into_file.rs index aa7ea7c..cf0e237 100644 --- a/src/action/base/create_or_insert_into_file.rs +++ b/src/action/base/create_or_insert_into_file.rs @@ -19,10 +19,6 @@ use tracing::{span, Span}; pub enum Position { Beginning, End, - Before { - index: usize, - expected_content: String, - }, } /** Create a file at the given location with the provided `buf` as @@ -144,20 +140,6 @@ impl CreateOrInsertIntoFile { return Ok(StatefulAction::completed(this)); } - if let Position::Before { - index, - expected_content, - } = &this.position - { - if discovered_buf.lines().nth(*index) != Some(expected_content.as_str()) { - return Err(ActionErrorKind::DifferentLineContent( - this.path.clone(), - this.position.clone(), - )) - .map_err(Self::error); - } - } - // If not, we can't skip this, so we still do it } @@ -239,75 +221,33 @@ impl Action for CreateOrInsertIntoFile { ActionErrorKind::Open(temp_file_path.clone(), e) }).map_err(Self::error)?; - match position { - Position::End => { - if let Some(ref mut orig_file) = orig_file { - tokio::io::copy(orig_file, &mut temp_file) - .await - .map_err(|e| { - ActionErrorKind::Copy(path.to_owned(), temp_file_path.to_owned(), e) - }) - .map_err(Self::error)?; - } - - temp_file - .write_all(buf.as_bytes()) + if *position == Position::End { + if let Some(ref mut orig_file) = orig_file { + tokio::io::copy(orig_file, &mut temp_file) .await - .map_err(|e| ActionErrorKind::Write(temp_file_path.clone(), e)) + .map_err(|e| { + ActionErrorKind::Copy(path.to_owned(), temp_file_path.to_owned(), e) + }) .map_err(Self::error)?; - }, - Position::Beginning => { - temp_file - .write_all(buf.as_bytes()) + } + } + + temp_file + .write_all(buf.as_bytes()) + .await + .map_err(|e| ActionErrorKind::Write(temp_file_path.clone(), e)) + .map_err(Self::error)?; + + if *position == Position::Beginning { + if let Some(ref mut orig_file) = orig_file { + tokio::io::copy(orig_file, &mut temp_file) .await - .map_err(|e| ActionErrorKind::Write(temp_file_path.clone(), e)) + .map_err(|e| { + ActionErrorKind::Copy(path.to_owned(), temp_file_path.to_owned(), e) + }) .map_err(Self::error)?; - - if let Some(ref mut orig_file) = orig_file { - tokio::io::copy(orig_file, &mut temp_file) - .await - .map_err(|e| { - ActionErrorKind::Copy(path.to_owned(), temp_file_path.to_owned(), e) - }) - .map_err(Self::error)?; - } - }, - Position::Before { - index, - expected_content, - } => { - let mut original_content_buf = Vec::new(); - if let Some(ref mut orig_file) = orig_file { - tokio::io::copy(orig_file, &mut original_content_buf) - .await - .map_err(|e| { - ActionErrorKind::Copy(path.to_owned(), temp_file_path.to_owned(), e) - }) - .map_err(Self::error)?; - } - let original_content = String::from_utf8(original_content_buf) - .map_err(ActionErrorKind::FromUtf8) - .map_err(Self::error)?; - let mut original_content_lines = original_content.lines().collect::>(); - // The last line should match expected - if original_content_lines.get(*index).copied() != Some(expected_content.as_str()) { - return Err(ActionErrorKind::DifferentLineContent( - path.clone(), - position.clone(), - )) - .map_err(Self::error); - } - - original_content_lines.insert(*index, buf); - let new_content = original_content_lines.join("\n"); - - temp_file - .write_all(new_content.as_bytes()) - .await - .map_err(|e| ActionErrorKind::Write(temp_file_path.clone(), e)) - .map_err(Self::error)?; - }, - }; + } + } let gid = if let Some(group) = group { Some( diff --git a/src/action/base/create_or_merge_nix_config.rs b/src/action/base/create_or_merge_nix_config.rs new file mode 100644 index 0000000..250ac0b --- /dev/null +++ b/src/action/base/create_or_merge_nix_config.rs @@ -0,0 +1,716 @@ +use std::{ + os::unix::fs::PermissionsExt, + path::{Path, PathBuf}, +}; + +use nix_config_parser::NixConfig; +use rand::Rng; +use tokio::{ + fs::{remove_file, OpenOptions}, + io::AsyncWriteExt, +}; +use tracing::{span, Span}; + +use crate::action::{ + Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction, +}; + +/// The `nix.conf` configuration names that are safe to merge. +// FIXME(@cole-h): make configurable by downstream users? +const MERGEABLE_CONF_NAMES: &[&str] = &["experimental-features"]; +const NIX_CONF_MODE: u32 = 0o664; +const NIX_CONF_COMMENT_CHAR: char = '#'; + +#[non_exhaustive] +#[derive(Debug, thiserror::Error)] +pub enum CreateOrMergeNixConfigError { + #[error(transparent)] + ParseNixConfig(#[from] nix_config_parser::ParseError), + #[error("Could not merge Nix configuration for key(s) {}; consider removing them from `{1}` in your editor, or removing your existing configuration with `rm {1}`", + .0 + .iter() + .map(|v| format!("`{v}`")) + .collect::>() + .join(", "))] + UnmergeableConfig(Vec, std::path::PathBuf), +} + +impl From for ActionErrorKind { + fn from(val: CreateOrMergeNixConfigError) -> Self { + ActionErrorKind::Custom(Box::new(val)) + } +} + +/// Create or merge an existing `nix.conf` at the specified path. +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateOrMergeNixConfig { + pub(crate) path: PathBuf, + pending_nix_config: NixConfig, +} + +impl CreateOrMergeNixConfig { + #[tracing::instrument(level = "debug", skip_all)] + pub async fn plan( + path: impl AsRef, + pending_nix_config: NixConfig, + ) -> Result, ActionError> { + let path = path.as_ref().to_path_buf(); + + let this = Self { + path, + pending_nix_config, + }; + + if this.path.exists() { + let (merged_nix_config, _) = + Self::validate_existing_nix_config(&this.pending_nix_config, &this.path)?; + + if !merged_nix_config.settings().is_empty() { + return Ok(StatefulAction::uncompleted(this)); + } else { + tracing::debug!( + "Setting Nix configurations in `{}` already complete", + this.path.display() + ); + return Ok(StatefulAction::completed(this)); + } + } + + Ok(StatefulAction::uncompleted(this)) + } + + fn merge_pending_and_existing_nix_config( + pending_nix_config: &NixConfig, + existing_nix_config: &NixConfig, + path: &Path, + ) -> Result<(NixConfig, NixConfig), CreateOrMergeNixConfigError> { + let mut merged_nix_config = NixConfig::new(); + let mut unmergeable_config_names = Vec::new(); + + for (pending_conf_name, pending_conf_value) in pending_nix_config.settings() { + if let Some(existing_conf_value) = existing_nix_config.settings().get(pending_conf_name) + { + let pending_conf_value = pending_conf_value.split(' ').collect::>(); + let existing_conf_value = existing_conf_value.split(' ').collect::>(); + + if pending_conf_value + .iter() + .all(|e| existing_conf_value.contains(e)) + { + // If _all_ the values we want are present in the existing config, + // merged_nix_config will be empty and this will be marked as completed. We + // don't return early here because there may be more config options to + // check. + } else if MERGEABLE_CONF_NAMES.contains(&pending_conf_name.as_str()) { + let mut merged_conf_value = + Vec::with_capacity(pending_conf_value.len() + existing_conf_value.len()); + merged_conf_value.extend(pending_conf_value); + merged_conf_value.extend(existing_conf_value); + merged_conf_value.dedup(); + let merged_conf_value = merged_conf_value.join(" "); + let merged_conf_value = merged_conf_value.trim(); + + merged_nix_config + .settings_mut() + .insert(pending_conf_name.to_owned(), merged_conf_value.to_owned()); + } else { + unmergeable_config_names.push(pending_conf_name.to_owned()); + } + } else { + merged_nix_config + .settings_mut() + .insert(pending_conf_name.to_owned(), pending_conf_value.to_owned()); + } + } + + if !unmergeable_config_names.is_empty() { + return Err(CreateOrMergeNixConfigError::UnmergeableConfig( + unmergeable_config_names, + path.to_path_buf(), + )); + } + + Ok((merged_nix_config, existing_nix_config.clone())) + } + + fn validate_existing_nix_config( + pending_nix_config: &NixConfig, + path: &Path, + ) -> Result<(NixConfig, NixConfig), ActionError> { + let path = path.to_path_buf(); + let metadata = path + .metadata() + .map_err(|e| Self::error(ActionErrorKind::GettingMetadata(path.clone(), e)))?; + + if !metadata.is_file() { + return Err(Self::error(ActionErrorKind::PathWasNotFile(path))); + } + + // Does the file have the right permissions? + let discovered_mode = metadata.permissions().mode(); + // We only care about user-group-other permissions + let discovered_mode = discovered_mode & 0o777; + + if discovered_mode != NIX_CONF_MODE { + return Err(Self::error(ActionErrorKind::PathModeMismatch( + path, + discovered_mode, + NIX_CONF_MODE, + ))); + } + + let existing_nix_config = NixConfig::parse_file(&path) + .map_err(CreateOrMergeNixConfigError::ParseNixConfig) + .map_err(Self::error)?; + + let (merged_nix_config, existing_nix_config) = Self::merge_pending_and_existing_nix_config( + pending_nix_config, + &existing_nix_config, + &path, + ) + .map_err(Self::error)?; + + Ok((merged_nix_config, existing_nix_config)) + } +} + +#[async_trait::async_trait] +#[typetag::serde(name = "create_or_merge_nix_config")] +impl Action for CreateOrMergeNixConfig { + fn action_tag() -> ActionTag { + ActionTag("create_or_merge_nix_config") + } + fn tracing_synopsis(&self) -> String { + format!( + "Merge or create nix.conf file `{path}`", + path = self.path.display(), + ) + } + + fn tracing_span(&self) -> Span { + let span = span!( + tracing::Level::DEBUG, + "create_or_merge_nix_config", + path = tracing::field::display(self.path.display()), + mode = tracing::field::display(format!("{:#o}", NIX_CONF_MODE)), + pending_nix_config = tracing::field::Empty, + ); + + if tracing::enabled!(tracing::Level::TRACE) { + span.record( + "pending_nix_config", + &self + .pending_nix_config + .settings() + .iter() + .map(|(k, v)| format!("{k}=\"{v}\"")) + .collect::>() + .join(","), + ); + } + span + } + + fn execute_description(&self) -> Vec { + vec![ActionDescription::new( + self.tracing_synopsis(), + vec![format!( + "Added settings: {settings}", + settings = self + .pending_nix_config + .settings() + .iter() + .map(|(k, v)| format!("{k}=\"{v}\"")) + .collect::>() + .join(", "), + )], + )] + } + + #[tracing::instrument(level = "debug", skip_all)] + async fn execute(&mut self) -> Result<(), ActionError> { + let Self { + path, + pending_nix_config, + } = self; + + if tracing::enabled!(tracing::Level::TRACE) { + let span = tracing::Span::current(); + span.record( + "pending_nix_config", + pending_nix_config + .settings() + .iter() + .map(|(k, v)| format!("{k}='{v}'")) + .collect::>() + .join(" "), + ); + } + + // Create a temporary file in the same directory as the one + // that the final file goes in, so that we can rename it + // atomically + let parent_dir = path.parent().expect("File must be in a directory"); + let mut temp_file_path = parent_dir.to_owned(); + { + let mut rng = rand::thread_rng(); + temp_file_path.push(format!("nix-installer-tmp.{}", rng.gen::())); + } + let mut temp_file = OpenOptions::new() + .create(true) + .write(true) + // If the file is created, ensure that it has harmless + // permissions regardless of whether the mode will be + // changed later (if we ever create setuid executables, + // they should only become setuid once they are owned by + // the appropriate user) + .mode(0o600) + .open(&temp_file_path) + .await + .map_err(|e| { + Self::error(ActionErrorKind::Open(temp_file_path.clone(), e)) + })?; + + let (mut merged_nix_config, mut existing_nix_config) = if path.exists() { + let (merged_nix_config, existing_nix_config) = + Self::validate_existing_nix_config(pending_nix_config, path)?; + (merged_nix_config, Some(existing_nix_config)) + } else { + (pending_nix_config.clone(), None) + }; + + let mut new_config = String::new(); + + if let Some(existing_nix_config) = existing_nix_config.as_mut() { + let mut discovered_buf = tokio::fs::read_to_string(&path) + .await + .map_err(|e| Self::error(ActionErrorKind::Read(path.to_path_buf(), e)))?; + + // We append a newline to ensure that, in the case there are comments at the end of the + // file and _NO_ trailing newline, we still preserve the entire block of comments. + discovered_buf.push('\n'); + + let (associated_lines, _, _) = discovered_buf.split('\n').fold( + (Vec::new(), Vec::new(), false), + |(mut all_assoc, mut current_assoc, mut associating): ( + Vec>, + Vec, + bool, + ), + line| { + let line = line.trim(); + + // Don't associate our "Generated by" comment if it appears + if line.starts_with("# Generated by") { + return (all_assoc, current_assoc, associating); + } + + if line.starts_with(NIX_CONF_COMMENT_CHAR) { + associating = true; + } else if line.is_empty() || !line.starts_with(NIX_CONF_COMMENT_CHAR) { + associating = false; + } + + current_assoc.push(line.to_string()); + + if !associating { + all_assoc.push(current_assoc); + current_assoc = Vec::new(); + } + + (all_assoc, current_assoc, associating) + }, + ); + + for line_group in associated_lines { + if line_group.is_empty() || line_group.iter().all(|line| line.is_empty()) { + continue; + } + + // This expect should never reasonably panic, because we would need a line group + // consisting solely of a comment and nothing else, but unconditionally appending a + // newline to the config string before grouping above prevents this from occurring. + let line_idx = line_group + .iter() + .position(|line| !line.starts_with(NIX_CONF_COMMENT_CHAR)) + .expect("There should always be one line without a comment character"); + + let setting_line = &line_group[line_idx]; + let comments = line_group[..line_idx].join("\n"); + + // If we're here, but the line without a comment char is empty, we have + // standalone comments to preserve, but no settings with inline comments. + if setting_line.is_empty() { + for line in &line_group { + new_config.push_str(line); + new_config.push('\n'); + } + + continue; + } + + // Preserve inline comments for settings we've merged + let to_remove = if let Some((name, value)) = existing_nix_config + .settings() + .iter() + .find(|(name, _value)| setting_line.starts_with(*name)) + { + let inline_comment_idx = + if let Some(idx) = setting_line.find(NIX_CONF_COMMENT_CHAR) { + idx + } else { + continue; + }; + + let inline_comment = &setting_line[inline_comment_idx..]; + + new_config.push_str(&comments); + new_config.push('\n'); + new_config.push_str(name); + new_config.push_str(" = "); + + if let Some(merged_value) = merged_nix_config.settings_mut().remove(name) { + new_config.push_str(&merged_value); + new_config.push(' '); + } else { + new_config.push_str(value); + } + + new_config.push_str(inline_comment); + new_config.push('\n'); + + Some(name.clone()) + } else { + new_config.push_str(&comments); + new_config.push('\n'); + new_config.push_str(setting_line); + new_config.push('\n'); + + None + }; + + if let Some(to_remove) = to_remove { + existing_nix_config.settings_mut().remove(&to_remove); + } + } + + // Add the leftover existing nix config + for (name, value) in existing_nix_config.settings() { + if merged_nix_config.settings().get(name).is_some() { + continue; + } + + new_config.push_str(name); + new_config.push_str(" = "); + new_config.push_str(value); + new_config.push('\n'); + } + + new_config.push('\n'); + } + + new_config.push_str(&format!( + "# Generated by https://github.com/DeterminateSystems/nix-installer, version {version}.\n", + version = env!("CARGO_PKG_VERSION"), + )); + + for (name, value) in merged_nix_config.settings() { + new_config.push_str(name); + new_config.push_str(" = "); + new_config.push_str(value); + new_config.push('\n'); + } + + temp_file + .write_all(new_config.as_bytes()) + .await + .map_err(|e| Self::error(ActionErrorKind::Write(temp_file_path.clone(), e)))?; + tokio::fs::set_permissions(&temp_file_path, PermissionsExt::from_mode(NIX_CONF_MODE)) + .await + .map_err(|e| { + Self::error(ActionErrorKind::SetPermissions( + NIX_CONF_MODE, + path.to_owned(), + e, + )) + })?; + temp_file + .sync_all() + .await + .map_err(|e| Self::error(ActionErrorKind::Sync(temp_file_path.clone(), e)))?; + tokio::fs::rename(&temp_file_path, &path) + .await + .map_err(|e| { + Self::error(ActionErrorKind::Rename( + temp_file_path.to_owned(), + path.to_owned(), + e, + )) + })?; + + Ok(()) + } + + fn revert_description(&self) -> Vec { + let Self { + path, + pending_nix_config: _, + } = &self; + + vec![ActionDescription::new( + format!("Delete file `{}`", path.display()), + vec![format!("Delete file `{}`", path.display())], + )] + } + + #[tracing::instrument(level = "debug", skip_all)] + async fn revert(&mut self) -> Result<(), ActionError> { + let Self { + path, + pending_nix_config: _, + } = self; + + remove_file(&path) + .await + .map_err(|e| Self::error(ActionErrorKind::Remove(path.to_owned(), e)))?; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use color_eyre::eyre::eyre; + use tokio::fs::write; + + #[tokio::test] + async fn creates_and_deletes_file() -> eyre::Result<()> { + let temp_dir = tempfile::TempDir::new()?; + let test_file = temp_dir.path().join("creates_and_deletes_file"); + let mut nix_config = NixConfig::new(); + nix_config + .settings_mut() + .insert("experimental-features".into(), "ca-references".into()); + let mut action = CreateOrMergeNixConfig::plan(&test_file, nix_config).await?; + + action.try_execute().await?; + + let s = std::fs::read_to_string(&test_file)?; + assert!(s.contains("# Generated by")); + assert!(s.contains("ca-references")); + assert!(NixConfig::parse_file(&test_file).is_ok()); + + action.try_revert().await?; + + assert!(!test_file.exists(), "File should have been deleted"); + + Ok(()) + } + + #[tokio::test] + async fn creates_and_deletes_file_even_if_edited() -> eyre::Result<()> { + let temp_dir = tempfile::TempDir::new()?; + let test_file = temp_dir + .path() + .join("creates_and_deletes_file_even_if_edited"); + let mut nix_config = NixConfig::new(); + nix_config + .settings_mut() + .insert("experimental-features".into(), "ca-references".into()); + let mut action = CreateOrMergeNixConfig::plan(&test_file, nix_config).await?; + + action.try_execute().await?; + + write(test_file.as_path(), "More content").await?; + + action.try_revert().await?; + + assert!(!test_file.exists(), "File should have been deleted"); + + Ok(()) + } + + #[tokio::test] + async fn recognizes_existing_exact_files_and_reverts_them() -> eyre::Result<()> { + let temp_dir = tempfile::TempDir::new()?; + let test_file = temp_dir + .path() + .join("recognizes_existing_exact_files_and_reverts_them"); + + let test_content = "experimental-features = flakes"; + write(test_file.as_path(), test_content).await?; + tokio::fs::set_permissions(&test_file, PermissionsExt::from_mode(NIX_CONF_MODE)).await?; + + let mut nix_config = NixConfig::new(); + nix_config + .settings_mut() + .insert("experimental-features".into(), "flakes".into()); + let mut action = CreateOrMergeNixConfig::plan(&test_file, nix_config).await?; + + action.try_execute().await?; + + action.try_revert().await?; + + assert!(!test_file.exists(), "File should have been deleted"); + + Ok(()) + } + + #[tokio::test] + async fn recognizes_existing_different_files_and_merges() -> eyre::Result<()> { + let temp_dir = tempfile::TempDir::new()?; + let test_file = temp_dir + .path() + .join("recognizes_existing_different_files_and_merges"); + + write( + test_file.as_path(), + "experimental-features = flakes\nwarn-dirty = true\n", + ) + .await?; + tokio::fs::set_permissions(&test_file, PermissionsExt::from_mode(NIX_CONF_MODE)).await?; + + let mut nix_config = NixConfig::new(); + nix_config + .settings_mut() + .insert("experimental-features".into(), "nix-command flakes".into()); + nix_config + .settings_mut() + .insert("allow-dirty".into(), "false".into()); + let mut action = CreateOrMergeNixConfig::plan(&test_file, nix_config).await?; + + action.try_execute().await?; + + let s = std::fs::read_to_string(&test_file)?; + assert!(s.contains("# Generated by")); + assert!(s.contains("flakes")); + assert!(s.contains("nix-command")); + assert_eq!( + s.matches("flakes").count(), + 1, + "we should not duplicate strings" + ); + assert!(s.contains("allow-dirty = false")); + assert!(s.contains("warn-dirty = true")); + assert!(NixConfig::parse_file(&test_file).is_ok()); + + action.try_revert().await?; + + assert!(!test_file.exists(), "File should have been deleted"); + + Ok(()) + } + + #[tokio::test] + async fn recognizes_existing_different_files_and_fails_to_merge() -> eyre::Result<()> { + let temp_dir = tempfile::TempDir::new()?; + let test_file = temp_dir + .path() + .join("recognizes_existing_different_files_and_fails_to_merge"); + + write( + test_file.as_path(), + "experimental-features = flakes\nwarn-dirty = true\n", + ) + .await?; + tokio::fs::set_permissions(&test_file, PermissionsExt::from_mode(NIX_CONF_MODE)).await?; + + let mut nix_config = NixConfig::new(); + nix_config + .settings_mut() + .insert("experimental-features".into(), "nix-command flakes".into()); + nix_config + .settings_mut() + .insert("warn-dirty".into(), "false".into()); + match CreateOrMergeNixConfig::plan(&test_file, nix_config).await { + Err(err) => { + if let ActionErrorKind::Custom(e) = err.kind() { + match e.downcast_ref::() { + Some(CreateOrMergeNixConfigError::UnmergeableConfig(_, path)) => { + assert_eq!(path, test_file.as_path()) + }, + _ => { + return Err(eyre!( + "Should have returned CreateOrMergeNixConfigError::UnmergeableConfig" + )) + }, + } + } + }, + _ => { + return Err(eyre!( + "Should have returned CreateOrMergeNixConfigError::UnmergeableConfig" + )) + }, + } + + assert!(test_file.exists(), "File should not have been deleted"); + + Ok(()) + } + + #[tokio::test] + async fn preserves_comments() -> eyre::Result<()> { + let temp_dir = tempfile::TempDir::new()?; + let test_file = temp_dir.path().join("preserves_comments"); + + write( + test_file.as_path(), + "# test 2\n# test\nexperimental-features = flakes # some inline comment about experimental-features\n# the following line should be warn-dirty = true\nwarn-dirty = true # this is an inline comment\n# this is an ungrouped comment\n# this too", + ) + .await?; + tokio::fs::set_permissions(&test_file, PermissionsExt::from_mode(NIX_CONF_MODE)).await?; + let mut nix_config = NixConfig::new(); + nix_config + .settings_mut() + .insert("experimental-features".into(), "ca-references".into()); + let mut action = CreateOrMergeNixConfig::plan(&test_file, nix_config).await?; + + action.try_execute().await?; + + let s = std::fs::read_to_string(&test_file)?; + assert!(s.contains("# the following line should be warn-dirty = true\nwarn-dirty = true")); + assert!(s.contains("# test 2\n# test\nexperimental-features")); + assert!(s.contains("# this is an inline comment")); + assert!(s.contains("# some inline comment about experimental-features")); + assert!(s.contains("# Generated by")); + assert!(s.contains("# this is an ungrouped comment\n# this too")); + assert!(s.contains("ca-references")); + assert!(NixConfig::parse_file(&test_file).is_ok()); + + action.try_revert().await?; + + assert!(!test_file.exists(), "File should have been deleted"); + + Ok(()) + } + + #[tokio::test] + async fn preserves_comments_edge_case() -> eyre::Result<()> { + let temp_dir = tempfile::TempDir::new()?; + let test_file = temp_dir.path().join("preserves_comments"); + + write(test_file.as_path(), " a = b\n c = d# lol\n# e = f").await?; + tokio::fs::set_permissions(&test_file, PermissionsExt::from_mode(NIX_CONF_MODE)).await?; + let mut nix_config = NixConfig::new(); + nix_config + .settings_mut() + .insert("experimental-features".into(), "ca-references".into()); + let mut action = CreateOrMergeNixConfig::plan(&test_file, nix_config).await?; + + action.try_execute().await?; + + let s = std::fs::read_to_string(&test_file)?; + assert!(s.contains("# Generated by")); + assert!(s.contains("ca-references")); + assert_eq!(s.matches("a = b").count(), 1); + assert!(NixConfig::parse_file(&test_file).is_ok()); + + action.try_revert().await?; + + assert!(!test_file.exists(), "File should have been deleted"); + + Ok(()) + } +} diff --git a/src/action/base/mod.rs b/src/action/base/mod.rs index 360239c..71a4c02 100644 --- a/src/action/base/mod.rs +++ b/src/action/base/mod.rs @@ -5,6 +5,7 @@ pub(crate) mod create_directory; pub(crate) mod create_file; pub(crate) mod create_group; pub(crate) mod create_or_insert_into_file; +pub(crate) mod create_or_merge_nix_config; pub(crate) mod create_user; pub(crate) mod delete_user; pub(crate) mod fetch_and_unpack_nix; @@ -17,6 +18,7 @@ pub use create_directory::CreateDirectory; pub use create_file::CreateFile; pub use create_group::CreateGroup; pub use create_or_insert_into_file::CreateOrInsertIntoFile; +pub use create_or_merge_nix_config::CreateOrMergeNixConfig; pub use create_user::CreateUser; pub use delete_user::DeleteUser; pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError}; diff --git a/src/action/common/place_nix_configuration.rs b/src/action/common/place_nix_configuration.rs index 2448f2f..5be4e4a 100644 --- a/src/action/common/place_nix_configuration.rs +++ b/src/action/common/place_nix_configuration.rs @@ -1,14 +1,15 @@ use tracing::{span, Span}; use url::Url; -use crate::action::base::create_or_insert_into_file::Position; -use crate::action::base::{CreateDirectory, CreateFile, CreateOrInsertIntoFile}; +use crate::action::base::create_or_merge_nix_config::CreateOrMergeNixConfigError; +use crate::action::base::{CreateDirectory, CreateOrMergeNixConfig}; use crate::action::{ Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction, }; use crate::parse_ssl_cert; use crate::settings::UrlOrPathOrString; -use std::path::{Path, PathBuf}; +use std::collections::hash_map::Entry; +use std::path::PathBuf; const NIX_CONF_FOLDER: &str = "/etc/nix"; const NIX_CONF: &str = "/etc/nix/nix.conf"; @@ -19,8 +20,7 @@ Place the `/etc/nix.conf` file #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct PlaceNixConfiguration { create_directory: StatefulAction, - create_or_insert_nix_conf: StatefulAction, - create_defaults_conf: StatefulAction, + create_or_merge_nix_config: StatefulAction, } impl PlaceNixConfiguration { @@ -32,10 +32,6 @@ impl PlaceNixConfiguration { extra_conf: Vec, force: bool, ) -> Result, ActionError> { - let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force) - .await - .map_err(Self::error)?; - let mut extra_conf_text = vec![]; for extra in extra_conf { let buf = match &extra { @@ -88,189 +84,65 @@ impl PlaceNixConfiguration { extra_conf_text.push(buf) } - let mut nix_conf_insert_settings = Vec::default(); - nix_conf_insert_settings.push("include ./nix-installer-defaults.conf".into()); - nix_conf_insert_settings.extend(extra_conf_text); - let nix_conf_insert_fragment = nix_conf_insert_settings.join("\n"); + let extra_conf = extra_conf_text.join("\n"); + let mut nix_config = nix_config_parser::NixConfig::parse_string(extra_conf, None) + .map_err(CreateOrMergeNixConfigError::ParseNixConfig) + .map_err(Self::error)?; + let settings = nix_config.settings_mut(); - let mut defaults_conf_settings = vec![ - ("build-users-group", nix_build_group_name), - ( - "experimental-features", - "nix-command flakes repl-flake".into(), - ), - ]; + settings.insert("build-users-group".to_string(), nix_build_group_name); + let experimental_features = ["nix-command", "flakes", "auto-allocate-uids"]; + match settings.entry("experimental-features".to_string()) { + Entry::Occupied(mut slot) => { + let slot_mut = slot.get_mut(); + for experimental_feature in experimental_features { + if !slot_mut.contains(experimental_feature) { + *slot_mut += " "; + *slot_mut += experimental_feature; + } + } + }, + Entry::Vacant(slot) => { + let _ = slot.insert(experimental_features.join(" ")); + }, + }; - defaults_conf_settings.push(("bash-prompt-prefix", "(nix:$name)\\040".into())); - defaults_conf_settings.push(("extra-nix-path", "nixpkgs=flake:nixpkgs".into())); - defaults_conf_settings.push(("max-jobs", "auto".to_string())); + // https://github.com/DeterminateSystems/nix-installer/issues/449#issuecomment-1551782281 + #[cfg(not(target_os = "macos"))] + settings.insert("auto-optimise-store".to_string(), "true".to_string()); + + settings.insert( + "bash-prompt-prefix".to_string(), + "(nix:$name)\\040".to_string(), + ); if let Some(ssl_cert_file) = ssl_cert_file { let ssl_cert_file_canonical = ssl_cert_file .canonicalize() .map_err(|e| Self::error(ActionErrorKind::Canonicalize(ssl_cert_file, e)))?; - defaults_conf_settings.push(( - "ssl-cert-file", + settings.insert( + "ssl-cert-file".to_string(), ssl_cert_file_canonical.display().to_string(), - )); - } - // https://github.com/DeterminateSystems/nix-installer/issues/449#issuecomment-1551782281 - #[cfg(not(target_os = "macos"))] - defaults_conf_settings.push(("auto-optimise-store", "true".into())); - let defaults_conf_insert_fragment = defaults_conf_settings - .iter() - .map(|(s, v)| format!("{s} = {v}")) - .collect::>() - .join("\n"); - - let nix_conf_insert_position = if Path::new(NIX_CONF).exists() { - let existing_nix_conf = tokio::fs::read_to_string(NIX_CONF) - .await - .map_err(|e| ActionErrorKind::Read(NIX_CONF.into(), e)) - .map_err(Self::error)?; - tracing::trace!("Found existing `/etc/nix/nix.conf`"); - - // Find the first line that isn't just `# ...` comments - let mut chosen_insert_after = None; - // Warn if there seems to be a setting which is a duplicate of one we set. - let mut existing_conf_settings = vec![]; - - for (index, line) in existing_nix_conf.lines().enumerate() { - let line = line.trim(); - if line.starts_with('#') { - continue; - } else { - chosen_insert_after = Some(Position::Before { - index, - expected_content: line.to_string(), - }); - } - - // We only scan one include of depth -- we should make this any depth, make sure to guard for loops - if line.starts_with("include") || line.starts_with("!include") { - let allow_not_existing = line.starts_with('!'); - // Need to read it in if it exists for settings - let path = line - .trim_start_matches("include") - .trim_start_matches("!include") - .trim(); - let path = Path::new(path); - let path = if path.is_relative() { - Path::new("/etc/nix/").join(path) - } else { - path.into() - }; - - tracing::trace!(path = %path.display(), "Reading included nix.conf"); - let existing_included_conf = match tokio::fs::read_to_string(&path).await { - Ok(v) => Some(v), - Err(_e) if allow_not_existing => None, - Err(e) => { - return Err(ActionErrorKind::Read(path, e)).map_err(Self::error)? - }, - }; - if let Some(existing_included_conf) = existing_included_conf { - let lines = existing_included_conf.lines(); - for line in lines { - let split = line.split_once('='); - if let Some((setting_name, setting_value)) = split { - let setting_name = setting_name.trim(); - let setting_value = setting_value.trim(); - existing_conf_settings - .push((setting_name.to_string(), setting_value.to_string())); - } - } - } - } else { - let split = line.split_once('='); - if let Some((setting_name, setting_value)) = split { - let setting_name = setting_name.trim(); - let setting_value = setting_value.trim(); - existing_conf_settings - .push((setting_name.to_string(), setting_value.to_string())); - } - } - } - tracing::trace!( - existing_conf_settings = existing_conf_settings - .iter() - .map(|(v, _)| v.to_string()) - .collect::>() - .join(","), - "Found existing config settings" ); + } + settings.insert( + "extra-nix-path".to_string(), + "nixpkgs=flake:nixpkgs".to_string(), + ); - // Some settings (eg `experimental-features`) we must be able to set it. - let required_settings = vec!["experimental-features"]; - for required_setting in required_settings { - if let Some((existing_setting, existing_value)) = existing_conf_settings - .iter() - .find(|(k, _v)| k == required_setting) - { - let required_setting = defaults_conf_settings - .iter() - .find(|(default_setting, _v)| default_setting.starts_with(existing_setting)) - .expect("Required setting was not planned -- please report this"); - if *existing_value != required_setting.1 { - return Err(ActionErrorKind::ConfigurationConflict { - setting: existing_setting.to_string(), - existing_value: existing_value.to_string(), - planned_value: required_setting.1.clone(), - }) - .map_err(Self::error); - } else { - tracing::trace!( - "Found existing setting `{} = {existing_value}` in config, continuing", - required_setting.0 - ) - } - } - } - - // Other settings, it's just a warning - for defaults_conf_setting in &defaults_conf_settings { - // We only set plain values so no need to be complicated. - for (existing_field, existing_value) in &existing_conf_settings { - if defaults_conf_setting.0.trim() == *existing_field - && defaults_conf_setting.1.trim() != existing_value.trim() - { - tracing::warn!("Found existing `/etc/nix/nix.conf` setting `{existing_field} = {existing_value}` which will override a default setting from the `nix-installer`, consider unsetting it. For settings like `experimental-features` you can use an `extra-*` prefix to append to the defaults") - } - } - } - - // If `None` then the file is likely just completely empty. - chosen_insert_after.unwrap_or(Position::Beginning) - } else { - tracing::trace!("Creating new `/etc/nix/nix.conf`"); - Position::Beginning - }; - - let create_or_insert_nix_conf = CreateOrInsertIntoFile::plan( - NIX_CONF, - None, - None, - 0o755, - nix_conf_insert_fragment + "\n", - nix_conf_insert_position, - ) - .await - .map_err(Self::error)?; - - let create_defaults_conf = CreateFile::plan( - PathBuf::from("/etc/nix/nix-installer-defaults.conf"), - None, - None, - 0o755, - defaults_conf_insert_fragment + "\n", - true, - ) - .await - .map_err(Self::error)?; + // Auto-allocate uids is broken on Mac. Tools like `whoami` don't work. + // e.g. https://github.com/NixOS/nix/issues/8444 + #[cfg(not(target_os = "macos"))] + settings.insert("auto-allocate-uids".to_string(), "true".to_string()); + let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force) + .await + .map_err(Self::error)?; + let create_or_merge_nix_config = CreateOrMergeNixConfig::plan(NIX_CONF, nix_config) + .await + .map_err(Self::error)?; Ok(Self { create_directory, - create_or_insert_nix_conf, - create_defaults_conf, + create_or_merge_nix_config, } .into()) } @@ -292,9 +164,8 @@ impl Action for PlaceNixConfiguration { fn execute_description(&self) -> Vec { let Self { + create_or_merge_nix_config, create_directory, - create_or_insert_nix_conf, - create_defaults_conf, } = self; let mut explanation = vec![ @@ -305,10 +176,7 @@ impl Action for PlaceNixConfiguration { if let Some(val) = create_directory.describe_execute().first() { explanation.push(val.description.clone()) } - for val in create_or_insert_nix_conf.describe_execute().iter() { - explanation.push(val.description.clone()) - } - for val in create_defaults_conf.describe_execute().iter() { + for val in create_or_merge_nix_config.describe_execute().iter() { explanation.push(val.description.clone()) } @@ -321,11 +189,7 @@ impl Action for PlaceNixConfiguration { .try_execute() .await .map_err(Self::error)?; - self.create_or_insert_nix_conf - .try_execute() - .await - .map_err(Self::error)?; - self.create_defaults_conf + self.create_or_merge_nix_config .try_execute() .await .map_err(Self::error)?; @@ -334,40 +198,19 @@ impl Action for PlaceNixConfiguration { } fn revert_description(&self) -> Vec { - let Self { - create_directory, - create_or_insert_nix_conf, - create_defaults_conf, - } = self; - - let mut explanation = vec![ - "This file is read by the Nix daemon to set its configuration options at runtime." - .to_string(), - ]; - - if let Some(val) = create_directory.describe_execute().first() { - explanation.push(val.description.clone()) - } - for val in create_or_insert_nix_conf.describe_execute().iter() { - explanation.push(val.description.clone()) - } - for val in create_defaults_conf.describe_execute().iter() { - explanation.push(val.description.clone()) - } - vec![ActionDescription::new( format!("Remove the Nix configuration in `{NIX_CONF}`"), - explanation, + vec![ + "This file is read by the Nix daemon to set its configuration options at runtime." + .to_string(), + ], )] } #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let mut errors = vec![]; - if let Err(err) = self.create_or_insert_nix_conf.try_revert().await { - errors.push(err); - } - if let Err(err) = self.create_defaults_conf.try_revert().await { + if let Err(err) = self.create_or_merge_nix_config.try_revert().await { errors.push(err); } if let Err(err) = self.create_directory.try_revert().await { diff --git a/src/action/mod.rs b/src/action/mod.rs index 0ec2c62..8856651 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -201,8 +201,6 @@ use tracing::Span; use crate::{error::HasExpectedErrors, settings::UrlOrPathError, CertificateError}; -use self::base::create_or_insert_into_file::Position; - /// An action which can be reverted or completed, with an action state /// /// This trait interacts with [`StatefulAction`] which does the [`ActionState`] manipulation and provides some tracing facilities. @@ -364,21 +362,6 @@ pub enum ActionErrorKind { /// A custom error #[error(transparent)] Custom(Box), - /// An error to do with the `nix.conf` configuration having a conflict - #[error("\ - A configuration conflict in `/etc/nix/nix.conf` exists.\n\ - The installer would set a default:\n\ - {indent}{setting} = {planned_value}\n\ - However, this setting is already set in the `nix.conf`:\n\ - {indent}{setting} = {existing_value}\n\ - \n\ - Consider unsetting the value. For lists like `experimental-features` you can append with `extra-experimental-features`.\ - ", indent = " ")] - ConfigurationConflict { - setting: String, - existing_value: String, - planned_value: String, - }, /// An error to do with certificates #[error(transparent)] Certificate(#[from] CertificateError), @@ -408,16 +391,6 @@ pub enum ActionErrorKind { "`{0}` exists with different content than planned, consider removing it with `rm {0}`" )] DifferentContent(std::path::PathBuf), - /// The path already exists with a different line content that expected - #[error( - "`{0}` exists with different line content than planned, {position} may have changed since read", - position = match .1 { - Position::Beginning => "the line at the start of the file".to_string(), - Position::End => "the line at the end of the file".to_string(), - Position::Before { index, .. } => format!("line {index}"), - } - )] - DifferentLineContent(std::path::PathBuf, Position), /// The file already exists #[error("`{0}` already exists, consider removing it with `rm {0}`")] FileExists(std::path::PathBuf), @@ -621,10 +594,8 @@ impl HasExpectedErrors for ActionErrorKind { match self { Self::PathUserMismatch(_, _, _) | Self::PathGroupMismatch(_, _, _) - | Self::PathModeMismatch(_, _, _) - | Self::ConfigurationConflict { .. } => Some(Box::new(self)), + | Self::PathModeMismatch(_, _, _) => Some(Box::new(self)), Self::SystemdMissing => Some(Box::new(self)), - Self::Child(child) => child.kind().expected(), _ => None, } } diff --git a/src/planner/mod.rs b/src/planner/mod.rs index 0f9d550..97e7250 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -442,7 +442,7 @@ impl HasExpectedErrors for PlannerError { fn expected<'a>(&'a self) -> Option> { match self { this @ PlannerError::UnsupportedArchitecture(_) => Some(Box::new(this)), - PlannerError::Action(action_error) => action_error.kind().expected(), + PlannerError::Action(_) => None, PlannerError::InstallSettings(_) => None, PlannerError::Plist(_) => None, PlannerError::Sysctl(_) => None, diff --git a/tests/fixtures/linux/linux.json b/tests/fixtures/linux/linux.json index fbddf38..c2a3184 100644 --- a/tests/fixtures/linux/linux.json +++ b/tests/fixtures/linux/linux.json @@ -8,7 +8,7 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": false, + "is_mountpoint": true, "force_prune_on_revert": true }, "state": "Uncompleted" @@ -27,6 +27,14 @@ }, "state": "Uncompleted" }, + "delete_users": [], + "create_group": { + "action": { + "name": "nixbld", + "gid": 30000 + }, + "state": "Uncompleted" + }, "create_nix_tree": { "action": { "create_directories": [ @@ -36,8 +44,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -47,8 +55,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -58,8 +66,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -69,8 +77,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -80,8 +88,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -91,8 +99,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -102,8 +110,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -113,8 +121,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -124,8 +132,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -135,8 +143,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -146,8 +154,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -157,8 +165,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -168,8 +176,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" } @@ -186,26 +194,6 @@ }, "state": "Uncompleted" }, - { - "action": { - "action": "create_users_and_group", - "nix_build_user_count": 0, - "nix_build_group_name": "nixbld", - "nix_build_group_id": 30000, - "nix_build_user_prefix": "nixbld", - "nix_build_user_id_base": 30000, - "create_group": { - "action": { - "name": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - "create_users": [], - "add_users_to_groups": [] - }, - "state": "Uncompleted" - }, { "action": { "action": "configure_nix", @@ -260,7 +248,7 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": false, + "is_mountpoint": true, "force_prune_on_revert": false }, "state": "Completed" @@ -356,30 +344,24 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": false, + "is_mountpoint": true, "force_prune_on_revert": false }, "state": "Uncompleted" }, - "create_or_insert_nix_conf": { + "create_or_merge_nix_config": { "action": { "path": "/etc/nix/nix.conf", - "user": null, - "group": null, - "mode": 493, - "buf": "include ./nix-installer-defaults.conf\n", - "position": "Beginning" - }, - "state": "Uncompleted" - }, - "create_defaults_conf": { - "action": { - "path": "/etc/nix/nix-installer-defaults.conf", - "user": null, - "group": null, - "mode": 493, - "buf": "build-users-group = nixbld\nexperimental-features = nix-command flakes auto-allocate-uids\nbash-prompt-prefix = (nix:$name)\\040\nextra-nix-path = nixpkgs=flake:nixpkgs\nmax-jobs = auto\nauto-optimise-store = true\nauto-allocate-uids = true\n", - "replace": true + "pending_nix_config": { + "settings": { + "experimental-features": "nix-command flakes auto-allocate-uids", + "build-users-group": "nixbld", + "auto-optimise-store": "true", + "bash-prompt-prefix": "(nix:$name)\\040", + "extra-nix-path": "nixpkgs=flake:nixpkgs", + "auto-allocate-uids": "true" + } + } }, "state": "Uncompleted" } @@ -405,7 +387,8 @@ "action": { "action": "configure_init_service", "init": "Systemd", - "start_daemon": true + "start_daemon": true, + "ssl_cert_file": null }, "state": "Uncompleted" }, @@ -423,8 +406,8 @@ "modify_profile": true, "nix_build_group_name": "nixbld", "nix_build_group_id": 30000, - "nix_build_user_prefix": "nixbld", "nix_build_user_count": 0, + "nix_build_user_prefix": "nixbld", "nix_build_user_id_base": 30000, "nix_package_url": { "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz" diff --git a/tests/fixtures/linux/steam-deck.json b/tests/fixtures/linux/steam-deck.json index 542cf14..9ec6afd 100644 --- a/tests/fixtures/linux/steam-deck.json +++ b/tests/fixtures/linux/steam-deck.json @@ -1,12 +1,6 @@ { "version": "0.12.1-unreleased", "actions": [ - { - "action": { - "action": "systemctl_daemon_reload" - }, - "state": "Uncompleted" - }, { "action": { "action": "create_directory", @@ -14,7 +8,7 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": false, + "is_mountpoint": true, "force_prune_on_revert": true }, "state": "Uncompleted" @@ -27,7 +21,7 @@ "group": null, "mode": 420, "buf": "[Unit]\nDescription=Create a `/nix` directory to be used for bind mounting\nPropagatesStopTo=nix-daemon.service\nPropagatesStopTo=nix.mount\nDefaultDependencies=no\nAfter=grub-recordfail.service\nAfter=steamos-finish-oobe-migration.service\n\n[Service]\nType=oneshot\nExecStart=steamos-readonly disable\nExecStart=mkdir -vp /nix\nExecStart=chmod -v 0755 /nix\nExecStart=chown -v root /nix\nExecStart=chgrp -v root /nix\nExecStart=steamos-readonly enable\nExecStop=steamos-readonly disable\nExecStop=rmdir /nix\nExecStop=steamos-readonly enable\nRemainAfterExit=true\n", - "replace": false + "force": false }, "state": "Uncompleted" }, @@ -38,8 +32,8 @@ "user": null, "group": null, "mode": 420, - "buf": "[Unit]\nDescription=Mount `/home/nix` on `/nix`\nPropagatesStopTo=nix-daemon.service\nPropagatesStopTo=nix-directory.service\nAfter=nix-directory.service\nRequires=nix-directory.service\nConditionPathIsDirectory=/nix\nDefaultDependencies=no\n\n[Mount]\nWhat=/home/nix\nWhere=/nix\nType=none\nDirectoryMode=0755\nOptions=bind\n\n[Install]\nRequiredBy=nix-daemon.service\nRequiredBy=nix-daemon.socket\n\n ", - "replace": false + "buf": "[Unit]\nDescription=Mount `/home/nix` on `/nix`\nPropagatesStopTo=nix-daemon.service\nPropagatesStopTo=nix-directory.service\nAfter=nix-directory.service\nRequires=nix-directory.service\nConditionPathIsDirectory=/nix\nDefaultDependencies=no\nRequiredBy=nix-daemon.service\nRequiredBy=nix-daemon.socket\n\n[Mount]\nWhat=/home/nix\nWhere=/nix\nType=none\nDirectoryMode=0755\nOptions=bind\n", + "force": false }, "state": "Uncompleted" }, @@ -50,8 +44,8 @@ "user": null, "group": null, "mode": 420, - "buf": "[Unit]\nDescription=Ensure Nix related units which are symlinked resolve\nAfter=nix.mount\nRequires=nix.mount\nDefaultDependencies=no\n\n[Service]\nType=oneshot\nRemainAfterExit=yes\nExecStart=/usr/bin/systemctl daemon-reload\nExecStart=/usr/bin/systemctl restart --no-block nix-daemon.socket\n\n[Install]\nWantedBy=sysinit.target\n", - "replace": false + "buf": "[Unit]\nDescription=Ensure Nix related units which are symlinked resolve\nAfter=nix.mount\nRequires=nix-directory.service\nRequires=nix.mount\nDefaultDependencies=no\n\n[Service]\nType=oneshot\nRemainAfterExit=yes\nExecStart=/usr/bin/systemctl daemon-reload\nExecStart=/usr/bin/systemctl restart --no-block nix-daemon.socket\n\n[Install]\nWantedBy=sysinit.target\n", + "force": false }, "state": "Uncompleted" }, @@ -77,6 +71,14 @@ }, "state": "Uncompleted" }, + "delete_users": [], + "create_group": { + "action": { + "name": "nixbld", + "gid": 30000 + }, + "state": "Uncompleted" + }, "create_nix_tree": { "action": { "create_directories": [ @@ -86,8 +88,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -97,8 +99,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -108,8 +110,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -119,8 +121,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -130,8 +132,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -141,8 +143,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -152,8 +154,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -163,8 +165,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -174,8 +176,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -185,8 +187,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -196,8 +198,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -207,8 +209,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -218,8 +220,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" } @@ -236,26 +238,6 @@ }, "state": "Uncompleted" }, - { - "action": { - "action": "create_users_and_group", - "nix_build_user_count": 0, - "nix_build_group_name": "nixbld", - "nix_build_group_id": 30000, - "nix_build_user_prefix": "nixbld", - "nix_build_user_id_base": 30000, - "create_group": { - "action": { - "name": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - "create_users": [], - "add_users_to_groups": [] - }, - "state": "Uncompleted" - }, { "action": { "action": "configure_nix", @@ -291,19 +273,7 @@ "/etc/zsh/zshrc" ] }, - "create_directories": [ - { - "action": { - "path": "/etc/zsh", - "user": null, - "group": null, - "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": false - }, - "state": "Uncompleted" - } - ], + "create_directories": [], "create_or_insert_into_files": [ { "action": { @@ -348,17 +318,6 @@ "position": "Beginning" }, "state": "Uncompleted" - }, - { - "action": { - "path": "/etc/zsh/zshrc", - "user": null, - "group": null, - "mode": 420, - "buf": "\n# Nix\nif [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then\n . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'\nfi\n# End Nix\n\n \n", - "position": "Beginning" - }, - "state": "Uncompleted" } ] }, @@ -372,30 +331,24 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": false, + "is_mountpoint": true, "force_prune_on_revert": false }, "state": "Uncompleted" }, - "create_or_insert_nix_conf": { + "create_or_merge_nix_config": { "action": { "path": "/etc/nix/nix.conf", - "user": null, - "group": null, - "mode": null, - "buf": "!include ./nix-installer-defaults.conf", - "position": "Beginning" - }, - "state": "Uncompleted" - }, - "create_defaults_conf": { - "action": { - "path": "/etc/nix/defaults.conf", - "user": null, - "group": null, - "mode": null, - "buf": "build-users-group = nixbld\nexperimental-features = nix-command flakes auto-allocate-uids\nbash-prompt-prefix = (nix:$name)\\040\nextra-nix-path = nixpkgs=flake:nixpkgs\nauto-optimise-store = true\nauto-allocate-uids = true", - "replace": true + "pending_nix_config": { + "settings": { + "auto-optimise-store": "true", + "bash-prompt-prefix": "(nix:$name)\\040", + "build-users-group": "nixbld", + "experimental-features": "nix-command flakes auto-allocate-uids", + "extra-nix-path": "nixpkgs=flake:nixpkgs", + "auto-allocate-uids": "true" + } + } }, "state": "Uncompleted" } @@ -409,7 +362,8 @@ "action": { "action": "configure_init_service", "init": "Systemd", - "start_daemon": true + "start_daemon": true, + "ssl_cert_file": null }, "state": "Uncompleted" }, @@ -427,12 +381,6 @@ "path": "/nix/temp-install-dir" }, "state": "Uncompleted" - }, - { - "action": { - "action": "systemctl_daemon_reload" - }, - "state": "Uncompleted" } ], "planner": { @@ -442,8 +390,8 @@ "modify_profile": true, "nix_build_group_name": "nixbld", "nix_build_group_id": 30000, - "nix_build_user_prefix": "nixbld", "nix_build_user_count": 0, + "nix_build_user_prefix": "nixbld", "nix_build_user_id_base": 30000, "nix_package_url": { "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz" @@ -467,4 +415,4 @@ "ssl_cert_file": null, "failure_chain": null } -} +} \ No newline at end of file diff --git a/tests/fixtures/macos/macos.json b/tests/fixtures/macos/macos.json index 25ecf7c..2456381 100644 --- a/tests/fixtures/macos/macos.json +++ b/tests/fixtures/macos/macos.json @@ -4,7 +4,7 @@ { "action": { "action": "create_apfs_volume", - "disk": "disk1", + "disk": "disk3", "name": "Nix Store", "case_sensitive": false, "encrypt": false, @@ -25,14 +25,14 @@ }, "unmount_volume": { "action": { - "disk": "disk1", + "disk": "disk3", "name": "Nix Store" }, "state": "Uncompleted" }, "create_volume": { "action": { - "disk": "disk1", + "disk": "disk3", "name": "Nix Store", "case_sensitive": false }, @@ -97,6 +97,14 @@ }, "state": "Uncompleted" }, + "delete_users_in_group": null, + "create_group": { + "action": { + "name": "nixbld", + "gid": 30000 + }, + "state": "Uncompleted" + }, "create_nix_tree": { "action": { "create_directories": [ @@ -106,8 +114,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -117,8 +125,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -128,8 +136,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -139,8 +147,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -150,8 +158,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -161,8 +169,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -172,8 +180,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -183,8 +191,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -194,8 +202,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -205,8 +213,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -216,8 +224,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -227,8 +235,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" }, @@ -238,8 +246,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": true + "is_mountpoint": true, + "force_prune_on_revert": false }, "state": "Uncompleted" } @@ -256,636 +264,6 @@ }, "state": "Uncompleted" }, - { - "action": { - "action": "create_users_and_group", - "nix_build_user_count": 32, - "nix_build_group_name": "nixbld", - "nix_build_group_id": 30000, - "nix_build_user_prefix": "_nixbld", - "nix_build_user_id_base": 300, - "create_group": { - "action": { - "name": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - "create_users": [ - { - "action": { - "name": "_nixbld1", - "uid": 301, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 1" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld2", - "uid": 302, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 2" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld3", - "uid": 303, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 3" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld4", - "uid": 304, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 4" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld5", - "uid": 305, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 5" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld6", - "uid": 306, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 6" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld7", - "uid": 307, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 7" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld8", - "uid": 308, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 8" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld9", - "uid": 309, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 9" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld10", - "uid": 310, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 10" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld11", - "uid": 311, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 11" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld12", - "uid": 312, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 12" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld13", - "uid": 313, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 13" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld14", - "uid": 314, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 14" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld15", - "uid": 315, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 15" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld16", - "uid": 316, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 16" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld17", - "uid": 317, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 17" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld18", - "uid": 318, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 18" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld19", - "uid": 319, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 19" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld20", - "uid": 320, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 20" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld21", - "uid": 321, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 21" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld22", - "uid": 322, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 22" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld23", - "uid": 323, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 23" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld24", - "uid": 324, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 24" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld25", - "uid": 325, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 25" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld26", - "uid": 326, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 26" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld27", - "uid": 327, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 27" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld28", - "uid": 328, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 28" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld29", - "uid": 329, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 29" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld30", - "uid": 330, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 30" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld31", - "uid": 331, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 31" - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld32", - "uid": 332, - "groupname": "nixbld", - "gid": 30000, - "comment": "Nix build user 32" - }, - "state": "Uncompleted" - } - ], - "add_users_to_groups": [ - { - "action": { - "name": "_nixbld1", - "uid": 301, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld2", - "uid": 302, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld3", - "uid": 303, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld4", - "uid": 304, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld5", - "uid": 305, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld6", - "uid": 306, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld7", - "uid": 307, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld8", - "uid": 308, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld9", - "uid": 309, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld10", - "uid": 310, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld11", - "uid": 311, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld12", - "uid": 312, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld13", - "uid": 313, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld14", - "uid": 314, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld15", - "uid": 315, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld16", - "uid": 316, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld17", - "uid": 317, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld18", - "uid": 318, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld19", - "uid": 319, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld20", - "uid": 320, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld21", - "uid": 321, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld22", - "uid": 322, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld23", - "uid": 323, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld24", - "uid": 324, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld25", - "uid": 325, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld26", - "uid": 326, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld27", - "uid": 327, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld28", - "uid": 328, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld29", - "uid": 329, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld30", - "uid": 330, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld31", - "uid": 331, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, - { - "action": { - "name": "_nixbld32", - "uid": 332, - "groupname": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - } - ] - }, - "state": "Uncompleted" - }, { "action": { "action": "set_tmutil_exclusions", @@ -942,30 +320,7 @@ "/etc/zsh/zshrc" ] }, - "create_directories": [ - { - "action": { - "path": "/etc/profile.d", - "user": null, - "group": null, - "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": false - }, - "state": "Uncompleted" - }, - { - "action": { - "path": "/etc/zsh", - "user": null, - "group": null, - "mode": 493, - "is_mountpoint": false, - "force_prune_on_revert": false - }, - "state": "Uncompleted" - } - ], + "create_directories": [], "create_or_insert_into_files": [ { "action": { @@ -978,17 +333,6 @@ }, "state": "Uncompleted" }, - { - "action": { - "path": "/etc/profile.d/nix.sh", - "user": null, - "group": null, - "mode": 420, - "buf": "\n# Nix\nif [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then\n . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'\nfi\n# End Nix\n\n \n", - "position": "Beginning" - }, - "state": "Uncompleted" - }, { "action": { "path": "/etc/bash.bashrc", @@ -1010,17 +354,6 @@ "position": "Beginning" }, "state": "Uncompleted" - }, - { - "action": { - "path": "/etc/zsh/zshrc", - "user": null, - "group": null, - "mode": 420, - "buf": "\n# Nix\nif [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then\n . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'\nfi\n# End Nix\n\n \n", - "position": "Beginning" - }, - "state": "Uncompleted" } ] }, @@ -1034,30 +367,24 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": false, + "is_mountpoint": true, "force_prune_on_revert": false }, "state": "Uncompleted" }, - "create_or_insert_nix_conf": { + "create_or_merge_nix_config": { "action": { "path": "/etc/nix/nix.conf", - "user": null, - "group": null, - "mode": 493, - "buf": "!include ./defaults.conf\n", - "position": "Beginning" - }, - "state": "Uncompleted" - }, - "create_defaults_conf": { - "action": { - "path": "/etc/nix/defaults.conf", - "user": null, - "group": null, - "mode": 493, - "buf": "build-users-group = nixbld\nexperimental-features = nix-command flakes auto-allocate-uids\nbash-prompt-prefix = (nix:$name)\\040\nextra-nix-path = nixpkgs=flake:nixpkgs\n", - "replace": true + "pending_nix_config": { + "settings": { + "extra-nix-path": "nixpkgs=flake:nixpkgs", + "auto-allocate-uids": "true", + "auto-optimise-store": "true", + "build-users-group": "nixbld", + "bash-prompt-prefix": "(nix:$name)\\040", + "experimental-features": "nix-command flakes auto-allocate-uids" + } + } }, "state": "Uncompleted" } @@ -1071,7 +398,8 @@ "action": { "action": "configure_init_service", "init": "Launchd", - "start_daemon": true + "start_daemon": true, + "ssl_cert_file": null }, "state": "Uncompleted" }, @@ -1089,8 +417,8 @@ "modify_profile": true, "nix_build_group_name": "nixbld", "nix_build_group_id": 30000, - "nix_build_user_prefix": "_nixbld", "nix_build_user_count": 32, + "nix_build_user_prefix": "_nixbld", "nix_build_user_id_base": 300, "nix_package_url": { "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz" @@ -1104,7 +432,7 @@ "encrypt": null, "case_sensitive": false, "volume_label": "Nix Store", - "root_disk": "disk1" + "root_disk": "disk3" }, "diagnostic_data": { "version": "0.12.1-unreleased", @@ -1112,7 +440,7 @@ "configured_settings": [], "os_name": "unknown", "os_version": "unknown", - "triple": "x86_64-apple-darwin", + "triple": "aarch64-apple-darwin", "is_ci": false, "endpoint": "https://install.determinate.systems/nix/diagnostic", "ssl_cert_file": null,