Make nix.conf changes deterministic (#620)

* Make nix.conf changes deterministic

* Add mac fixture

* Warn/error if user settings don't match our needs

* Repair mac fixture

* fmt

* clipster

* Tidy up some feedback

* fmt

* resolve some nits
This commit is contained in:
Ana Hobden 2023-09-13 10:02:05 -07:00 committed by GitHub
parent 0cd1d4bb03
commit 05571a4990
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1286 additions and 989 deletions

11
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -27,7 +27,7 @@ pub struct CreateFile {
group: Option<String>,
mode: Option<u32>,
buf: String,
force: bool,
replace: bool,
}
impl CreateFile {
@ -38,7 +38,7 @@ impl CreateFile {
group: impl Into<Option<String>>,
mode: impl Into<Option<u32>>,
buf: String,
force: bool,
replace: bool,
) -> Result<StatefulAction<Self>, 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() {

View file

@ -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::<Vec<_>>();
// 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(

View file

@ -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::<Vec<_>>()
.join(", "))]
UnmergeableConfig(Vec<String>, std::path::PathBuf),
}
impl From<CreateOrMergeNixConfigError> 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<Path>,
pending_nix_config: NixConfig,
) -> Result<StatefulAction<Self>, 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::<Vec<_>>();
let existing_conf_value = existing_conf_value.split(' ').collect::<Vec<_>>();
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::<Vec<_>>()
.join(","),
);
}
span
}
fn execute_description(&self) -> Vec<ActionDescription> {
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::<Vec<_>>()
.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::<Vec<_>>()
.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::<u32>()));
}
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<String>>,
Vec<String>,
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<ActionDescription> {
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::<CreateOrMergeNixConfigError>() {
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(())
}
}

View file

@ -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};

View file

@ -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<CreateDirectory>,
create_or_merge_nix_config: StatefulAction<CreateOrMergeNixConfig>,
create_or_insert_nix_conf: StatefulAction<CreateOrInsertIntoFile>,
create_defaults_conf: StatefulAction<CreateFile>,
}
impl PlaceNixConfiguration {
@ -28,65 +28,198 @@ impl PlaceNixConfiguration {
extra_conf: Vec<String>,
force: bool,
) -> Result<StatefulAction<Self>, 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::<Vec<_>>()
.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::<Vec<_>>()
.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<ActionDescription> {
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<ActionDescription> {
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 {

View file

@ -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<dyn std::error::Error + Send + Sync>),
/// 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,
}
}

View file

@ -441,7 +441,7 @@ impl HasExpectedErrors for PlannerError {
fn expected<'a>(&'a self) -> Option<Box<dyn std::error::Error + 'a>> {
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,

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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,