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", "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]] [[package]]
name = "nix-installer" name = "nix-installer"
version = "0.11.0" version = "0.11.0"
@ -956,7 +946,6 @@ dependencies = [
"glob", "glob",
"is_ci", "is_ci",
"nix", "nix",
"nix-config-parser",
"os-release", "os-release",
"owo-colors", "owo-colors",
"plist", "plist",

View file

@ -56,7 +56,6 @@ uuid = { version = "1.2.2", features = ["serde"] }
os-release = { version = "0.1.0", default-features = false } os-release = { version = "0.1.0", default-features = false }
is_ci = { version = "1.1.1", default-features = false, optional = true } is_ci = { version = "1.1.1", default-features = false, optional = true }
strum = { version = "0.25.0", features = ["derive"] } strum = { version = "0.25.0", features = ["derive"] }
nix-config-parser = { version = "0.1.2", features = ["serde"] }
which = "4.4.0" which = "4.4.0"
sysctl = "0.5.4" sysctl = "0.5.4"
walkdir = "2.3.3" walkdir = "2.3.3"

View file

@ -27,7 +27,7 @@ pub struct CreateFile {
group: Option<String>, group: Option<String>,
mode: Option<u32>, mode: Option<u32>,
buf: String, buf: String,
force: bool, replace: bool,
} }
impl CreateFile { impl CreateFile {
@ -38,7 +38,7 @@ impl CreateFile {
group: impl Into<Option<String>>, group: impl Into<Option<String>>,
mode: impl Into<Option<u32>>, mode: impl Into<Option<u32>>,
buf: String, buf: String,
force: bool, replace: bool,
) -> Result<StatefulAction<Self>, ActionError> { ) -> Result<StatefulAction<Self>, ActionError> {
let path = path.as_ref().to_path_buf(); let path = path.as_ref().to_path_buf();
let mode = mode.into(); let mode = mode.into();
@ -50,7 +50,7 @@ impl CreateFile {
group, group,
mode, mode,
buf, buf,
force, replace,
}; };
if this.path.exists() { if this.path.exists() {
@ -128,7 +128,7 @@ impl CreateFile {
.map_err(|e| ActionErrorKind::Read(this.path.clone(), e)) .map_err(|e| ActionErrorKind::Read(this.path.clone(), e))
.map_err(Self::error)?; .map_err(Self::error)?;
if discovered_buf != this.buf { if discovered_buf != this.buf && !this.replace {
return Err(Self::error(ActionErrorKind::DifferentContent( return Err(Self::error(ActionErrorKind::DifferentContent(
this.path.clone(), this.path.clone(),
))); )));
@ -183,7 +183,8 @@ impl Action for CreateFile {
group, group,
mode, mode,
buf, buf,
force: _, // If `replace` was not set, and the file existed, the `plan` step would have errored
replace: _,
} = self; } = self;
if tracing::enabled!(tracing::Level::TRACE) { if tracing::enabled!(tracing::Level::TRACE) {
@ -247,7 +248,7 @@ impl Action for CreateFile {
group: _, group: _,
mode: _, mode: _,
buf: _, buf: _,
force: _, replace: _,
} = &self; } = &self;
vec![ActionDescription::new( vec![ActionDescription::new(
@ -264,7 +265,7 @@ impl Action for CreateFile {
group: _, group: _,
mode: _, mode: _,
buf: _, buf: _,
force: _, replace: _,
} = self; } = self;
// The user already deleted it // The user already deleted it
if !path.exists() { if !path.exists() {

View file

@ -19,6 +19,10 @@ use tracing::{span, Span};
pub enum Position { pub enum Position {
Beginning, Beginning,
End, End,
Before {
index: usize,
expected_content: String,
},
} }
/** Create a file at the given location with the provided `buf` as /** Create a file at the given location with the provided `buf` as
@ -140,6 +144,20 @@ impl CreateOrInsertIntoFile {
return Ok(StatefulAction::completed(this)); 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 // If not, we can't skip this, so we still do it
} }
@ -221,7 +239,8 @@ impl Action for CreateOrInsertIntoFile {
ActionErrorKind::Open(temp_file_path.clone(), e) ActionErrorKind::Open(temp_file_path.clone(), e)
}).map_err(Self::error)?; }).map_err(Self::error)?;
if *position == Position::End { match position {
Position::End => {
if let Some(ref mut orig_file) = orig_file { if let Some(ref mut orig_file) = orig_file {
tokio::io::copy(orig_file, &mut temp_file) tokio::io::copy(orig_file, &mut temp_file)
.await .await
@ -230,15 +249,20 @@ impl Action for CreateOrInsertIntoFile {
}) })
.map_err(Self::error)?; .map_err(Self::error)?;
} }
}
temp_file temp_file
.write_all(buf.as_bytes()) .write_all(buf.as_bytes())
.await .await
.map_err(|e| ActionErrorKind::Write(temp_file_path.clone(), e)) .map_err(|e| ActionErrorKind::Write(temp_file_path.clone(), e))
.map_err(Self::error)?; .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 *position == Position::Beginning {
if let Some(ref mut orig_file) = orig_file { if let Some(ref mut orig_file) = orig_file {
tokio::io::copy(orig_file, &mut temp_file) tokio::io::copy(orig_file, &mut temp_file)
.await .await
@ -247,7 +271,43 @@ impl Action for CreateOrInsertIntoFile {
}) })
.map_err(Self::error)?; .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 { let gid = if let Some(group) = group {
Some( 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_file;
pub(crate) mod create_group; pub(crate) mod create_group;
pub(crate) mod create_or_insert_into_file; pub(crate) mod create_or_insert_into_file;
pub(crate) mod create_or_merge_nix_config;
pub(crate) mod create_user; pub(crate) mod create_user;
pub(crate) mod delete_user; pub(crate) mod delete_user;
pub(crate) mod fetch_and_unpack_nix; pub(crate) mod fetch_and_unpack_nix;
@ -18,7 +17,6 @@ pub use create_directory::CreateDirectory;
pub use create_file::CreateFile; pub use create_file::CreateFile;
pub use create_group::CreateGroup; pub use create_group::CreateGroup;
pub use create_or_insert_into_file::CreateOrInsertIntoFile; pub use create_or_insert_into_file::CreateOrInsertIntoFile;
pub use create_or_merge_nix_config::CreateOrMergeNixConfig;
pub use create_user::CreateUser; pub use create_user::CreateUser;
pub use delete_user::DeleteUser; pub use delete_user::DeleteUser;
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError}; pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};

View file

@ -1,12 +1,11 @@
use tracing::{span, Span}; use tracing::{span, Span};
use crate::action::base::create_or_merge_nix_config::CreateOrMergeNixConfigError; use crate::action::base::create_or_insert_into_file::Position;
use crate::action::base::{CreateDirectory, CreateOrMergeNixConfig}; use crate::action::base::{CreateDirectory, CreateFile, CreateOrInsertIntoFile};
use crate::action::{ use crate::action::{
Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction, Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction,
}; };
use std::collections::hash_map::Entry; use std::path::{Path, PathBuf};
use std::path::PathBuf;
const NIX_CONF_FOLDER: &str = "/etc/nix"; const NIX_CONF_FOLDER: &str = "/etc/nix";
const NIX_CONF: &str = "/etc/nix/nix.conf"; 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)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct PlaceNixConfiguration { pub struct PlaceNixConfiguration {
create_directory: StatefulAction<CreateDirectory>, 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 { impl PlaceNixConfiguration {
@ -28,65 +28,198 @@ impl PlaceNixConfiguration {
extra_conf: Vec<String>, extra_conf: Vec<String>,
force: bool, force: bool,
) -> Result<StatefulAction<Self>, ActionError> { ) -> Result<StatefulAction<Self>, ActionError> {
let extra_conf = extra_conf.join("\n"); let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force)
let mut nix_config = nix_config_parser::NixConfig::parse_string(extra_conf, None) .await
.map_err(CreateOrMergeNixConfigError::ParseNixConfig)
.map_err(Self::error)?; .map_err(Self::error)?;
let settings = nix_config.settings_mut();
settings.insert("build-users-group".to_string(), nix_build_group_name); let mut nix_conf_insert_settings = Vec::default();
let experimental_features = ["nix-command", "flakes", "auto-allocate-uids"]; nix_conf_insert_settings.push("include ./nix-installer-defaults.conf".into());
match settings.entry("experimental-features".to_string()) { nix_conf_insert_settings.extend(extra_conf);
Entry::Occupied(mut slot) => { let nix_conf_insert_fragment = nix_conf_insert_settings.join("\n");
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(" "));
},
};
// https://github.com/DeterminateSystems/nix-installer/issues/449#issuecomment-1551782281 let mut defaults_conf_settings = vec![
#[cfg(not(target_os = "macos"))] ("build-users-group", nix_build_group_name),
settings.insert("auto-optimise-store".to_string(), "true".to_string()); (
"experimental-features",
"nix-command flakes auto-allocate-uids".into(),
),
];
settings.insert( defaults_conf_settings.push(("bash-prompt-prefix", "(nix:$name)\\040".into()));
"bash-prompt-prefix".to_string(), defaults_conf_settings.push(("extra-nix-path", "nixpkgs=flake:nixpkgs".into()));
"(nix:$name)\\040".to_string(),
);
if let Some(ssl_cert_file) = ssl_cert_file { if let Some(ssl_cert_file) = ssl_cert_file {
let ssl_cert_file_canonical = ssl_cert_file let ssl_cert_file_canonical = ssl_cert_file
.canonicalize() .canonicalize()
.map_err(|e| Self::error(ActionErrorKind::Canonicalize(ssl_cert_file, e)))?; .map_err(|e| Self::error(ActionErrorKind::Canonicalize(ssl_cert_file, e)))?;
settings.insert( defaults_conf_settings.push((
"ssl-cert-file".to_string(), "ssl-cert-file",
ssl_cert_file_canonical.display().to_string(), ssl_cert_file_canonical.display().to_string(),
); ));
} }
settings.insert( // https://github.com/DeterminateSystems/nix-installer/issues/449#issuecomment-1551782281
"extra-nix-path".to_string(), #[cfg(not(target_os = "macos"))]
"nixpkgs=flake:nixpkgs".to_string(), defaults_conf_settings.push(("auto-optimise-store", "true".into()));
);
// Auto-allocate uids is broken on Mac. Tools like `whoami` don't work. // Auto-allocate uids is broken on Mac. Tools like `whoami` don't work.
// e.g. https://github.com/NixOS/nix/issues/8444 // e.g. https://github.com/NixOS/nix/issues/8444
#[cfg(not(target_os = "macos"))] #[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 create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force) 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 .await
.map_err(Self::error)?; .map_err(Self::error)?;
let create_or_merge_nix_config = CreateOrMergeNixConfig::plan(NIX_CONF, nix_config)
let create_defaults_conf = CreateFile::plan(
PathBuf::from("/etc/nix/nix-installer-defaults.conf"),
None,
None,
0o755,
defaults_conf_insert_fragment + "\n",
true,
)
.await .await
.map_err(Self::error)?; .map_err(Self::error)?;
Ok(Self { Ok(Self {
create_directory, create_directory,
create_or_merge_nix_config, create_or_insert_nix_conf,
create_defaults_conf,
} }
.into()) .into())
} }
@ -108,8 +241,9 @@ impl Action for PlaceNixConfiguration {
fn execute_description(&self) -> Vec<ActionDescription> { fn execute_description(&self) -> Vec<ActionDescription> {
let Self { let Self {
create_or_merge_nix_config,
create_directory, create_directory,
create_or_insert_nix_conf,
create_defaults_conf,
} = self; } = self;
let mut explanation = vec![ let mut explanation = vec![
@ -120,7 +254,10 @@ impl Action for PlaceNixConfiguration {
if let Some(val) = create_directory.describe_execute().first() { if let Some(val) = create_directory.describe_execute().first() {
explanation.push(val.description.clone()) 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()) explanation.push(val.description.clone())
} }
@ -133,7 +270,11 @@ impl Action for PlaceNixConfiguration {
.try_execute() .try_execute()
.await .await
.map_err(Self::error)?; .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() .try_execute()
.await .await
.map_err(Self::error)?; .map_err(Self::error)?;
@ -142,19 +283,40 @@ impl Action for PlaceNixConfiguration {
} }
fn revert_description(&self) -> Vec<ActionDescription> { fn revert_description(&self) -> Vec<ActionDescription> {
vec![ActionDescription::new( let Self {
format!("Remove the Nix configuration in `{NIX_CONF}`"), create_directory,
vec![ 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." "This file is read by the Nix daemon to set its configuration options at runtime."
.to_string(), .to_string(),
], ];
if let Some(val) = create_directory.describe_execute().first() {
explanation.push(val.description.clone())
}
for val in create_or_insert_nix_conf.describe_execute().iter() {
explanation.push(val.description.clone())
}
for val in create_defaults_conf.describe_execute().iter() {
explanation.push(val.description.clone())
}
vec![ActionDescription::new(
format!("Remove the Nix configuration in `{NIX_CONF}`"),
explanation,
)] )]
} }
#[tracing::instrument(level = "debug", skip_all)] #[tracing::instrument(level = "debug", skip_all)]
async fn revert(&mut self) -> Result<(), ActionError> { async fn revert(&mut self) -> Result<(), ActionError> {
let mut errors = vec![]; 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); errors.push(err);
} }
if let Err(err) = self.create_directory.try_revert().await { 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 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 /// 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. /// 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 /// A custom error
#[error(transparent)] #[error(transparent)]
Custom(Box<dyn std::error::Error + Send + Sync>), 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 /// An error to do with certificates
#[error(transparent)] #[error(transparent)]
Certificate(#[from] CertificateError), Certificate(#[from] CertificateError),
@ -390,6 +407,16 @@ pub enum ActionErrorKind {
"`{0}` exists with different content than planned, consider removing it with `rm {0}`" "`{0}` exists with different content than planned, consider removing it with `rm {0}`"
)] )]
DifferentContent(std::path::PathBuf), 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 /// The file already exists
#[error("`{0}` already exists, consider removing it with `rm {0}`")] #[error("`{0}` already exists, consider removing it with `rm {0}`")]
FileExists(std::path::PathBuf), FileExists(std::path::PathBuf),
@ -583,8 +610,10 @@ impl HasExpectedErrors for ActionErrorKind {
match self { match self {
Self::PathUserMismatch(_, _, _) Self::PathUserMismatch(_, _, _)
| Self::PathGroupMismatch(_, _, _) | Self::PathGroupMismatch(_, _, _)
| Self::PathModeMismatch(_, _, _) => Some(Box::new(self)), | Self::PathModeMismatch(_, _, _)
| Self::ConfigurationConflict { .. } => Some(Box::new(self)),
Self::SystemdMissing => Some(Box::new(self)), Self::SystemdMissing => Some(Box::new(self)),
Self::Child(child) => child.kind().expected(),
_ => None, _ => None,
} }
} }

View file

@ -441,7 +441,7 @@ impl HasExpectedErrors for PlannerError {
fn expected<'a>(&'a self) -> Option<Box<dyn std::error::Error + 'a>> { fn expected<'a>(&'a self) -> Option<Box<dyn std::error::Error + 'a>> {
match self { match self {
this @ PlannerError::UnsupportedArchitecture(_) => Some(Box::new(this)), this @ PlannerError::UnsupportedArchitecture(_) => Some(Box::new(this)),
PlannerError::Action(_) => None, PlannerError::Action(action_error) => action_error.kind().expected(),
PlannerError::InstallSettings(_) => None, PlannerError::InstallSettings(_) => None,
PlannerError::Plist(_) => None, PlannerError::Plist(_) => None,
PlannerError::Sysctl(_) => None, PlannerError::Sysctl(_) => None,

View file

@ -8,7 +8,7 @@
"user": null, "user": null,
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": true "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
@ -18,21 +18,13 @@
"action": "provision_nix", "action": "provision_nix",
"fetch_nix": { "fetch_nix": {
"action": { "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", "dest": "/nix/temp-install-dir",
"proxy": null, "proxy": null,
"ssl_cert_file": null "ssl_cert_file": null
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
"delete_users": [],
"create_group": {
"action": {
"name": "nixbld",
"gid": 30000
},
"state": "Uncompleted"
},
"create_nix_tree": { "create_nix_tree": {
"action": { "action": {
"create_directories": [ "create_directories": [
@ -42,8 +34,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -53,8 +45,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -64,8 +56,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -75,8 +67,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -86,8 +78,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -97,8 +89,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -108,8 +100,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -119,8 +111,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -130,8 +122,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -141,8 +133,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -152,8 +144,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -163,8 +155,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -174,8 +166,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
} }
@ -192,6 +184,26 @@
}, },
"state": "Uncompleted" "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": {
"action": "configure_nix", "action": "configure_nix",
@ -229,13 +241,24 @@
] ]
}, },
"create_directories": [ "create_directories": [
{
"action": {
"path": "/etc/zsh",
"user": null,
"group": null,
"mode": 493,
"is_mountpoint": false,
"force_prune_on_revert": false
},
"state": "Uncompleted"
},
{ {
"action": { "action": {
"path": "/usr/share/fish/vendor_conf.d", "path": "/usr/share/fish/vendor_conf.d",
"user": null, "user": null,
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": false
}, },
"state": "Completed" "state": "Completed"
@ -286,6 +309,17 @@
}, },
"state": "Uncompleted" "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": { "action": {
"path": "/usr/share/fish/vendor_conf.d/nix.fish", "path": "/usr/share/fish/vendor_conf.d/nix.fish",
@ -309,24 +343,30 @@
"user": null, "user": null,
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": false
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
"create_or_merge_nix_config": { "create_or_insert_nix_conf": {
"action": { "action": {
"path": "/etc/nix/nix.conf", "path": "/etc/nix/nix.conf",
"pending_nix_config": { "user": null,
"settings": { "group": null,
"experimental-features": "nix-command flakes auto-allocate-uids", "mode": null,
"build-users-group": "nixbld", "buf": "!include ./nix-installer-defaults.conf",
"auto-optimise-store": "true", "position": "Beginning"
"bash-prompt-prefix": "(nix:$name)\\040", },
"extra-nix-path": "nixpkgs=flake:nixpkgs", "state": "Uncompleted"
"auto-allocate-uids": "true" },
} "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" "state": "Uncompleted"
} }
@ -336,12 +376,23 @@
}, },
"state": "Uncompleted" "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": {
"action": "configure_init_service", "action": "configure_init_service",
"init": "Systemd", "init": "Systemd",
"start_daemon": true, "start_daemon": true
"ssl_cert_file": null
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -359,10 +410,10 @@
"modify_profile": true, "modify_profile": true,
"nix_build_group_name": "nixbld", "nix_build_group_name": "nixbld",
"nix_build_group_id": 30000, "nix_build_group_id": 30000,
"nix_build_user_count": 0,
"nix_build_user_prefix": "nixbld", "nix_build_user_prefix": "nixbld",
"nix_build_user_count": 0,
"nix_build_user_id_base": 30000, "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, "proxy": null,
"ssl_cert_file": null, "ssl_cert_file": null,
"extra_conf": [], "extra_conf": [],

View file

@ -1,6 +1,12 @@
{ {
"version": "0.11.0", "version": "0.11.0",
"actions": [ "actions": [
{
"action": {
"action": "systemctl_daemon_reload"
},
"state": "Uncompleted"
},
{ {
"action": { "action": {
"action": "create_directory", "action": "create_directory",
@ -8,7 +14,7 @@
"user": null, "user": null,
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": true "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
@ -21,7 +27,7 @@
"group": null, "group": null,
"mode": 420, "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", "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" "state": "Uncompleted"
}, },
@ -32,8 +38,8 @@
"user": null, "user": null,
"group": null, "group": null,
"mode": 420, "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", "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 ",
"force": false "replace": false
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -44,8 +50,8 @@
"user": null, "user": null,
"group": null, "group": null,
"mode": 420, "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", "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",
"force": false "replace": false
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -62,21 +68,13 @@
"action": "provision_nix", "action": "provision_nix",
"fetch_nix": { "fetch_nix": {
"action": { "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", "dest": "/nix/temp-install-dir",
"proxy": null, "proxy": null,
"ssl_cert_file": null "ssl_cert_file": null
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
"delete_users": [],
"create_group": {
"action": {
"name": "nixbld",
"gid": 30000
},
"state": "Uncompleted"
},
"create_nix_tree": { "create_nix_tree": {
"action": { "action": {
"create_directories": [ "create_directories": [
@ -86,8 +84,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -97,8 +95,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -108,8 +106,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -119,8 +117,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -130,8 +128,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -141,8 +139,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -152,8 +150,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -163,8 +161,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -174,8 +172,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -185,8 +183,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -196,8 +194,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -207,8 +205,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -218,8 +216,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
} }
@ -236,6 +234,26 @@
}, },
"state": "Uncompleted" "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": {
"action": "configure_nix", "action": "configure_nix",
@ -271,7 +289,19 @@
"/etc/zsh/zshrc" "/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": [ "create_or_insert_into_files": [
{ {
"action": { "action": {
@ -316,6 +346,17 @@
"position": "Beginning" "position": "Beginning"
}, },
"state": "Uncompleted" "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, "user": null,
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": false
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
"create_or_merge_nix_config": { "create_or_insert_nix_conf": {
"action": { "action": {
"path": "/etc/nix/nix.conf", "path": "/etc/nix/nix.conf",
"pending_nix_config": { "user": null,
"settings": { "group": null,
"auto-optimise-store": "true", "mode": null,
"bash-prompt-prefix": "(nix:$name)\\040", "buf": "!include ./nix-installer-defaults.conf",
"build-users-group": "nixbld", "position": "Beginning"
"experimental-features": "nix-command flakes auto-allocate-uids", },
"extra-nix-path": "nixpkgs=flake:nixpkgs", "state": "Uncompleted"
"auto-allocate-uids": "true" },
} "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" "state": "Uncompleted"
} }
@ -360,8 +407,7 @@
"action": { "action": {
"action": "configure_init_service", "action": "configure_init_service",
"init": "Systemd", "init": "Systemd",
"start_daemon": true, "start_daemon": true
"ssl_cert_file": null
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -379,6 +425,12 @@
"path": "/nix/temp-install-dir" "path": "/nix/temp-install-dir"
}, },
"state": "Uncompleted" "state": "Uncompleted"
},
{
"action": {
"action": "systemctl_daemon_reload"
},
"state": "Uncompleted"
} }
], ],
"planner": { "planner": {
@ -388,10 +440,10 @@
"modify_profile": true, "modify_profile": true,
"nix_build_group_name": "nixbld", "nix_build_group_name": "nixbld",
"nix_build_group_id": 30000, "nix_build_group_id": 30000,
"nix_build_user_count": 0,
"nix_build_user_prefix": "nixbld", "nix_build_user_prefix": "nixbld",
"nix_build_user_count": 0,
"nix_build_user_id_base": 30000, "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, "proxy": null,
"ssl_cert_file": null, "ssl_cert_file": null,
"extra_conf": [], "extra_conf": [],

View file

@ -4,7 +4,7 @@
{ {
"action": { "action": {
"action": "create_apfs_volume", "action": "create_apfs_volume",
"disk": "disk3", "disk": "disk1",
"name": "Nix Store", "name": "Nix Store",
"case_sensitive": false, "case_sensitive": false,
"encrypt": false, "encrypt": false,
@ -25,14 +25,14 @@
}, },
"unmount_volume": { "unmount_volume": {
"action": { "action": {
"disk": "disk3", "disk": "disk1",
"name": "Nix Store" "name": "Nix Store"
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
"create_volume": { "create_volume": {
"action": { "action": {
"disk": "disk3", "disk": "disk1",
"name": "Nix Store", "name": "Nix Store",
"case_sensitive": false "case_sensitive": false
}, },
@ -88,21 +88,13 @@
"action": "provision_nix", "action": "provision_nix",
"fetch_nix": { "fetch_nix": {
"action": { "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", "dest": "/nix/temp-install-dir",
"proxy": null, "proxy": null,
"ssl_cert_file": null "ssl_cert_file": null
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
"delete_users_in_group": null,
"create_group": {
"action": {
"name": "nixbld",
"gid": 30000
},
"state": "Uncompleted"
},
"create_nix_tree": { "create_nix_tree": {
"action": { "action": {
"create_directories": [ "create_directories": [
@ -112,8 +104,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -123,8 +115,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -134,8 +126,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -145,8 +137,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -156,8 +148,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -167,8 +159,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -178,8 +170,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -189,8 +181,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -200,8 +192,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -211,8 +203,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -222,8 +214,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -233,8 +225,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -244,8 +236,8 @@
"user": "root", "user": "root",
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": true
}, },
"state": "Uncompleted" "state": "Uncompleted"
} }
@ -262,6 +254,636 @@
}, },
"state": "Uncompleted" "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": {
"action": "set_tmutil_exclusions", "action": "set_tmutil_exclusions",
@ -318,7 +940,30 @@
"/etc/zsh/zshrc" "/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": [ "create_or_insert_into_files": [
{ {
"action": { "action": {
@ -331,6 +976,17 @@
}, },
"state": "Uncompleted" "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": { "action": {
"path": "/etc/bash.bashrc", "path": "/etc/bash.bashrc",
@ -352,6 +1008,17 @@
"position": "Beginning" "position": "Beginning"
}, },
"state": "Uncompleted" "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, "user": null,
"group": null, "group": null,
"mode": 493, "mode": 493,
"is_mountpoint": true, "is_mountpoint": false,
"force_prune_on_revert": false "force_prune_on_revert": false
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
"create_or_merge_nix_config": { "create_or_insert_nix_conf": {
"action": { "action": {
"path": "/etc/nix/nix.conf", "path": "/etc/nix/nix.conf",
"pending_nix_config": { "user": null,
"settings": { "group": null,
"extra-nix-path": "nixpkgs=flake:nixpkgs", "mode": 493,
"auto-allocate-uids": "true", "buf": "!include ./defaults.conf\n",
"auto-optimise-store": "true", "position": "Beginning"
"build-users-group": "nixbld", },
"bash-prompt-prefix": "(nix:$name)\\040", "state": "Uncompleted"
"experimental-features": "nix-command flakes auto-allocate-uids" },
} "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" "state": "Uncompleted"
} }
@ -396,8 +1069,7 @@
"action": { "action": {
"action": "configure_init_service", "action": "configure_init_service",
"init": "Launchd", "init": "Launchd",
"start_daemon": true, "start_daemon": true
"ssl_cert_file": null
}, },
"state": "Uncompleted" "state": "Uncompleted"
}, },
@ -415,10 +1087,10 @@
"modify_profile": true, "modify_profile": true,
"nix_build_group_name": "nixbld", "nix_build_group_name": "nixbld",
"nix_build_group_id": 30000, "nix_build_group_id": 30000,
"nix_build_user_count": 32,
"nix_build_user_prefix": "_nixbld", "nix_build_user_prefix": "_nixbld",
"nix_build_user_count": 32,
"nix_build_user_id_base": 300, "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, "proxy": null,
"ssl_cert_file": null, "ssl_cert_file": null,
"extra_conf": [], "extra_conf": [],
@ -428,7 +1100,7 @@
"encrypt": null, "encrypt": null,
"case_sensitive": false, "case_sensitive": false,
"volume_label": "Nix Store", "volume_label": "Nix Store",
"root_disk": "disk3" "root_disk": "disk1"
}, },
"diagnostic_data": { "diagnostic_data": {
"version": "0.11.0", "version": "0.11.0",
@ -436,7 +1108,7 @@
"configured_settings": [], "configured_settings": [],
"os_name": "unknown", "os_name": "unknown",
"os_version": "unknown", "os_version": "unknown",
"triple": "aarch64-apple-darwin", "triple": "x86_64-apple-darwin",
"is_ci": false, "is_ci": false,
"endpoint": "https://install.determinate.systems/nix/diagnostic", "endpoint": "https://install.determinate.systems/nix/diagnostic",
"ssl_cert_file": null, "ssl_cert_file": null,