diff --git a/src/actions/base/configure_nix_daemon_service.rs b/src/actions/base/configure_nix_daemon_service.rs index 8c234fb..31e750c 100644 --- a/src/actions/base/configure_nix_daemon_service.rs +++ b/src/actions/base/configure_nix_daemon_service.rs @@ -81,7 +81,7 @@ impl Actionable for ConfigureNixDaemonService { const DARWIN_NIX_DAEMON_DEST: &str = "/Library/LaunchDaemons/org.nixos.nix-daemon.plist"; - let src = Path::new("/nix/var/nix/profiles/default").join(DARWIN_NIX_DAEMON_DEST); + let src = Path::new("/nix/var/nix/profiles/default/Library/LaunchDaemons/org.nixos.nix-daemon.plist"); tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST) .await .map_err(|e| { diff --git a/src/actions/base/create_user.rs b/src/actions/base/create_user.rs index 1ebefaf..47d6abc 100644 --- a/src/actions/base/create_user.rs +++ b/src/actions/base/create_user.rs @@ -9,16 +9,18 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable}; pub struct CreateUser { name: String, uid: usize, + groupname: String, gid: usize, action_state: ActionState, } impl CreateUser { #[tracing::instrument(skip_all)] - pub fn plan(name: String, uid: usize, gid: usize) -> Self { + pub fn plan(name: String, uid: usize, groupname: String, gid: usize) -> Self { Self { name, uid, + groupname, gid, action_state: ActionState::Uncompleted, } @@ -36,6 +38,7 @@ impl Actionable for CreateUser { let Self { name, uid, + groupname: _, gid, action_state: _, } = self; @@ -52,12 +55,14 @@ impl Actionable for CreateUser { #[tracing::instrument(skip_all, fields( user = self.name, uid = self.uid, + groupname = self.groupname, gid = self.gid, ))] async fn execute(&mut self) -> Result<(), Self::Error> { let Self { name, uid, + groupname, gid, action_state, } = self; @@ -77,15 +82,79 @@ impl Actionable for CreateUser { | OperatingSystem::Darwin => { execute_command(Command::new("/usr/bin/dscl").args([ ".", - "create", + "-create", &format!("/Users/{name}"), - "UniqueId", + ])) + .await + .map_err(Self::Error::Command)?; + execute_command(Command::new("/usr/bin/dscl").args([ + ".", + "-create", + &format!("/Users/{name}"), + "UniqueID", &format!("{uid}"), + ])) + .await + .map_err(Self::Error::Command)?; + execute_command(Command::new("/usr/bin/dscl").args([ + ".", + "-create", + &format!("/Users/{name}"), "PrimaryGroupID", &format!("{gid}"), ])) .await .map_err(Self::Error::Command)?; + execute_command(Command::new("/usr/bin/dscl").args([ + ".", + "-create", + &format!("/Users/{name}"), + "NFSHomeDirectory", + "/var/empty", + ])) + .await + .map_err(Self::Error::Command)?; + execute_command(Command::new("/usr/bin/dscl").args([ + ".", + "-create", + &format!("/Users/{name}"), + "UserShell", + "/sbin/nologin", + ])) + .await + .map_err(Self::Error::Command)?; + execute_command( + Command::new("/usr/bin/dscl") + .args([ + ".", + "-append", + &format!("/Groups/{groupname}"), + "GroupMembership", + ]) + .arg(&name), + ) + .await + .map_err(Self::Error::Command)?; + execute_command(Command::new("/usr/bin/dscl").args([ + ".", + "-create", + &format!("/Users/{name}"), + "IsHidden", + "1", + ])) + .await + .map_err(Self::Error::Command)?; + execute_command( + Command::new("/usr/sbin/dseditgroup") + .args(["-o", "edit"]) + .arg("-a") + .arg(&name) + .arg("-t") + .arg(&name) + .arg(groupname), + ) + .await + .map_err(Self::Error::Command)?; }, _ => { execute_command(Command::new("useradd").args([ @@ -124,6 +193,7 @@ impl Actionable for CreateUser { let Self { name, uid, + groupname: _, gid, action_state: _, } = self; @@ -146,6 +216,7 @@ impl Actionable for CreateUser { let Self { name, uid: _, + groupname: _, gid: _, action_state, } = self; @@ -163,7 +234,13 @@ impl Actionable for CreateUser { patch: _, } | OperatingSystem::Darwin => { - todo!() + execute_command(Command::new("/usr/bin/dscl").args([ + ".", + "-delete", + &format!("/Users/{name}"), + ])) + .await + .map_err(Self::Error::Command)?; }, _ => { execute_command(Command::new("userdel").args([&name.to_string()])) diff --git a/src/actions/base/darwin/bootstrap_volume.rs b/src/actions/base/darwin/bootstrap_volume.rs index e40f31a..2321b29 100644 --- a/src/actions/base/darwin/bootstrap_volume.rs +++ b/src/actions/base/darwin/bootstrap_volume.rs @@ -56,9 +56,13 @@ impl Actionable for BootstrapVolume { ) .await .map_err(Self::Error::Command)?; - execute_command(Command::new("launchctl").args(["-k", "system/org.nixos.darwin-store"])) - .await - .map_err(Self::Error::Command)?; + execute_command(Command::new("launchctl").args([ + "kickstart", + "-k", + "system/org.nixos.darwin-store", + ])) + .await + .map_err(Self::Error::Command)?; tracing::trace!("Bootstrapped volume"); *action_state = ActionState::Completed; diff --git a/src/actions/base/darwin/create_volume.rs b/src/actions/base/darwin/create_volume.rs index 19aaa68..64b9df7 100644 --- a/src/actions/base/darwin/create_volume.rs +++ b/src/actions/base/darwin/create_volume.rs @@ -121,14 +121,9 @@ impl Actionable for CreateVolume { } tracing::debug!("Deleting volume"); - execute_command(Command::new("/usr/sbin/diskutil").args([ - "apfs", - "deleteVolume", - &format!("{}", disk.display()), - name, - ])) - .await - .map_err(Self::Error::Command)?; + execute_command(Command::new("/usr/sbin/diskutil").args(["apfs", "deleteVolume", name])) + .await + .map_err(Self::Error::Command)?; tracing::trace!("Deleted volume"); *action_state = ActionState::Completed; diff --git a/src/actions/base/setup_default_profile.rs b/src/actions/base/setup_default_profile.rs index 9d72768..93d1150 100644 --- a/src/actions/base/setup_default_profile.rs +++ b/src/actions/base/setup_default_profile.rs @@ -73,15 +73,6 @@ impl Actionable for SetupDefaultProfile { return Err(Self::Error::NoNssCacert); // TODO(@hoverbear): Fix this error }; - // Install `nix` itself into the store - execute_command( - Command::new(nix_pkg.join("bin/nix-env")) - .arg("-i") - .arg(&nix_pkg), - ) - .await - .map_err(SetupDefaultProfileError::Command)?; - // Find an `nss-cacert` package, add it too. let nss_ca_cert_pkg_glob = "/nix/store/*-nss-cacert-*"; let mut found_nss_ca_cert_pkg = None; @@ -101,11 +92,29 @@ impl Actionable for SetupDefaultProfile { return Err(Self::Error::NoNssCacert); }; + // Install `nix` itself into the store + execute_command( + Command::new(nix_pkg.join("bin/nix-env")) + .arg("-i") + .arg(&nix_pkg) + .env("HOME", dirs::home_dir().ok_or(Self::Error::NoRootHome)?) + .env( + "NIX_SSL_CERT_FILE", + nss_ca_cert_pkg.join("etc/ssl/certs/ca-bundle.crt"), + ), /* This is apparently load bearing... */ + ) + .await + .map_err(SetupDefaultProfileError::Command)?; + // Install `nss-cacert` into the store execute_command( Command::new(nix_pkg.join("bin/nix-env")) .arg("-i") - .arg(&nss_ca_cert_pkg), + .arg(&nss_ca_cert_pkg) + .env( + "NIX_SSL_CERT_FILE", + nss_ca_cert_pkg.join("etc/ssl/certs/ca-bundle.crt"), + ), ) .await .map_err(SetupDefaultProfileError::Command)?; @@ -199,4 +208,6 @@ pub enum SetupDefaultProfileError { #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error, ), + #[error("No root home found to place channel configuration in")] + NoRootHome, } diff --git a/src/actions/meta/create_users_and_group.rs b/src/actions/meta/create_users_and_group.rs index d74ccf6..9c88c16 100644 --- a/src/actions/meta/create_users_and_group.rs +++ b/src/actions/meta/create_users_and_group.rs @@ -32,6 +32,7 @@ impl CreateUsersAndGroup { CreateUser::plan( format!("{}{count}", settings.nix_build_user_prefix), settings.nix_build_user_id_base + count, + settings.nix_build_group_name.clone(), settings.nix_build_group_id, ) }) @@ -108,35 +109,30 @@ impl Actionable for CreateUsersAndGroup { // Create group create_group.execute().await?; - // Create users - // TODO(@hoverbear): Abstract this, it will be common - let mut set = JoinSet::new(); - - let mut errors = Vec::default(); - - for (idx, create_user) in create_users.iter().enumerate() { - let mut create_user_clone = create_user.clone(); - let _abort_handle = set.spawn(async move { - create_user_clone.execute().await?; - Result::<_, CreateUserError>::Ok((idx, create_user_clone)) - }); + // Mac is apparently not threadsafe here... + for create_user in create_users.iter_mut() { + // let mut create_user_clone = create_user.clone(); + // let _abort_handle = set.spawn(async move { + create_user.execute().await?; + // Result::<_, CreateUserError>::Ok((idx, create_user_clone)) + // }); } - while let Some(result) = set.join_next().await { - match result { - Ok(Ok((idx, success))) => create_users[idx] = success, - Ok(Err(e)) => errors.push(e), - Err(e) => return Err(e)?, - }; - } + // while let Some(result) = set.join_next().await { + // match result { + // Ok(Ok((idx, success))) => create_users[idx] = success, + // Ok(Err(e)) => errors.push(e), + // Err(e) => return Err(e)?, + // }; + // } - if !errors.is_empty() { - if errors.len() == 1 { - return Err(errors.into_iter().next().unwrap().into()); - } else { - return Err(CreateUsersAndGroupError::CreateUsers(errors)); - } - } + // if !errors.is_empty() { + // if errors.len() == 1 { + // return Err(errors.into_iter().next().unwrap().into()); + // } else { + // return Err(CreateUsersAndGroupError::CreateUsers(errors)); + // } + // } tracing::trace!("Created users and groups"); *action_state = ActionState::Completed; diff --git a/src/actions/meta/darwin/create_apfs_volume.rs b/src/actions/meta/darwin/create_apfs_volume.rs index 343d6ef..04ddd70 100644 --- a/src/actions/meta/darwin/create_apfs_volume.rs +++ b/src/actions/meta/darwin/create_apfs_volume.rs @@ -1,5 +1,8 @@ use serde::Serialize; -use std::path::{Path, PathBuf}; +use std::{ + path::{Path, PathBuf}, + time::Duration, +}; use crate::actions::base::{ darwin::{ @@ -7,7 +10,7 @@ use crate::actions::base::{ CreateVolume, CreateVolumeError, EnableOwnership, EnableOwnershipError, EncryptVolume, EncryptVolumeError, UnmountVolume, UnmountVolumeError, }, - CreateOrAppendFile, CreateOrAppendFileError, + CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError, }; use crate::actions::{base::darwin, Action, ActionDescription, ActionState, Actionable}; @@ -25,6 +28,7 @@ pub struct CreateApfsVolume { create_volume: CreateVolume, create_or_append_fstab: CreateOrAppendFile, encrypt_volume: Option, + setup_volume_daemon: CreateFile, bootstrap_volume: BootstrapVolume, enable_ownership: EnableOwnership, action_state: ActionState, @@ -39,9 +43,14 @@ impl CreateApfsVolume { encrypt: Option, ) -> Result { let disk = disk.as_ref(); - let create_or_append_synthetic_conf = - CreateOrAppendFile::plan("/etc/synthetic.conf", None, None, 0o0655, "nix".into()) - .await?; + let create_or_append_synthetic_conf = CreateOrAppendFile::plan( + "/etc/synthetic.conf", + None, + None, + 0o0655, + "nix\n".into(), /* The newline is required otherwise it segfaults */ + ) + .await?; let create_synthetic_objects = CreateSyntheticObjects::plan().await?; @@ -64,6 +73,49 @@ impl CreateApfsVolume { None }; + let mount_command = if encrypt.is_some() { + vec![ + "/bin/sh", + "-c", + "/usr/bin/security find-generic-password", + "-s", + "{name}", + "-w", + "|", + "/usr/sbin/diskutil", + "apfs", + "unlockVolume", + &name, + "-mountpoint", + "/nix", + "-stdinpassphrase", + ] + } else { + vec!["/usr/sbin/diskutil", "mount", "-mountPoint", "/nix", &name] + }; + // TODO(@hoverbear): Use plist lib we have in tree... + let mount_plist = format!( + "\ + \n\ + \n\ + \n\ + \n\ + RunAtLoad\n\ + \n\ + Label\n\ + org.nixos.darwin-store\n\ + ProgramArguments\n\ + \n\ + {}\ + \n\ + \n\ + \n\ + \ + ", mount_command.iter().map(|v| format!("{v}\n")).collect::>().join("\n") + ); + let setup_volume_daemon = + CreateFile::plan(NIX_VOLUME_MOUNTD_DEST, None, None, None, mount_plist, false).await?; + let bootstrap_volume = BootstrapVolume::plan(NIX_VOLUME_MOUNTD_DEST).await?; let enable_ownership = EnableOwnership::plan("/nix").await?; @@ -78,6 +130,7 @@ impl CreateApfsVolume { create_volume, create_or_append_fstab, encrypt_volume, + setup_volume_daemon, bootstrap_volume, enable_ownership, action_state: ActionState::Uncompleted, @@ -121,6 +174,7 @@ impl Actionable for CreateApfsVolume { create_volume, create_or_append_fstab, encrypt_volume, + setup_volume_daemon, bootstrap_volume, enable_ownership, action_state, @@ -139,7 +193,13 @@ impl Actionable for CreateApfsVolume { if let Some(encrypt_volume) = encrypt_volume { encrypt_volume.execute().await?; } + setup_volume_daemon.execute().await?; + bootstrap_volume.execute().await?; + + // TODO: Check wait + tokio::time::sleep(Duration::from_millis(5000)).await; + enable_ownership.execute().await?; tracing::trace!("Created APFS volume"); @@ -179,6 +239,7 @@ impl Actionable for CreateApfsVolume { create_volume, create_or_append_fstab, encrypt_volume, + setup_volume_daemon, bootstrap_volume, enable_ownership, action_state, @@ -191,6 +252,7 @@ impl Actionable for CreateApfsVolume { enable_ownership.revert().await?; bootstrap_volume.revert().await?; + setup_volume_daemon.revert().await?; if let Some(encrypt_volume) = encrypt_volume { encrypt_volume.revert().await?; } @@ -217,6 +279,8 @@ impl From for Action { #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateApfsVolumeError { + #[error(transparent)] + CreateFile(#[from] CreateFileError), #[error(transparent)] DarwinBootstrapVolume(#[from] BootstrapVolumeError), #[error(transparent)] diff --git a/src/actions/meta/place_channel_configuration.rs b/src/actions/meta/place_channel_configuration.rs index 0e60a15..56a34cc 100644 --- a/src/actions/meta/place_channel_configuration.rs +++ b/src/actions/meta/place_channel_configuration.rs @@ -26,7 +26,7 @@ impl PlaceChannelConfiguration { let create_file = CreateFile::plan( dirs::home_dir() .ok_or(PlaceChannelConfigurationError::NoRootHome)? - .join("/.nix-channels"), + .join(".nix-channels"), None, None, 0o0664, diff --git a/src/actions/meta/provision_nix.rs b/src/actions/meta/provision_nix.rs index 034a887..e2ea7f7 100644 --- a/src/actions/meta/provision_nix.rs +++ b/src/actions/meta/provision_nix.rs @@ -15,7 +15,6 @@ use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersA #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct ProvisionNix { - create_nix_dir: CreateDirectory, fetch_nix: FetchNix, create_users_and_group: CreateUsersAndGroup, create_nix_tree: CreateNixTree, @@ -26,8 +25,6 @@ pub struct ProvisionNix { impl ProvisionNix { #[tracing::instrument(skip_all)] pub async fn plan(settings: InstallSettings) -> Result { - let create_nix_dir = CreateDirectory::plan("/nix", None, None, 0o0755, true).await?; - let fetch_nix = FetchNix::plan( settings.nix_package_url.clone(), PathBuf::from("/nix/temp-install-dir"), @@ -38,7 +35,6 @@ impl ProvisionNix { let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?; Ok(Self { - create_nix_dir, fetch_nix, create_users_and_group, create_nix_tree, @@ -53,7 +49,6 @@ impl Actionable for ProvisionNix { type Error = ProvisionNixError; fn describe_execute(&self) -> Vec { let Self { - create_nix_dir, fetch_nix, create_users_and_group, create_nix_tree, @@ -64,7 +59,6 @@ impl Actionable for ProvisionNix { vec![] } else { let mut buf = Vec::default(); - buf.append(&mut create_nix_dir.describe_execute()); buf.append(&mut fetch_nix.describe_execute()); buf.append(&mut create_users_and_group.describe_execute()); buf.append(&mut create_nix_tree.describe_execute()); @@ -77,7 +71,6 @@ impl Actionable for ProvisionNix { #[tracing::instrument(skip_all)] async fn execute(&mut self) -> Result<(), Self::Error> { let Self { - create_nix_dir, fetch_nix, create_nix_tree, create_users_and_group, @@ -91,8 +84,6 @@ impl Actionable for ProvisionNix { *action_state = ActionState::Progress; tracing::debug!("Provisioning Nix"); - create_nix_dir.execute().await?; - // We fetch nix while doing the rest, then move it over. let mut fetch_nix_clone = fetch_nix.clone(); let fetch_nix_handle = tokio::task::spawn(async { @@ -116,7 +107,6 @@ impl Actionable for ProvisionNix { fn describe_revert(&self) -> Vec { let Self { - create_nix_dir, fetch_nix, create_users_and_group, create_nix_tree, @@ -131,7 +121,6 @@ impl Actionable for ProvisionNix { buf.append(&mut create_nix_tree.describe_revert()); buf.append(&mut create_users_and_group.describe_revert()); buf.append(&mut fetch_nix.describe_revert()); - buf.append(&mut create_nix_dir.describe_revert()); buf } } @@ -139,7 +128,6 @@ impl Actionable for ProvisionNix { #[tracing::instrument(skip_all)] async fn revert(&mut self) -> Result<(), Self::Error> { let Self { - create_nix_dir, fetch_nix, create_nix_tree, create_users_and_group, @@ -172,8 +160,6 @@ impl Actionable for ProvisionNix { *fetch_nix = fetch_nix_handle.await.map_err(ProvisionNixError::from)??; move_unpacked_nix.revert().await?; - create_nix_dir.revert().await?; - tracing::trace!("Unprovisioned Nix"); *action_state = ActionState::Uncompleted; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 2751e84..b971574 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,10 @@ async fn execute_command(command: &mut Command) -> Result Ok(output), false => Err(std::io::Error::new( std::io::ErrorKind::Other, - format!("Command `{command_str}` failed status"), + format!( + "Command `{command_str}` failed status, stderr:\n{}\n", + String::from_utf8(output.stderr).unwrap_or_else(|_e| String::from("")) + ), )), } } diff --git a/src/planner/linux/multi_user.rs b/src/planner/linux/multi_user.rs index c2d29cb..4f941bb 100644 --- a/src/planner/linux/multi_user.rs +++ b/src/planner/linux/multi_user.rs @@ -1,6 +1,6 @@ use crate::{ actions::{ - base::StartSystemdUnit, + base::{CreateDirectory, StartSystemdUnit}, meta::{ConfigureNix, ProvisionNix}, Action, ActionError, }, @@ -21,6 +21,10 @@ impl Plannable for LinuxMultiUser { planner: Self.into(), settings: settings.clone(), actions: vec![ + CreateDirectory::plan("/nix", None, None, 0o0755, true) + .await + .map(Action::from) + .map_err(ActionError::from)?, ProvisionNix::plan(settings.clone()) .await .map(Action::from) diff --git a/src/planner/mod.rs b/src/planner/mod.rs index 4a85c31..f1b1279 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -20,8 +20,10 @@ impl Planner { match (Architecture::host(), OperatingSystem::host()) { (Architecture::X86_64, OperatingSystem::Linux) => Ok(Self::LinuxMultiUser), (Architecture::Aarch64(_), OperatingSystem::Linux) => Ok(Self::LinuxMultiUser), - (Architecture::X86_64, OperatingSystem::MacOSX { .. }) => Ok(Self::DarwinMultiUser), - (Architecture::Aarch64(_), OperatingSystem::MacOSX { .. }) => Ok(Self::DarwinMultiUser), + (Architecture::X86_64, OperatingSystem::MacOSX { .. }) + | (Architecture::X86_64, OperatingSystem::Darwin) => Ok(Self::DarwinMultiUser), + (Architecture::Aarch64(_), OperatingSystem::MacOSX { .. }) + | (Architecture::Aarch64(_), OperatingSystem::Darwin) => Ok(Self::DarwinMultiUser), _ => Err(PlannerError::UnsupportedArchitecture(target_lexicon::HOST)), } } diff --git a/src/planner/specific/steam_deck.rs b/src/planner/specific/steam_deck.rs index e92ad20..e31d4c9 100644 --- a/src/planner/specific/steam_deck.rs +++ b/src/planner/specific/steam_deck.rs @@ -1,6 +1,6 @@ use crate::{ actions::{ - base::StartSystemdUnit, + base::{CreateDirectory, StartSystemdUnit}, meta::{CreateSystemdSysext, ProvisionNix}, Action, ActionError, }, @@ -27,6 +27,10 @@ impl Plannable for SteamDeck { .await .map(Action::from) .map_err(ActionError::from)?, + CreateDirectory::plan("/nix", None, None, 0o0755, true) + .await + .map(Action::from) + .map_err(ActionError::from)?, ProvisionNix::plan(settings.clone()) .await .map(Action::from) diff --git a/src/settings.rs b/src/settings.rs index 5fca261..bb1fff3 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -22,22 +22,32 @@ pub struct InstallSettings { impl InstallSettings { pub fn default() -> Result { let url; + let nix_build_user_prefix; + let nix_build_user_id_base; use target_lexicon::{Architecture, OperatingSystem}; match (Architecture::host(), OperatingSystem::host()) { (Architecture::X86_64, OperatingSystem::Linux) => { url = "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-linux.tar.xz"; + nix_build_user_prefix = "nixbld"; + nix_build_user_id_base = 3000; }, (Architecture::Aarch64(_), OperatingSystem::Linux) => { url = "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-aarch64-linux.tar.xz"; + nix_build_user_prefix = "nixbld"; + nix_build_user_id_base = 3000; }, (Architecture::X86_64, OperatingSystem::MacOSX { .. }) | (Architecture::X86_64, OperatingSystem::Darwin) => { url = "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-darwin.tar.xz"; + nix_build_user_prefix = "_nixbld"; + nix_build_user_id_base = 300; }, (Architecture::Aarch64(_), OperatingSystem::MacOSX { .. }) | (Architecture::Aarch64(_), OperatingSystem::Darwin) => { url = "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-aarch64-darwin.tar.xz"; + nix_build_user_prefix = "_nixbld"; + nix_build_user_id_base = 300; }, _ => { return Err(InstallSettingsError::UnsupportedArchitecture( @@ -53,8 +63,8 @@ impl InstallSettings { modify_profile: Default::default(), nix_build_group_name: String::from("nixbld"), nix_build_group_id: 3000, - nix_build_user_prefix: String::from("nixbld"), - nix_build_user_id_base: 3001, + nix_build_user_prefix: nix_build_user_prefix.to_string(), + nix_build_user_id_base, nix_package_url: url .parse() .expect("Could not parse default Nix archive url, please report this issue"),