forked from lix-project/lix-installer
Curing existing /nix (#310)
* Curing existing /nix * Fixup macs * Suggest an uninstall command if the binary is not present * Fixup some nits * Skip a not great suggestion * Suggest a nice url
This commit is contained in:
parent
b7d7afd02e
commit
85abfc3cb5
|
@ -78,7 +78,7 @@ impl CreateDirectory {
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("Creating directory `{}` already complete", path.display(),);
|
tracing::debug!("Creating directory `{}` already complete", path.display(),);
|
||||||
ActionState::Skipped
|
ActionState::Completed
|
||||||
} else {
|
} else {
|
||||||
ActionState::Uncompleted
|
ActionState::Uncompleted
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub(crate) mod create_or_merge_nix_config;
|
||||||
pub(crate) mod create_user;
|
pub(crate) mod create_user;
|
||||||
pub(crate) mod fetch_and_unpack_nix;
|
pub(crate) mod fetch_and_unpack_nix;
|
||||||
pub(crate) mod move_unpacked_nix;
|
pub(crate) mod move_unpacked_nix;
|
||||||
|
pub(crate) mod remove_directory;
|
||||||
pub(crate) mod setup_default_profile;
|
pub(crate) mod setup_default_profile;
|
||||||
|
|
||||||
pub use add_user_to_group::AddUserToGroup;
|
pub use add_user_to_group::AddUserToGroup;
|
||||||
|
@ -20,4 +21,5 @@ pub use create_or_merge_nix_config::CreateOrMergeNixConfig;
|
||||||
pub use create_user::CreateUser;
|
pub use create_user::CreateUser;
|
||||||
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
|
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
|
||||||
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
||||||
|
pub use remove_directory::RemoveDirectory;
|
||||||
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
||||||
|
|
|
@ -11,14 +11,14 @@ Move an unpacked Nix at `src` to `/nix`
|
||||||
*/
|
*/
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct MoveUnpackedNix {
|
pub struct MoveUnpackedNix {
|
||||||
src: PathBuf,
|
unpacked_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MoveUnpackedNix {
|
impl MoveUnpackedNix {
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
pub async fn plan(src: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
|
pub async fn plan(unpacked_path: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
// Note: Do NOT try to check for the src/dest since the installer creates those
|
// Note: Do NOT try to check for the src/dest since the installer creates those
|
||||||
Ok(Self { src }.into())
|
Ok(Self { unpacked_path }.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ impl Action for MoveUnpackedNix {
|
||||||
span!(
|
span!(
|
||||||
tracing::Level::DEBUG,
|
tracing::Level::DEBUG,
|
||||||
"mount_unpacked_nix",
|
"mount_unpacked_nix",
|
||||||
src = tracing::field::display(self.src.display()),
|
src = tracing::field::display(self.unpacked_path.display()),
|
||||||
dest = DEST,
|
dest = DEST,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -46,44 +46,56 @@ impl Action for MoveUnpackedNix {
|
||||||
format!("Move the downloaded Nix into `/nix`"),
|
format!("Move the downloaded Nix into `/nix`"),
|
||||||
vec![format!(
|
vec![format!(
|
||||||
"Nix is being downloaded to `{}` and should be in `/nix`",
|
"Nix is being downloaded to `{}` and should be in `/nix`",
|
||||||
self.src.display(),
|
self.unpacked_path.display(),
|
||||||
)],
|
)],
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { src } = self;
|
let Self { unpacked_path } = self;
|
||||||
|
|
||||||
// TODO(@Hoverbear): I would like to make this less awful
|
// This is the `nix-$VERSION` folder which unpacks from the tarball, not a nix derivation
|
||||||
let found_nix_paths = glob::glob(&format!("{}/nix-*", src.display()))
|
let found_nix_paths = glob::glob(&format!("{}/nix-*", unpacked_path.display()))
|
||||||
.map_err(|e| ActionError::Custom(Box::new(e)))?
|
.map_err(|e| ActionError::Custom(Box::new(e)))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|e| ActionError::Custom(Box::new(e)))?;
|
.map_err(|e| ActionError::Custom(Box::new(e)))?;
|
||||||
assert_eq!(
|
if found_nix_paths.len() != 1 {
|
||||||
found_nix_paths.len(),
|
return Err(ActionError::MalformedBinaryTarball);
|
||||||
1,
|
}
|
||||||
"Did not expect to find multiple nix paths, please report this"
|
|
||||||
);
|
|
||||||
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
|
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
|
||||||
let src_store = found_nix_path.join("store");
|
let src_store = found_nix_path.join("store");
|
||||||
let dest = Path::new(DEST).join("store");
|
let mut src_store_listing = tokio::fs::read_dir(src_store.clone())
|
||||||
tracing::trace!(src = %src_store.display(), dest = %dest.display(), "Renaming");
|
|
||||||
tokio::fs::rename(&src_store, &dest)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Rename(src_store.clone(), dest.to_owned(), e))?;
|
.map_err(|e| ActionError::ReadDir(src_store.clone(), e))?;
|
||||||
|
let dest_store = Path::new(DEST).join("store");
|
||||||
|
if dest_store.exists() {
|
||||||
|
if !dest_store.is_dir() {
|
||||||
|
return Err(ActionError::PathWasNotDirectory(dest_store.clone()))?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tokio::fs::create_dir(&dest_store)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ActionError::CreateDirectory(dest_store.clone(), e))?;
|
||||||
|
}
|
||||||
|
|
||||||
let src_reginfo = found_nix_path.join(".reginfo");
|
while let Some(entry) = src_store_listing
|
||||||
|
.next_entry()
|
||||||
// Move_unpacked_nix expects it here
|
|
||||||
let dest_reginfo = Path::new(DEST).join(".reginfo");
|
|
||||||
tokio::fs::rename(&src_reginfo, &dest_reginfo)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Rename(src_reginfo.clone(), dest_reginfo.to_owned(), e))?;
|
.map_err(|e| ActionError::ReadDir(src_store.clone(), e))?
|
||||||
|
{
|
||||||
tokio::fs::remove_dir_all(&src)
|
let entry_dest = dest_store.join(entry.file_name());
|
||||||
.await
|
if entry_dest.exists() {
|
||||||
.map_err(|e| ActionError::Remove(src.clone(), e))?;
|
tracing::trace!(src = %entry.path().display(), dest = %entry_dest.display(), "Skipping, already exists");
|
||||||
|
} else {
|
||||||
|
tracing::trace!(src = %entry.path().display(), dest = %entry_dest.display(), "Renaming");
|
||||||
|
tokio::fs::rename(&entry.path(), &entry_dest)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
ActionError::Rename(entry.path().clone(), entry_dest.to_owned(), e)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
76
src/action/base/remove_directory.rs
Normal file
76
src/action/base/remove_directory.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use tokio::fs::remove_dir_all;
|
||||||
|
use tracing::{span, Span};
|
||||||
|
|
||||||
|
use crate::action::{Action, ActionDescription, ActionState};
|
||||||
|
use crate::action::{ActionError, StatefulAction};
|
||||||
|
|
||||||
|
/** Remove a directory, does nothing on revert.
|
||||||
|
*/
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct RemoveDirectory {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RemoveDirectory {
|
||||||
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
|
pub async fn plan(path: impl AsRef<Path>) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
|
let path = path.as_ref().to_path_buf();
|
||||||
|
|
||||||
|
Ok(StatefulAction {
|
||||||
|
action: Self {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
},
|
||||||
|
state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
#[typetag::serde(name = "remove_directory")]
|
||||||
|
impl Action for RemoveDirectory {
|
||||||
|
fn action_tag() -> crate::action::ActionTag {
|
||||||
|
crate::action::ActionTag("remove_directory")
|
||||||
|
}
|
||||||
|
fn tracing_synopsis(&self) -> String {
|
||||||
|
format!("Remove directory `{}`", self.path.display())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tracing_span(&self) -> Span {
|
||||||
|
span!(
|
||||||
|
tracing::Level::DEBUG,
|
||||||
|
"remove_directory",
|
||||||
|
path = tracing::field::display(self.path.display()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_description(&self) -> Vec<ActionDescription> {
|
||||||
|
vec![ActionDescription::new(self.tracing_synopsis(), vec![])]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
|
if self.path.exists() {
|
||||||
|
if !self.path.is_dir() {
|
||||||
|
return Err(ActionError::PathWasNotDirectory(self.path.clone()));
|
||||||
|
}
|
||||||
|
remove_dir_all(&self.path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ActionError::Remove(self.path.clone(), e))?;
|
||||||
|
} else {
|
||||||
|
tracing::debug!("Directory `{}` not present, skipping", self.path.display(),);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn revert_description(&self) -> Vec<ActionDescription> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{ActionError, ActionTag, StatefulAction},
|
action::{ActionError, ActionTag, StatefulAction},
|
||||||
|
@ -16,12 +16,14 @@ use crate::action::{Action, ActionDescription};
|
||||||
Setup the default Nix profile with `nss-cacert` and `nix` itself.
|
Setup the default Nix profile with `nss-cacert` and `nix` itself.
|
||||||
*/
|
*/
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct SetupDefaultProfile {}
|
pub struct SetupDefaultProfile {
|
||||||
|
unpacked_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
impl SetupDefaultProfile {
|
impl SetupDefaultProfile {
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
pub async fn plan(unpacked_path: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
Ok(Self {}.into())
|
Ok(Self { unpacked_path }.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,56 +93,61 @@ impl Action for SetupDefaultProfile {
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
let found_nix_paths = glob::glob(&format!("{}/nix-*", self.unpacked_path.display()))
|
||||||
let reginfo_path =
|
.map_err(|e| ActionError::Custom(Box::new(e)))?
|
||||||
Path::new(crate::action::base::move_unpacked_nix::DEST).join(".reginfo");
|
.collect::<Result<Vec<_>, _>>()
|
||||||
let reginfo = tokio::fs::read(®info_path)
|
.map_err(|e| ActionError::Custom(Box::new(e)))?;
|
||||||
.await
|
if found_nix_paths.len() != 1 {
|
||||||
.map_err(|e| ActionError::Read(reginfo_path.to_path_buf(), e))?;
|
return Err(ActionError::MalformedBinaryTarball);
|
||||||
let mut load_db_command = Command::new(nix_pkg.join("bin/nix-store"));
|
|
||||||
load_db_command.process_group(0);
|
|
||||||
load_db_command.arg("--load-db");
|
|
||||||
load_db_command.stdin(std::process::Stdio::piped());
|
|
||||||
load_db_command.stdout(std::process::Stdio::piped());
|
|
||||||
load_db_command.stderr(std::process::Stdio::piped());
|
|
||||||
load_db_command.env(
|
|
||||||
"HOME",
|
|
||||||
dirs::home_dir().ok_or_else(|| {
|
|
||||||
ActionError::Custom(Box::new(SetupDefaultProfileError::NoRootHome))
|
|
||||||
})?,
|
|
||||||
);
|
|
||||||
tracing::trace!(
|
|
||||||
"Executing `{:?}` with stdin from `{}`",
|
|
||||||
load_db_command.as_std(),
|
|
||||||
reginfo_path.display()
|
|
||||||
);
|
|
||||||
let mut handle = load_db_command
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| ActionError::command(&load_db_command, e))?;
|
|
||||||
|
|
||||||
let mut stdin = handle.stdin.take().unwrap();
|
|
||||||
stdin
|
|
||||||
.write_all(®info)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ActionError::Write(PathBuf::from("/dev/stdin"), e))?;
|
|
||||||
stdin
|
|
||||||
.flush()
|
|
||||||
.await
|
|
||||||
.map_err(|e| ActionError::Write(PathBuf::from("/dev/stdin"), e))?;
|
|
||||||
drop(stdin);
|
|
||||||
tracing::trace!(
|
|
||||||
"Wrote `{}` to stdin of `nix-store --load-db`",
|
|
||||||
reginfo_path.display()
|
|
||||||
);
|
|
||||||
|
|
||||||
let output = handle
|
|
||||||
.wait_with_output()
|
|
||||||
.await
|
|
||||||
.map_err(|e| ActionError::command(&load_db_command, e))?;
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(ActionError::command_output(&load_db_command, output));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
|
||||||
|
let reginfo_path = PathBuf::from(found_nix_path).join(".reginfo");
|
||||||
|
let reginfo = tokio::fs::read(®info_path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ActionError::Read(reginfo_path.to_path_buf(), e))?;
|
||||||
|
let mut load_db_command = Command::new(nix_pkg.join("bin/nix-store"));
|
||||||
|
load_db_command.process_group(0);
|
||||||
|
load_db_command.arg("--load-db");
|
||||||
|
load_db_command.stdin(std::process::Stdio::piped());
|
||||||
|
load_db_command.stdout(std::process::Stdio::piped());
|
||||||
|
load_db_command.stderr(std::process::Stdio::piped());
|
||||||
|
load_db_command.env(
|
||||||
|
"HOME",
|
||||||
|
dirs::home_dir().ok_or_else(|| {
|
||||||
|
ActionError::Custom(Box::new(SetupDefaultProfileError::NoRootHome))
|
||||||
|
})?,
|
||||||
|
);
|
||||||
|
tracing::trace!(
|
||||||
|
"Executing `{:?}` with stdin from `{}`",
|
||||||
|
load_db_command.as_std(),
|
||||||
|
reginfo_path.display()
|
||||||
|
);
|
||||||
|
let mut handle = load_db_command
|
||||||
|
.spawn()
|
||||||
|
.map_err(|e| ActionError::command(&load_db_command, e))?;
|
||||||
|
|
||||||
|
let mut stdin = handle.stdin.take().unwrap();
|
||||||
|
stdin
|
||||||
|
.write_all(®info)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ActionError::Write(PathBuf::from("/dev/stdin"), e))?;
|
||||||
|
stdin
|
||||||
|
.flush()
|
||||||
|
.await
|
||||||
|
.map_err(|e| ActionError::Write(PathBuf::from("/dev/stdin"), e))?;
|
||||||
|
drop(stdin);
|
||||||
|
tracing::trace!(
|
||||||
|
"Wrote `{}` to stdin of `nix-store --load-db`",
|
||||||
|
reginfo_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = handle
|
||||||
|
.wait_with_output()
|
||||||
|
.await
|
||||||
|
.map_err(|e| ActionError::command(&load_db_command, e))?;
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(ActionError::command_output(&load_db_command, output));
|
||||||
|
};
|
||||||
|
|
||||||
// Install `nix` itself into the store
|
// Install `nix` itself into the store
|
||||||
execute_command(
|
execute_command(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tracing::{span, Span};
|
use tracing::{span, Span};
|
||||||
|
@ -138,15 +138,17 @@ impl Action for ConfigureInitService {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
InitSystem::Systemd => {
|
InitSystem::Systemd => {
|
||||||
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
|
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
|
||||||
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
|
if !Path::new(TMPFILES_DEST).exists() {
|
||||||
.await
|
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
|
||||||
.map_err(|e| {
|
.await
|
||||||
ActionError::Symlink(
|
.map_err(|e| {
|
||||||
PathBuf::from(TMPFILES_SRC),
|
ActionError::Symlink(
|
||||||
PathBuf::from(TMPFILES_DEST),
|
PathBuf::from(TMPFILES_SRC),
|
||||||
e,
|
PathBuf::from(TMPFILES_DEST),
|
||||||
)
|
e,
|
||||||
})?;
|
)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemd-tmpfiles")
|
Command::new("systemd-tmpfiles")
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
base::SetupDefaultProfile,
|
base::SetupDefaultProfile,
|
||||||
common::{ConfigureShellProfile, PlaceNixConfiguration},
|
common::{ConfigureShellProfile, PlaceNixConfiguration},
|
||||||
Action, ActionDescription, ActionError, ActionTag, StatefulAction,
|
Action, ActionDescription, ActionError, ActionTag, StatefulAction,
|
||||||
},
|
},
|
||||||
settings::CommonSettings,
|
settings::{CommonSettings, SCRATCH_DIR},
|
||||||
};
|
};
|
||||||
|
|
||||||
use tracing::{span, Instrument, Span};
|
use tracing::{span, Instrument, Span};
|
||||||
|
@ -22,7 +24,7 @@ pub struct ConfigureNix {
|
||||||
impl ConfigureNix {
|
impl ConfigureNix {
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let setup_default_profile = SetupDefaultProfile::plan()
|
let setup_default_profile = SetupDefaultProfile::plan(PathBuf::from(SCRATCH_DIR))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Child(SetupDefaultProfile::action_tag(), Box::new(e)))?;
|
.map_err(|e| ActionError::Child(SetupDefaultProfile::action_tag(), Box::new(e)))?;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
base::{FetchAndUnpackNix, MoveUnpackedNix},
|
base::{FetchAndUnpackNix, MoveUnpackedNix},
|
||||||
Action, ActionDescription, ActionError, ActionTag, StatefulAction,
|
Action, ActionDescription, ActionError, ActionTag, StatefulAction,
|
||||||
},
|
},
|
||||||
settings::CommonSettings,
|
settings::{CommonSettings, SCRATCH_DIR},
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -24,18 +24,16 @@ pub struct ProvisionNix {
|
||||||
impl ProvisionNix {
|
impl ProvisionNix {
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let fetch_nix = FetchAndUnpackNix::plan(
|
let fetch_nix =
|
||||||
settings.nix_package_url.clone(),
|
FetchAndUnpackNix::plan(settings.nix_package_url.clone(), PathBuf::from(SCRATCH_DIR))
|
||||||
PathBuf::from("/nix/temp-install-dir"),
|
.await?;
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone())
|
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Child(CreateUsersAndGroups::action_tag(), Box::new(e)))?;
|
.map_err(|e| ActionError::Child(CreateUsersAndGroups::action_tag(), Box::new(e)))?;
|
||||||
let create_nix_tree = CreateNixTree::plan()
|
let create_nix_tree = CreateNixTree::plan()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Child(CreateNixTree::action_tag(), Box::new(e)))?;
|
.map_err(|e| ActionError::Child(CreateNixTree::action_tag(), Box::new(e)))?;
|
||||||
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir"))
|
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from(SCRATCH_DIR))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Child(MoveUnpackedNix::action_tag(), Box::new(e)))?;
|
.map_err(|e| ActionError::Child(MoveUnpackedNix::action_tag(), Box::new(e)))?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
|
@ -311,8 +311,10 @@ pub enum ActionError {
|
||||||
existing_mode = .1 & 0o777,
|
existing_mode = .1 & 0o777,
|
||||||
planned_mode = .2 & 0o777)]
|
planned_mode = .2 & 0o777)]
|
||||||
PathModeMismatch(std::path::PathBuf, u32, u32),
|
PathModeMismatch(std::path::PathBuf, u32, u32),
|
||||||
#[error("`{0}` was not a file")]
|
#[error("Path `{0}` exists, but is not a file, consider removing it with `rm {0}`")]
|
||||||
PathWasNotFile(std::path::PathBuf),
|
PathWasNotFile(std::path::PathBuf),
|
||||||
|
#[error("Path `{0}` exists, but is not a directory, consider removing it with `rm {0}`")]
|
||||||
|
PathWasNotDirectory(std::path::PathBuf),
|
||||||
#[error("Getting metadata for {0}`")]
|
#[error("Getting metadata for {0}`")]
|
||||||
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
|
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
|
||||||
#[error("Creating directory `{0}`")]
|
#[error("Creating directory `{0}`")]
|
||||||
|
@ -341,6 +343,8 @@ pub enum ActionError {
|
||||||
),
|
),
|
||||||
#[error("Read path `{0}`")]
|
#[error("Read path `{0}`")]
|
||||||
Read(std::path::PathBuf, #[source] std::io::Error),
|
Read(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Reading directory `{0}`")]
|
||||||
|
ReadDir(std::path::PathBuf, #[source] std::io::Error),
|
||||||
#[error("Open path `{0}`")]
|
#[error("Open path `{0}`")]
|
||||||
Open(std::path::PathBuf, #[source] std::io::Error),
|
Open(std::path::PathBuf, #[source] std::io::Error),
|
||||||
#[error("Write path `{0}`")]
|
#[error("Write path `{0}`")]
|
||||||
|
@ -410,6 +414,8 @@ pub enum ActionError {
|
||||||
/// A MacOS (Darwin) plist related error
|
/// A MacOS (Darwin) plist related error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Plist(#[from] plist::Error),
|
Plist(#[from] plist::Error),
|
||||||
|
#[error("Unexpected binary tarball contents found, the build result from `https://releases.nixos.org/?prefix=nix/` or `nix build nix#hydraJobs.binaryTarball.$SYSTEM` is expected")]
|
||||||
|
MalformedBinaryTarball,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActionError {
|
impl ActionError {
|
||||||
|
|
|
@ -71,6 +71,7 @@ impl CommandExecute for Install {
|
||||||
|
|
||||||
let existing_receipt: Option<InstallPlan> = match Path::new(RECEIPT_LOCATION).exists() {
|
let existing_receipt: Option<InstallPlan> = match Path::new(RECEIPT_LOCATION).exists() {
|
||||||
true => {
|
true => {
|
||||||
|
tracing::trace!("Reading existing receipt");
|
||||||
let install_plan_string = tokio::fs::read_to_string(&RECEIPT_LOCATION)
|
let install_plan_string = tokio::fs::read_to_string(&RECEIPT_LOCATION)
|
||||||
.await
|
.await
|
||||||
.wrap_err("Reading plan")?;
|
.wrap_err("Reading plan")?;
|
||||||
|
@ -79,6 +80,11 @@ impl CommandExecute for Install {
|
||||||
false => None,
|
false => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let uninstall_command = match Path::new("/nix/nix-installer").exists() {
|
||||||
|
true => "/nix/nix-installer uninstall".into(),
|
||||||
|
false => format!("curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix/tag/v{} | sh -s -- uninstall", env!("CARGO_PKG_VERSION")),
|
||||||
|
};
|
||||||
|
|
||||||
let mut install_plan = match (planner, plan) {
|
let mut install_plan = match (planner, plan) {
|
||||||
(Some(planner), None) => {
|
(Some(planner), None) => {
|
||||||
let chosen_planner: Box<dyn Planner> = planner.clone().boxed();
|
let chosen_planner: Box<dyn Planner> = planner.clone().boxed();
|
||||||
|
@ -86,18 +92,15 @@ impl CommandExecute for Install {
|
||||||
match existing_receipt {
|
match existing_receipt {
|
||||||
Some(existing_receipt) => {
|
Some(existing_receipt) => {
|
||||||
if existing_receipt.planner.typetag_name() != chosen_planner.typetag_name() {
|
if existing_receipt.planner.typetag_name() != chosen_planner.typetag_name() {
|
||||||
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used a different planner, try uninstalling the existing install").red());
|
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used a different planner, try uninstalling the existing install with `{uninstall_command}`").red());
|
||||||
return Ok(ExitCode::FAILURE)
|
return Ok(ExitCode::FAILURE)
|
||||||
}
|
}
|
||||||
if existing_receipt.planner.settings().map_err(|e| eyre!(e))? != chosen_planner.settings().map_err(|e| eyre!(e))? {
|
if existing_receipt.planner.settings().map_err(|e| eyre!(e))? != chosen_planner.settings().map_err(|e| eyre!(e))? {
|
||||||
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used different planner settings, try uninstalling the existing install").red());
|
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used different planner settings, try uninstalling the existing install with `{uninstall_command}`").red());
|
||||||
return Ok(ExitCode::FAILURE)
|
return Ok(ExitCode::FAILURE)
|
||||||
}
|
}
|
||||||
if existing_receipt.actions.iter().all(|v| v.state == ActionState::Completed) {
|
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed, try uninstalling (`{uninstall_command}`) and reinstalling if Nix isn't working").red());
|
||||||
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed, try uninstalling and reinstalling if Nix isn't working").red());
|
return Ok(ExitCode::FAILURE)
|
||||||
return Ok(ExitCode::FAILURE)
|
|
||||||
}
|
|
||||||
existing_receipt
|
|
||||||
} ,
|
} ,
|
||||||
None => {
|
None => {
|
||||||
planner.plan().await.map_err(|e| eyre!(e))?
|
planner.plan().await.map_err(|e| eyre!(e))?
|
||||||
|
@ -114,18 +117,19 @@ impl CommandExecute for Install {
|
||||||
let builtin_planner = BuiltinPlanner::from_common_settings(settings)
|
let builtin_planner = BuiltinPlanner::from_common_settings(settings)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| eyre::eyre!(e))?;
|
.map_err(|e| eyre::eyre!(e))?;
|
||||||
|
|
||||||
match existing_receipt {
|
match existing_receipt {
|
||||||
Some(existing_receipt) => {
|
Some(existing_receipt) => {
|
||||||
if existing_receipt.planner.typetag_name() != builtin_planner.typetag_name() {
|
if existing_receipt.planner.typetag_name() != builtin_planner.typetag_name() {
|
||||||
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used a different planner, try uninstalling the existing install").red());
|
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used a different planner, try uninstalling the existing install with `{uninstall_command}`").red());
|
||||||
return Ok(ExitCode::FAILURE)
|
return Ok(ExitCode::FAILURE)
|
||||||
}
|
}
|
||||||
if existing_receipt.planner.settings().map_err(|e| eyre!(e))? != builtin_planner.settings().map_err(|e| eyre!(e))? {
|
if existing_receipt.planner.settings().map_err(|e| eyre!(e))? != builtin_planner.settings().map_err(|e| eyre!(e))? {
|
||||||
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used different planner settings, try uninstalling the existing install").red());
|
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}` which used different planner settings, try uninstalling the existing install with `{uninstall_command}`").red());
|
||||||
return Ok(ExitCode::FAILURE)
|
return Ok(ExitCode::FAILURE)
|
||||||
}
|
}
|
||||||
if existing_receipt.actions.iter().all(|v| v.state == ActionState::Completed) {
|
if existing_receipt.actions.iter().all(|v| v.state == ActionState::Completed) {
|
||||||
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed, try uninstalling and reinstalling if Nix isn't working").red());
|
eprintln!("{}", format!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed, try uninstalling (`{uninstall_command}`) and reinstalling if Nix isn't working").red());
|
||||||
return Ok(ExitCode::FAILURE)
|
return Ok(ExitCode::FAILURE)
|
||||||
}
|
}
|
||||||
existing_receipt
|
existing_receipt
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
base::CreateDirectory,
|
base::{CreateDirectory, RemoveDirectory},
|
||||||
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
||||||
StatefulAction,
|
StatefulAction,
|
||||||
},
|
},
|
||||||
|
@ -88,6 +88,10 @@ impl Planner for Linux {
|
||||||
.await
|
.await
|
||||||
.map_err(PlannerError::Action)?
|
.map_err(PlannerError::Action)?
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
RemoveDirectory::plan(crate::settings::SCRATCH_DIR)
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?
|
||||||
|
.boxed(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ use tokio::process::Command;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
|
base::RemoveDirectory,
|
||||||
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
||||||
macos::CreateNixVolume,
|
macos::CreateNixVolume,
|
||||||
StatefulAction,
|
StatefulAction,
|
||||||
|
@ -145,6 +146,10 @@ impl Planner for Macos {
|
||||||
.await
|
.await
|
||||||
.map_err(PlannerError::Action)?
|
.map_err(PlannerError::Action)?
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
RemoveDirectory::plan(crate::settings::SCRATCH_DIR)
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?
|
||||||
|
.boxed(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
base::{CreateDirectory, CreateFile},
|
base::{CreateDirectory, CreateFile, RemoveDirectory},
|
||||||
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
||||||
linux::StartSystemdUnit,
|
linux::StartSystemdUnit,
|
||||||
Action, StatefulAction,
|
Action, StatefulAction,
|
||||||
|
@ -234,6 +234,10 @@ impl Planner for SteamDeck {
|
||||||
.await
|
.await
|
||||||
.map_err(PlannerError::Action)?
|
.map_err(PlannerError::Action)?
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
RemoveDirectory::plan(crate::settings::SCRATCH_DIR)
|
||||||
|
.await
|
||||||
|
.map_err(PlannerError::Action)?
|
||||||
|
.boxed(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ use std::collections::HashMap;
|
||||||
use clap::ArgAction;
|
use clap::ArgAction;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
pub const SCRATCH_DIR: &str = "/nix/temp-install-dir";
|
||||||
|
|
||||||
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for Linux x86_64
|
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for Linux x86_64
|
||||||
pub const NIX_X64_64_LINUX_URL: &str =
|
pub const NIX_X64_64_LINUX_URL: &str =
|
||||||
"https://releases.nixos.org/nix/nix-2.13.3/nix-2.13.3-x86_64-linux.tar.xz";
|
"https://releases.nixos.org/nix/nix-2.13.3/nix-2.13.3-x86_64-linux.tar.xz";
|
||||||
|
|
5
tests/fixtures/linux/linux.json
vendored
5
tests/fixtures/linux/linux.json
vendored
|
@ -758,7 +758,7 @@
|
||||||
},
|
},
|
||||||
"move_unpacked_nix": {
|
"move_unpacked_nix": {
|
||||||
"action": {
|
"action": {
|
||||||
"src": "/nix/temp-install-dir"
|
"unpacked_path": "/nix/temp-install-dir"
|
||||||
},
|
},
|
||||||
"state": "Uncompleted"
|
"state": "Uncompleted"
|
||||||
}
|
}
|
||||||
|
@ -775,7 +775,8 @@
|
||||||
"nixpkgs",
|
"nixpkgs",
|
||||||
"https://nixos.org/channels/nixpkgs-unstable"
|
"https://nixos.org/channels/nixpkgs-unstable"
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
"unpacked_path": "/nix/temp-install-dir"
|
||||||
},
|
},
|
||||||
"state": "Uncompleted"
|
"state": "Uncompleted"
|
||||||
},
|
},
|
||||||
|
|
5
tests/fixtures/linux/steam-deck.json
vendored
5
tests/fixtures/linux/steam-deck.json
vendored
|
@ -802,7 +802,7 @@
|
||||||
},
|
},
|
||||||
"move_unpacked_nix": {
|
"move_unpacked_nix": {
|
||||||
"action": {
|
"action": {
|
||||||
"src": "/nix/temp-install-dir"
|
"unpacked_path": "/nix/temp-install-dir"
|
||||||
},
|
},
|
||||||
"state": "Uncompleted"
|
"state": "Uncompleted"
|
||||||
}
|
}
|
||||||
|
@ -819,7 +819,8 @@
|
||||||
"nixpkgs",
|
"nixpkgs",
|
||||||
"https://nixos.org/channels/nixpkgs-unstable"
|
"https://nixos.org/channels/nixpkgs-unstable"
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
"unpacked_path": "/nix/temp-install-dir"
|
||||||
},
|
},
|
||||||
"state": "Uncompleted"
|
"state": "Uncompleted"
|
||||||
},
|
},
|
||||||
|
|
5
tests/fixtures/macos/macos.json
vendored
5
tests/fixtures/macos/macos.json
vendored
|
@ -823,7 +823,7 @@
|
||||||
},
|
},
|
||||||
"move_unpacked_nix": {
|
"move_unpacked_nix": {
|
||||||
"action": {
|
"action": {
|
||||||
"src": "/nix/temp-install-dir"
|
"unpacked_path": "/nix/temp-install-dir"
|
||||||
},
|
},
|
||||||
"state": "Uncompleted"
|
"state": "Uncompleted"
|
||||||
}
|
}
|
||||||
|
@ -840,7 +840,8 @@
|
||||||
"nixpkgs",
|
"nixpkgs",
|
||||||
"https://nixos.org/channels/nixpkgs-unstable"
|
"https://nixos.org/channels/nixpkgs-unstable"
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
"unpacked_path": "/nix/temp-install-dir"
|
||||||
},
|
},
|
||||||
"state": "Uncompleted"
|
"state": "Uncompleted"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue