diff --git a/Cargo.lock b/Cargo.lock index 8f49d80..41dd7c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -932,16 +932,6 @@ 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.11.0" @@ -956,7 +946,6 @@ dependencies = [ "glob", "is_ci", "nix", - "nix-config-parser", "os-release", "owo-colors", "plist", diff --git a/Cargo.toml b/Cargo.toml index 157161b..5a6ba14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,6 @@ 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 18667ff..88183a3 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, - force: bool, + replace: bool, } impl CreateFile { @@ -38,7 +38,7 @@ impl CreateFile { group: impl Into>, mode: impl Into>, buf: String, - force: bool, + replace: bool, ) -> Result, ActionError> { let path = path.as_ref().to_path_buf(); let mode = mode.into(); @@ -50,7 +50,7 @@ impl CreateFile { group, mode, buf, - force, + replace, }; 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 { + if discovered_buf != this.buf && !this.replace { return Err(Self::error(ActionErrorKind::DifferentContent( this.path.clone(), ))); @@ -183,7 +183,8 @@ impl Action for CreateFile { group, mode, buf, - force: _, + // If `replace` was not set, and the file existed, the `plan` step would have errored + replace: _, } = self; if tracing::enabled!(tracing::Level::TRACE) { @@ -247,7 +248,7 @@ impl Action for CreateFile { group: _, mode: _, buf: _, - force: _, + replace: _, } = &self; vec![ActionDescription::new( @@ -264,7 +265,7 @@ impl Action for CreateFile { group: _, mode: _, buf: _, - force: _, + replace: _, } = 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 cf0e237..aa7ea7c 100644 --- a/src/action/base/create_or_insert_into_file.rs +++ b/src/action/base/create_or_insert_into_file.rs @@ -19,6 +19,10 @@ 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 @@ -140,6 +144,20 @@ 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 } @@ -221,33 +239,75 @@ impl Action for CreateOrInsertIntoFile { ActionErrorKind::Open(temp_file_path.clone(), e) }).map_err(Self::error)?; - 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::Copy(path.to_owned(), temp_file_path.to_owned(), 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()) - .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) + temp_file + .write_all(buf.as_bytes()) .await - .map_err(|e| { - ActionErrorKind::Copy(path.to_owned(), temp_file_path.to_owned(), e) - }) + .map_err(|e| ActionErrorKind::Write(temp_file_path.clone(), e)) .map_err(Self::error)?; - } - } + }, + Position::Beginning => { + temp_file + .write_all(buf.as_bytes()) + .await + .map_err(|e| ActionErrorKind::Write(temp_file_path.clone(), 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 deleted file mode 100644 index 250ac0b..0000000 --- a/src/action/base/create_or_merge_nix_config.rs +++ /dev/null @@ -1,716 +0,0 @@ -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 71a4c02..360239c 100644 --- a/src/action/base/mod.rs +++ b/src/action/base/mod.rs @@ -5,7 +5,6 @@ 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; @@ -18,7 +17,6 @@ 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 69502ac..5a74d24 100644 --- a/src/action/common/place_nix_configuration.rs +++ b/src/action/common/place_nix_configuration.rs @@ -1,12 +1,11 @@ use tracing::{span, Span}; -use crate::action::base::create_or_merge_nix_config::CreateOrMergeNixConfigError; -use crate::action::base::{CreateDirectory, CreateOrMergeNixConfig}; +use crate::action::base::create_or_insert_into_file::Position; +use crate::action::base::{CreateDirectory, CreateFile, CreateOrInsertIntoFile}; use crate::action::{ Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction, }; -use std::collections::hash_map::Entry; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; const NIX_CONF_FOLDER: &str = "/etc/nix"; const NIX_CONF: &str = "/etc/nix/nix.conf"; @@ -17,7 +16,8 @@ Place the `/etc/nix.conf` file #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct PlaceNixConfiguration { create_directory: StatefulAction, - create_or_merge_nix_config: StatefulAction, + create_or_insert_nix_conf: StatefulAction, + create_defaults_conf: StatefulAction, } impl PlaceNixConfiguration { @@ -28,65 +28,198 @@ impl PlaceNixConfiguration { extra_conf: Vec, force: bool, ) -> Result, ActionError> { - let extra_conf = extra_conf.join("\n"); - let mut nix_config = nix_config_parser::NixConfig::parse_string(extra_conf, None) - .map_err(CreateOrMergeNixConfigError::ParseNixConfig) + let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force) + .await .map_err(Self::error)?; - let settings = nix_config.settings_mut(); - 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(" ")); - }, - }; + 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); + let nix_conf_insert_fragment = nix_conf_insert_settings.join("\n"); - // 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()); + let mut defaults_conf_settings = vec![ + ("build-users-group", nix_build_group_name), + ( + "experimental-features", + "nix-command flakes auto-allocate-uids".into(), + ), + ]; - settings.insert( - "bash-prompt-prefix".to_string(), - "(nix:$name)\\040".to_string(), - ); + defaults_conf_settings.push(("bash-prompt-prefix", "(nix:$name)\\040".into())); + defaults_conf_settings.push(("extra-nix-path", "nixpkgs=flake:nixpkgs".into())); 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)))?; - settings.insert( - "ssl-cert-file".to_string(), + defaults_conf_settings.push(( + "ssl-cert-file", ssl_cert_file_canonical.display().to_string(), - ); + )); } - settings.insert( - "extra-nix-path".to_string(), - "nixpkgs=flake:nixpkgs".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())); // 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()); + defaults_conf_settings.push(("auto-allocate-uids", "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" + ); + + // Some settings (eg `experimental-features`) we must be able to set it. + let mut required_settings = vec!["experimental-features"]; + #[cfg(not(target_os = "macos"))] + required_settings.push("auto-allocate-uids"); + 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)?; - 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_merge_nix_config, + create_or_insert_nix_conf, + create_defaults_conf, } .into()) } @@ -108,8 +241,9 @@ 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![ @@ -120,7 +254,10 @@ impl Action for PlaceNixConfiguration { if let Some(val) = create_directory.describe_execute().first() { explanation.push(val.description.clone()) } - for val in create_or_merge_nix_config.describe_execute().iter() { + 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()) } @@ -133,7 +270,11 @@ impl Action for PlaceNixConfiguration { .try_execute() .await .map_err(Self::error)?; - self.create_or_merge_nix_config + self.create_or_insert_nix_conf + .try_execute() + .await + .map_err(Self::error)?; + self.create_defaults_conf .try_execute() .await .map_err(Self::error)?; @@ -142,19 +283,40 @@ 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}`"), - vec![ - "This file is read by the Nix daemon to set its configuration options at runtime." - .to_string(), - ], + explanation, )] } #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let mut errors = vec![]; - if let Err(err) = self.create_or_merge_nix_config.try_revert().await { + 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 { 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 d860391..36b1f81 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -200,6 +200,8 @@ use tracing::Span; use crate::{error::HasExpectedErrors, 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. @@ -361,6 +363,21 @@ 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), @@ -390,6 +407,16 @@ 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), @@ -583,8 +610,10 @@ impl HasExpectedErrors for ActionErrorKind { match self { Self::PathUserMismatch(_, _, _) | Self::PathGroupMismatch(_, _, _) - | Self::PathModeMismatch(_, _, _) => Some(Box::new(self)), + | Self::PathModeMismatch(_, _, _) + | Self::ConfigurationConflict { .. } => 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 a28d0ca..0c54860 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -441,7 +441,7 @@ impl HasExpectedErrors for PlannerError { fn expected<'a>(&'a self) -> Option> { match self { this @ PlannerError::UnsupportedArchitecture(_) => Some(Box::new(this)), - PlannerError::Action(_) => None, + PlannerError::Action(action_error) => action_error.kind().expected(), PlannerError::InstallSettings(_) => None, PlannerError::Plist(_) => None, PlannerError::Sysctl(_) => None, diff --git a/tests/fixtures/linux/linux.json b/tests/fixtures/linux/linux.json index 4efa2e1..acf7f34 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": true, + "is_mountpoint": false, "force_prune_on_revert": true }, "state": "Uncompleted" @@ -18,21 +18,13 @@ "action": "provision_nix", "fetch_nix": { "action": { - "url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz", + "url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz", "dest": "/nix/temp-install-dir", "proxy": null, "ssl_cert_file": null }, "state": "Uncompleted" }, - "delete_users": [], - "create_group": { - "action": { - "name": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, "create_nix_tree": { "action": { "create_directories": [ @@ -42,8 +34,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -53,8 +45,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -64,8 +56,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -75,8 +67,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -86,8 +78,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -97,8 +89,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -108,8 +100,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -119,8 +111,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -130,8 +122,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -141,8 +133,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -152,8 +144,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -163,8 +155,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -174,8 +166,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" } @@ -192,6 +184,26 @@ }, "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", @@ -229,13 +241,24 @@ ] }, "create_directories": [ + { + "action": { + "path": "/etc/zsh", + "user": null, + "group": null, + "mode": 493, + "is_mountpoint": false, + "force_prune_on_revert": false + }, + "state": "Uncompleted" + }, { "action": { "path": "/usr/share/fish/vendor_conf.d", "user": null, "group": null, "mode": 493, - "is_mountpoint": true, + "is_mountpoint": false, "force_prune_on_revert": false }, "state": "Completed" @@ -286,6 +309,17 @@ }, "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" + }, { "action": { "path": "/usr/share/fish/vendor_conf.d/nix.fish", @@ -309,24 +343,30 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": true, + "is_mountpoint": false, "force_prune_on_revert": false }, "state": "Uncompleted" }, - "create_or_merge_nix_config": { + "create_or_insert_nix_conf": { "action": { "path": "/etc/nix/nix.conf", - "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" - } - } + "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 }, "state": "Uncompleted" } @@ -336,12 +376,23 @@ }, "state": "Uncompleted" }, + { + "action": { + "action": "create_directory", + "path": "/etc/tmpfiles.d", + "user": null, + "group": null, + "mode": 493, + "is_mountpoint": false, + "force_prune_on_revert": false + }, + "state": "Completed" + }, { "action": { "action": "configure_init_service", "init": "Systemd", - "start_daemon": true, - "ssl_cert_file": null + "start_daemon": true }, "state": "Uncompleted" }, @@ -359,10 +410,10 @@ "modify_profile": true, "nix_build_group_name": "nixbld", "nix_build_group_id": 30000, - "nix_build_user_count": 0, "nix_build_user_prefix": "nixbld", + "nix_build_user_count": 0, "nix_build_user_id_base": 30000, - "nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz", + "nix_package_url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz", "proxy": null, "ssl_cert_file": null, "extra_conf": [], @@ -386,4 +437,4 @@ "ssl_cert_file": null, "failure_chain": null } -} \ No newline at end of file +} diff --git a/tests/fixtures/linux/steam-deck.json b/tests/fixtures/linux/steam-deck.json index 26597dd..fd99201 100644 --- a/tests/fixtures/linux/steam-deck.json +++ b/tests/fixtures/linux/steam-deck.json @@ -1,6 +1,12 @@ { "version": "0.11.0", "actions": [ + { + "action": { + "action": "systemctl_daemon_reload" + }, + "state": "Uncompleted" + }, { "action": { "action": "create_directory", @@ -8,7 +14,7 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": true, + "is_mountpoint": false, "force_prune_on_revert": true }, "state": "Uncompleted" @@ -21,7 +27,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", - "force": false + "replace": false }, "state": "Uncompleted" }, @@ -32,8 +38,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\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 + "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 }, "state": "Uncompleted" }, @@ -44,8 +50,8 @@ "user": null, "group": null, "mode": 420, - "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 + "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 }, "state": "Uncompleted" }, @@ -62,21 +68,13 @@ "action": "provision_nix", "fetch_nix": { "action": { - "url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz", + "url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz", "dest": "/nix/temp-install-dir", "proxy": null, "ssl_cert_file": null }, "state": "Uncompleted" }, - "delete_users": [], - "create_group": { - "action": { - "name": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, "create_nix_tree": { "action": { "create_directories": [ @@ -86,8 +84,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -97,8 +95,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -108,8 +106,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -119,8 +117,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -130,8 +128,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -141,8 +139,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -152,8 +150,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -163,8 +161,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -174,8 +172,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -185,8 +183,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -196,8 +194,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -207,8 +205,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -218,8 +216,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" } @@ -236,6 +234,26 @@ }, "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", @@ -271,7 +289,19 @@ "/etc/zsh/zshrc" ] }, - "create_directories": [], + "create_directories": [ + { + "action": { + "path": "/etc/zsh", + "user": null, + "group": null, + "mode": 493, + "is_mountpoint": false, + "force_prune_on_revert": false + }, + "state": "Uncompleted" + } + ], "create_or_insert_into_files": [ { "action": { @@ -316,6 +346,17 @@ "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" } ] }, @@ -329,24 +370,30 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": true, + "is_mountpoint": false, "force_prune_on_revert": false }, "state": "Uncompleted" }, - "create_or_merge_nix_config": { + "create_or_insert_nix_conf": { "action": { "path": "/etc/nix/nix.conf", - "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" - } - } + "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 }, "state": "Uncompleted" } @@ -360,8 +407,7 @@ "action": { "action": "configure_init_service", "init": "Systemd", - "start_daemon": true, - "ssl_cert_file": null + "start_daemon": true }, "state": "Uncompleted" }, @@ -379,6 +425,12 @@ "path": "/nix/temp-install-dir" }, "state": "Uncompleted" + }, + { + "action": { + "action": "systemctl_daemon_reload" + }, + "state": "Uncompleted" } ], "planner": { @@ -388,10 +440,10 @@ "modify_profile": true, "nix_build_group_name": "nixbld", "nix_build_group_id": 30000, - "nix_build_user_count": 0, "nix_build_user_prefix": "nixbld", + "nix_build_user_count": 0, "nix_build_user_id_base": 30000, - "nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz", + "nix_package_url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz", "proxy": null, "ssl_cert_file": null, "extra_conf": [], @@ -411,4 +463,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 5823902..3b8468c 100644 --- a/tests/fixtures/macos/macos.json +++ b/tests/fixtures/macos/macos.json @@ -4,7 +4,7 @@ { "action": { "action": "create_apfs_volume", - "disk": "disk3", + "disk": "disk1", "name": "Nix Store", "case_sensitive": false, "encrypt": false, @@ -25,14 +25,14 @@ }, "unmount_volume": { "action": { - "disk": "disk3", + "disk": "disk1", "name": "Nix Store" }, "state": "Uncompleted" }, "create_volume": { "action": { - "disk": "disk3", + "disk": "disk1", "name": "Nix Store", "case_sensitive": false }, @@ -88,21 +88,13 @@ "action": "provision_nix", "fetch_nix": { "action": { - "url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-aarch64-darwin.tar.xz", + "url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz", "dest": "/nix/temp-install-dir", "proxy": null, "ssl_cert_file": null }, "state": "Uncompleted" }, - "delete_users_in_group": null, - "create_group": { - "action": { - "name": "nixbld", - "gid": 30000 - }, - "state": "Uncompleted" - }, "create_nix_tree": { "action": { "create_directories": [ @@ -112,8 +104,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -123,8 +115,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -134,8 +126,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -145,8 +137,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -156,8 +148,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -167,8 +159,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -178,8 +170,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -189,8 +181,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -200,8 +192,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -211,8 +203,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -222,8 +214,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -233,8 +225,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" }, @@ -244,8 +236,8 @@ "user": "root", "group": null, "mode": 493, - "is_mountpoint": true, - "force_prune_on_revert": false + "is_mountpoint": false, + "force_prune_on_revert": true }, "state": "Uncompleted" } @@ -262,6 +254,636 @@ }, "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", @@ -318,7 +940,30 @@ "/etc/zsh/zshrc" ] }, - "create_directories": [], + "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_or_insert_into_files": [ { "action": { @@ -331,6 +976,17 @@ }, "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", @@ -352,6 +1008,17 @@ "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" } ] }, @@ -365,24 +1032,30 @@ "user": null, "group": null, "mode": 493, - "is_mountpoint": true, + "is_mountpoint": false, "force_prune_on_revert": false }, "state": "Uncompleted" }, - "create_or_merge_nix_config": { + "create_or_insert_nix_conf": { "action": { "path": "/etc/nix/nix.conf", - "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" - } - } + "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 }, "state": "Uncompleted" } @@ -396,8 +1069,7 @@ "action": { "action": "configure_init_service", "init": "Launchd", - "start_daemon": true, - "ssl_cert_file": null + "start_daemon": true }, "state": "Uncompleted" }, @@ -415,10 +1087,10 @@ "modify_profile": true, "nix_build_group_name": "nixbld", "nix_build_group_id": 30000, - "nix_build_user_count": 32, "nix_build_user_prefix": "_nixbld", + "nix_build_user_count": 32, "nix_build_user_id_base": 300, - "nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-aarch64-darwin.tar.xz", + "nix_package_url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz", "proxy": null, "ssl_cert_file": null, "extra_conf": [], @@ -428,7 +1100,7 @@ "encrypt": null, "case_sensitive": false, "volume_label": "Nix Store", - "root_disk": "disk3" + "root_disk": "disk1" }, "diagnostic_data": { "version": "0.11.0", @@ -436,7 +1108,7 @@ "configured_settings": [], "os_name": "unknown", "os_version": "unknown", - "triple": "aarch64-apple-darwin", + "triple": "x86_64-apple-darwin", "is_ci": false, "endpoint": "https://install.determinate.systems/nix/diagnostic", "ssl_cert_file": null,