forked from lix-project/lix-installer
parent
16e886f53b
commit
4ce8d94ac7
|
@ -5,11 +5,8 @@ use nix::unistd::{chown, Group, User};
|
|||
|
||||
use tokio::fs::{create_dir, remove_dir_all};
|
||||
|
||||
use crate::action::StatefulAction;
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, ActionState},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription, ActionState};
|
||||
use crate::action::{ActionError, StatefulAction};
|
||||
|
||||
/** Create a directory at the given location, optionally with an owning user, group, and mode.
|
||||
|
||||
|
@ -33,16 +30,16 @@ impl CreateDirectory {
|
|||
group: impl Into<Option<String>>,
|
||||
mode: impl Into<Option<u32>>,
|
||||
force_prune_on_revert: bool,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let path = path.as_ref();
|
||||
let user = user.into();
|
||||
let group = group.into();
|
||||
let mode = mode.into();
|
||||
|
||||
let action_state = if path.exists() {
|
||||
let metadata = tokio::fs::metadata(path).await.map_err(|e| {
|
||||
CreateDirectoryError::GettingMetadata(path.to_path_buf(), e).boxed()
|
||||
})?;
|
||||
let metadata = tokio::fs::metadata(path)
|
||||
.await
|
||||
.map_err(|e| ActionError::GettingMetadata(path.to_path_buf(), e))?;
|
||||
if metadata.is_dir() {
|
||||
tracing::debug!(
|
||||
"Creating directory `{}` already complete, skipping",
|
||||
|
@ -51,14 +48,7 @@ impl CreateDirectory {
|
|||
// TODO: Validate owner/group...
|
||||
ActionState::Skipped
|
||||
} else {
|
||||
return Err(CreateDirectoryError::Exists(std::io::Error::new(
|
||||
std::io::ErrorKind::AlreadyExists,
|
||||
format!(
|
||||
"Path `{}` already exists and is not directory",
|
||||
path.display()
|
||||
),
|
||||
))
|
||||
.boxed());
|
||||
return Err(ActionError::Exists(path.to_owned()));
|
||||
}
|
||||
} else {
|
||||
ActionState::Uncompleted
|
||||
|
@ -94,7 +84,7 @@ impl Action for CreateDirectory {
|
|||
group = self.group,
|
||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
path,
|
||||
user,
|
||||
|
@ -106,8 +96,8 @@ impl Action for CreateDirectory {
|
|||
let gid = if let Some(group) = group {
|
||||
Some(
|
||||
Group::from_name(group.as_str())
|
||||
.map_err(|e| CreateDirectoryError::GroupId(group.clone(), e).boxed())?
|
||||
.ok_or(CreateDirectoryError::NoGroup(group.clone()).boxed())?
|
||||
.map_err(|e| ActionError::GroupId(group.clone(), e))?
|
||||
.ok_or(ActionError::NoGroup(group.clone()))?
|
||||
.gid,
|
||||
)
|
||||
} else {
|
||||
|
@ -116,8 +106,8 @@ impl Action for CreateDirectory {
|
|||
let uid = if let Some(user) = user {
|
||||
Some(
|
||||
User::from_name(user.as_str())
|
||||
.map_err(|e| CreateDirectoryError::UserId(user.clone(), e).boxed())?
|
||||
.ok_or(CreateDirectoryError::NoUser(user.clone()).boxed())?
|
||||
.map_err(|e| ActionError::UserId(user.clone(), e))?
|
||||
.ok_or(ActionError::NoUser(user.clone()))?
|
||||
.uid,
|
||||
)
|
||||
} else {
|
||||
|
@ -126,15 +116,13 @@ impl Action for CreateDirectory {
|
|||
|
||||
create_dir(path.clone())
|
||||
.await
|
||||
.map_err(|e| CreateDirectoryError::Creating(path.clone(), e).boxed())?;
|
||||
chown(path, uid, gid).map_err(|e| CreateDirectoryError::Chown(path.clone(), e).boxed())?;
|
||||
.map_err(|e| ActionError::CreateDirectory(path.clone(), e))?;
|
||||
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
|
||||
|
||||
if let Some(mode) = mode {
|
||||
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
CreateDirectoryError::SetPermissions(*mode, path.to_owned(), e).boxed()
|
||||
})?;
|
||||
.map_err(|e| ActionError::SetPermissions(*mode, path.to_owned(), e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -168,7 +156,7 @@ impl Action for CreateDirectory {
|
|||
group = self.group,
|
||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
path,
|
||||
user: _,
|
||||
|
@ -179,13 +167,13 @@ impl Action for CreateDirectory {
|
|||
|
||||
let is_empty = path
|
||||
.read_dir()
|
||||
.map_err(|e| CreateDirectoryError::ReadDir(path.clone(), e).boxed())?
|
||||
.map_err(|e| ActionError::Read(path.clone(), e))?
|
||||
.next()
|
||||
.is_some();
|
||||
match (is_empty, force_prune_on_revert) {
|
||||
(true, _) | (false, true) => remove_dir_all(path.clone())
|
||||
.await
|
||||
.map_err(|e| CreateDirectoryError::Removing(path.clone(), e).boxed())?,
|
||||
.map_err(|e| ActionError::Remove(path.clone(), e))?,
|
||||
(false, false) => {
|
||||
tracing::debug!("Not removing `{}`, the folder is not empty", path.display());
|
||||
},
|
||||
|
@ -194,29 +182,3 @@ impl Action for CreateDirectory {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateDirectoryError {
|
||||
#[error(transparent)]
|
||||
Exists(std::io::Error),
|
||||
#[error("Creating directory `{0}`")]
|
||||
Creating(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Removing directory `{0}`")]
|
||||
Removing(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Getting metadata for {0}`")]
|
||||
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Reading directory `{0}``")]
|
||||
ReadDir(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Set mode `{0}` on `{1}`")]
|
||||
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Chowning directory `{0}`")]
|
||||
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
|
||||
#[error("Getting uid for user `{0}`")]
|
||||
UserId(String, #[source] nix::errno::Errno),
|
||||
#[error("Getting user `{0}`")]
|
||||
NoUser(String),
|
||||
#[error("Getting gid for group `{0}`")]
|
||||
GroupId(String, #[source] nix::errno::Errno),
|
||||
#[error("Getting group `{0}`")]
|
||||
NoGroup(String),
|
||||
}
|
||||
|
|
|
@ -6,10 +6,7 @@ use tokio::{
|
|||
io::AsyncWriteExt,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, StatefulAction},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||
|
||||
/** Create a file at the given location with the provided `buf`,
|
||||
optionally with an owning user, group, and mode.
|
||||
|
@ -36,11 +33,11 @@ impl CreateFile {
|
|||
mode: impl Into<Option<u32>>,
|
||||
buf: String,
|
||||
force: bool,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let path = path.as_ref().to_path_buf();
|
||||
|
||||
if path.exists() && !force {
|
||||
return Err(CreateFileError::Exists(path.to_path_buf()).boxed());
|
||||
return Err(ActionError::Exists(path.to_path_buf()));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
|
@ -71,7 +68,7 @@ impl Action for CreateFile {
|
|||
group = self.group,
|
||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
path,
|
||||
user,
|
||||
|
@ -91,17 +88,17 @@ impl Action for CreateFile {
|
|||
let mut file = options
|
||||
.open(&path)
|
||||
.await
|
||||
.map_err(|e| CreateFileError::OpenFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Open(path.to_owned(), e))?;
|
||||
|
||||
file.write_all(buf.as_bytes())
|
||||
.await
|
||||
.map_err(|e| CreateFileError::WriteFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
|
||||
|
||||
let gid = if let Some(group) = group {
|
||||
Some(
|
||||
Group::from_name(group.as_str())
|
||||
.map_err(|e| CreateFileError::GroupId(group.clone(), e).boxed())?
|
||||
.ok_or(CreateFileError::NoGroup(group.clone()).boxed())?
|
||||
.map_err(|e| ActionError::GroupId(group.clone(), e))?
|
||||
.ok_or(ActionError::NoGroup(group.clone()))?
|
||||
.gid,
|
||||
)
|
||||
} else {
|
||||
|
@ -110,14 +107,14 @@ impl Action for CreateFile {
|
|||
let uid = if let Some(user) = user {
|
||||
Some(
|
||||
User::from_name(user.as_str())
|
||||
.map_err(|e| CreateFileError::UserId(user.clone(), e).boxed())?
|
||||
.ok_or(CreateFileError::NoUser(user.clone()).boxed())?
|
||||
.map_err(|e| ActionError::UserId(user.clone(), e))?
|
||||
.ok_or(ActionError::NoUser(user.clone()))?
|
||||
.uid,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
chown(path, uid, gid).map_err(|e| CreateFileError::Chown(path.clone(), e).boxed())?;
|
||||
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -144,7 +141,7 @@ impl Action for CreateFile {
|
|||
group = self.group,
|
||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
path,
|
||||
user: _,
|
||||
|
@ -156,30 +153,8 @@ impl Action for CreateFile {
|
|||
|
||||
remove_file(&path)
|
||||
.await
|
||||
.map_err(|e| CreateFileError::RemoveFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Remove(path.to_owned(), e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateFileError {
|
||||
#[error("File exists `{0}`")]
|
||||
Exists(std::path::PathBuf),
|
||||
#[error("Remove file `{0}`")]
|
||||
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Open file `{0}`")]
|
||||
OpenFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Write file `{0}`")]
|
||||
WriteFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Getting uid for user `{0}`")]
|
||||
UserId(String, #[source] nix::errno::Errno),
|
||||
#[error("Getting user `{0}`")]
|
||||
NoUser(String),
|
||||
#[error("Getting gid for group `{0}`")]
|
||||
GroupId(String, #[source] nix::errno::Errno),
|
||||
#[error("Getting group `{0}`")]
|
||||
NoGroup(String),
|
||||
#[error("Chowning directory `{0}`")]
|
||||
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use tokio::process::Command;
|
||||
|
||||
use crate::action::ActionError;
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, StatefulAction},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
|
||||
/**
|
||||
Create an operating system level user group
|
||||
|
@ -43,7 +41,7 @@ impl Action for CreateGroup {
|
|||
user = self.name,
|
||||
gid = self.gid,
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { name, gid } = self;
|
||||
|
||||
use target_lexicon::OperatingSystem;
|
||||
|
@ -60,7 +58,8 @@ impl Action for CreateGroup {
|
|||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.status()
|
||||
.await?
|
||||
.await
|
||||
.map_err(ActionError::Command)?
|
||||
.success()
|
||||
{
|
||||
()
|
||||
|
@ -80,7 +79,7 @@ impl Action for CreateGroup {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateGroupError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
@ -91,7 +90,7 @@ impl Action for CreateGroup {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateGroupError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -112,7 +111,7 @@ impl Action for CreateGroup {
|
|||
user = self.name,
|
||||
gid = self.gid,
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self { name, gid: _ } = self;
|
||||
|
||||
use target_lexicon::OperatingSystem;
|
||||
|
@ -142,16 +141,10 @@ impl Action for CreateGroup {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateGroupError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateGroupError {
|
||||
#[error("Failed to execute command")]
|
||||
Command(#[source] std::io::Error),
|
||||
}
|
||||
|
|
|
@ -10,10 +10,7 @@ use tokio::{
|
|||
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, StatefulAction},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||
|
||||
/** Create a file at the given location with the provided `buf`,
|
||||
optionally with an owning user, group, and mode.
|
||||
|
@ -40,7 +37,7 @@ impl CreateOrAppendFile {
|
|||
group: impl Into<Option<String>>,
|
||||
mode: impl Into<Option<u32>>,
|
||||
buf: String,
|
||||
) -> Result<StatefulAction<Self>, CreateOrAppendFileError> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let path = path.as_ref().to_path_buf();
|
||||
|
||||
Ok(Self {
|
||||
|
@ -71,7 +68,7 @@ impl Action for CreateOrAppendFile {
|
|||
group = self.group,
|
||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
path,
|
||||
user,
|
||||
|
@ -86,21 +83,21 @@ impl Action for CreateOrAppendFile {
|
|||
.read(true)
|
||||
.open(&path)
|
||||
.await
|
||||
.map_err(|e| CreateOrAppendFileError::OpenFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Open(path.to_owned(), e))?;
|
||||
|
||||
file.seek(SeekFrom::End(0))
|
||||
.await
|
||||
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
|
||||
|
||||
file.write_all(buf.as_bytes())
|
||||
.await
|
||||
.map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
|
||||
|
||||
let gid = if let Some(group) = group {
|
||||
Some(
|
||||
Group::from_name(group.as_str())
|
||||
.map_err(|e| CreateOrAppendFileError::GroupId(group.clone(), e).boxed())?
|
||||
.ok_or(CreateOrAppendFileError::NoGroup(group.clone()).boxed())?
|
||||
.map_err(|e| ActionError::GroupId(group.clone(), e))?
|
||||
.ok_or(ActionError::NoGroup(group.clone()))?
|
||||
.gid,
|
||||
)
|
||||
} else {
|
||||
|
@ -109,8 +106,8 @@ impl Action for CreateOrAppendFile {
|
|||
let uid = if let Some(user) = user {
|
||||
Some(
|
||||
User::from_name(user.as_str())
|
||||
.map_err(|e| CreateOrAppendFileError::UserId(user.clone(), e).boxed())?
|
||||
.ok_or(CreateOrAppendFileError::NoUser(user.clone()).boxed())?
|
||||
.map_err(|e| ActionError::UserId(user.clone(), e))?
|
||||
.ok_or(ActionError::NoUser(user.clone()))?
|
||||
.uid,
|
||||
)
|
||||
} else {
|
||||
|
@ -120,13 +117,10 @@ impl Action for CreateOrAppendFile {
|
|||
if let Some(mode) = mode {
|
||||
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
CreateOrAppendFileError::SetPermissions(*mode, path.to_owned(), e).boxed()
|
||||
})?;
|
||||
.map_err(|e| ActionError::SetPermissions(*mode, path.to_owned(), e))?;
|
||||
}
|
||||
|
||||
chown(path, uid, gid)
|
||||
.map_err(|e| CreateOrAppendFileError::Chown(path.clone(), e).boxed())?;
|
||||
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -154,7 +148,7 @@ impl Action for CreateOrAppendFile {
|
|||
group = self.group,
|
||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
path,
|
||||
user: _,
|
||||
|
@ -168,12 +162,12 @@ impl Action for CreateOrAppendFile {
|
|||
.read(true)
|
||||
.open(&path)
|
||||
.await
|
||||
.map_err(|e| CreateOrAppendFileError::ReadFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Read(path.to_owned(), e))?;
|
||||
|
||||
let mut file_contents = String::default();
|
||||
file.read_to_string(&mut file_contents)
|
||||
.await
|
||||
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
|
||||
|
||||
if let Some(start) = file_contents.rfind(buf.as_str()) {
|
||||
let end = start + buf.len();
|
||||
|
@ -183,41 +177,15 @@ impl Action for CreateOrAppendFile {
|
|||
if buf.is_empty() {
|
||||
remove_file(&path)
|
||||
.await
|
||||
.map_err(|e| CreateOrAppendFileError::RemoveFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Remove(path.to_owned(), e))?;
|
||||
} else {
|
||||
file.seek(SeekFrom::Start(0))
|
||||
.await
|
||||
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
|
||||
file.write_all(file_contents.as_bytes())
|
||||
.await
|
||||
.map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateOrAppendFileError {
|
||||
#[error("Remove file `{0}`")]
|
||||
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Remove file `{0}`")]
|
||||
ReadFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Open file `{0}`")]
|
||||
OpenFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Write file `{0}`")]
|
||||
WriteFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Seek file `{0}`")]
|
||||
SeekFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Getting uid for user `{0}`")]
|
||||
UserId(String, #[source] nix::errno::Errno),
|
||||
#[error("Getting user `{0}`")]
|
||||
NoUser(String),
|
||||
#[error("Getting gid for group `{0}`")]
|
||||
GroupId(String, #[source] nix::errno::Errno),
|
||||
#[error("Getting group `{0}`")]
|
||||
NoGroup(String),
|
||||
#[error("Set mode `{0}` on `{1}`")]
|
||||
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Chowning directory `{0}`")]
|
||||
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use tokio::process::Command;
|
||||
|
||||
use crate::action::ActionError;
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, StatefulAction},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
|
||||
/**
|
||||
Create an operating system level user in the given group
|
||||
|
@ -55,7 +53,7 @@ impl Action for CreateUser {
|
|||
groupname = self.groupname,
|
||||
gid = self.gid,
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
name,
|
||||
uid,
|
||||
|
@ -80,7 +78,8 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.status()
|
||||
.await?
|
||||
.await
|
||||
.map_err(ActionError::Command)?
|
||||
.success()
|
||||
{
|
||||
()
|
||||
|
@ -92,7 +91,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
|
@ -106,7 +105,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
|
@ -120,7 +119,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
|
@ -134,7 +133,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
|
@ -148,7 +147,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
|
@ -162,7 +161,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
|
@ -170,7 +169,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
execute_command(
|
||||
Command::new("/usr/sbin/dseditgroup")
|
||||
.process_group(0)
|
||||
|
@ -183,7 +182,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
@ -212,7 +211,7 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -236,7 +235,7 @@ impl Action for CreateUser {
|
|||
uid = self.uid,
|
||||
gid = self.gid,
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
name,
|
||||
uid: _,
|
||||
|
@ -271,16 +270,10 @@ impl Action for CreateUser {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateUserError {
|
||||
#[error("Failed to execute command")]
|
||||
Command(#[source] std::io::Error),
|
||||
}
|
||||
|
|
|
@ -3,12 +3,7 @@ use std::path::PathBuf;
|
|||
use bytes::Buf;
|
||||
use reqwest::Url;
|
||||
|
||||
use tokio::task::JoinError;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, StatefulAction},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||
|
||||
/**
|
||||
Fetch a URL to the given path
|
||||
|
@ -21,7 +16,7 @@ pub struct FetchAndUnpackNix {
|
|||
|
||||
impl FetchAndUnpackNix {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(url: Url, dest: PathBuf) -> Result<StatefulAction<Self>, FetchUrlError> {
|
||||
pub async fn plan(url: Url, dest: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
|
||||
// TODO(@hoverbear): Check URL exists?
|
||||
// TODO(@hoverbear): Check tempdir exists
|
||||
|
||||
|
@ -44,16 +39,16 @@ impl Action for FetchAndUnpackNix {
|
|||
url = %self.url,
|
||||
dest = %self.dest.display(),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { url, dest } = self;
|
||||
|
||||
let res = reqwest::get(url.clone())
|
||||
.await
|
||||
.map_err(|e| FetchUrlError::Reqwest(e).boxed())?;
|
||||
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
|
||||
let bytes = res
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|e| FetchUrlError::Reqwest(e).boxed())?;
|
||||
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
|
||||
// TODO(@Hoverbear): Pick directory
|
||||
tracing::trace!("Unpacking tar.xz");
|
||||
let dest_clone = dest.clone();
|
||||
|
@ -62,7 +57,7 @@ impl Action for FetchAndUnpackNix {
|
|||
let mut archive = tar::Archive::new(decoder);
|
||||
archive
|
||||
.unpack(&dest_clone)
|
||||
.map_err(|e| FetchUrlError::Unarchive(e).boxed())?;
|
||||
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Unarchive(e))))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -75,7 +70,7 @@ impl Action for FetchAndUnpackNix {
|
|||
url = %self.url,
|
||||
dest = %self.dest.display(),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self { url: _, dest: _ } = self;
|
||||
|
||||
Ok(())
|
||||
|
@ -84,12 +79,6 @@ impl Action for FetchAndUnpackNix {
|
|||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum FetchUrlError {
|
||||
#[error("Joining spawned async task")]
|
||||
Join(
|
||||
#[source]
|
||||
#[from]
|
||||
JoinError,
|
||||
),
|
||||
#[error("Request error")]
|
||||
Reqwest(
|
||||
#[from]
|
||||
|
|
|
@ -9,11 +9,11 @@ mod fetch_and_unpack_nix;
|
|||
mod move_unpacked_nix;
|
||||
mod setup_default_profile;
|
||||
|
||||
pub use create_directory::{CreateDirectory, CreateDirectoryError};
|
||||
pub use create_file::{CreateFile, CreateFileError};
|
||||
pub use create_group::{CreateGroup, CreateGroupError};
|
||||
pub use create_or_append_file::{CreateOrAppendFile, CreateOrAppendFileError};
|
||||
pub use create_user::{CreateUser, CreateUserError};
|
||||
pub use create_directory::CreateDirectory;
|
||||
pub use create_file::CreateFile;
|
||||
pub use create_group::CreateGroup;
|
||||
pub use create_or_append_file::CreateOrAppendFile;
|
||||
pub use create_user::CreateUser;
|
||||
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
|
||||
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
||||
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, StatefulAction},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||
|
||||
const DEST: &str = "/nix/store";
|
||||
|
||||
|
@ -17,7 +14,7 @@ pub struct MoveUnpackedNix {
|
|||
|
||||
impl MoveUnpackedNix {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(src: PathBuf) -> Result<StatefulAction<Self>, MoveUnpackedNixError> {
|
||||
pub async fn plan(src: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
|
||||
// Note: Do NOT try to check for the src/dest since the installer creates those
|
||||
Ok(Self { src }.into())
|
||||
}
|
||||
|
@ -44,14 +41,14 @@ impl Action for MoveUnpackedNix {
|
|||
src = %self.src.display(),
|
||||
dest = DEST,
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { src } = self;
|
||||
|
||||
// TODO(@Hoverbear): I would like to make this less awful
|
||||
let found_nix_paths = glob::glob(&format!("{}/nix-*", src.display()))
|
||||
.map_err(|e| e.boxed())?
|
||||
.map_err(|e| ActionError::Custom(Box::new(e)))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| e.boxed())?;
|
||||
.map_err(|e| ActionError::Custom(Box::new(e)))?;
|
||||
assert_eq!(
|
||||
found_nix_paths.len(),
|
||||
1,
|
||||
|
@ -63,13 +60,11 @@ impl Action for MoveUnpackedNix {
|
|||
tracing::trace!(src = %src_store.display(), dest = %dest.display(), "Renaming");
|
||||
tokio::fs::rename(src_store.clone(), dest)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
MoveUnpackedNixError::Rename(src_store.clone(), dest.to_owned(), e).boxed()
|
||||
})?;
|
||||
.map_err(|e| ActionError::Rename(src_store.clone(), dest.to_owned(), e))?;
|
||||
|
||||
tokio::fs::remove_dir_all(src)
|
||||
.await
|
||||
.map_err(|e| MoveUnpackedNixError::Rename(src_store, dest.to_owned(), e).boxed())?;
|
||||
.map_err(|e| ActionError::Rename(src_store, dest.to_owned(), e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -82,7 +77,7 @@ impl Action for MoveUnpackedNix {
|
|||
src = %self.src.display(),
|
||||
dest = DEST,
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
// Noop
|
||||
Ok(())
|
||||
}
|
||||
|
@ -102,10 +97,4 @@ pub enum MoveUnpackedNixError {
|
|||
#[source]
|
||||
glob::GlobError,
|
||||
),
|
||||
#[error("Rename `{0}` to `{1}`")]
|
||||
Rename(
|
||||
std::path::PathBuf,
|
||||
std::path::PathBuf,
|
||||
#[source] std::io::Error,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use crate::{action::StatefulAction, execute_command, set_env, BoxableError};
|
||||
use crate::{
|
||||
action::{ActionError, StatefulAction},
|
||||
execute_command, set_env,
|
||||
};
|
||||
|
||||
use glob::glob;
|
||||
|
||||
|
@ -16,9 +19,7 @@ pub struct SetupDefaultProfile {
|
|||
|
||||
impl SetupDefaultProfile {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(
|
||||
channels: Vec<String>,
|
||||
) -> Result<StatefulAction<Self>, SetupDefaultProfileError> {
|
||||
pub async fn plan(channels: Vec<String>) -> Result<StatefulAction<Self>, ActionError> {
|
||||
Ok(Self { channels }.into())
|
||||
}
|
||||
}
|
||||
|
@ -37,15 +38,15 @@ impl Action for SetupDefaultProfile {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
channels = %self.channels.join(","),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { channels } = self;
|
||||
|
||||
// Find an `nix` package
|
||||
let nix_pkg_glob = "/nix/store/*-nix-*";
|
||||
let mut found_nix_pkg = None;
|
||||
for entry in
|
||||
glob(nix_pkg_glob).map_err(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())?
|
||||
{
|
||||
for entry in glob(nix_pkg_glob).map_err(|e| {
|
||||
ActionError::Custom(Box::new(SetupDefaultProfileError::GlobPatternError(e)))
|
||||
})? {
|
||||
match entry {
|
||||
Ok(path) => {
|
||||
// TODO(@Hoverbear): Should probably ensure is unique
|
||||
|
@ -58,15 +59,17 @@ impl Action for SetupDefaultProfile {
|
|||
let nix_pkg = if let Some(nix_pkg) = found_nix_pkg {
|
||||
nix_pkg
|
||||
} else {
|
||||
return Err(Box::new(SetupDefaultProfileError::NoNssCacert)); // TODO(@hoverbear): Fix this error
|
||||
return Err(ActionError::Custom(Box::new(
|
||||
SetupDefaultProfileError::NoNix,
|
||||
)));
|
||||
};
|
||||
|
||||
// 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;
|
||||
for entry in glob(nss_ca_cert_pkg_glob)
|
||||
.map_err(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())?
|
||||
{
|
||||
for entry in glob(nss_ca_cert_pkg_glob).map_err(|e| {
|
||||
ActionError::Custom(Box::new(SetupDefaultProfileError::GlobPatternError(e)))
|
||||
})? {
|
||||
match entry {
|
||||
Ok(path) => {
|
||||
// TODO(@Hoverbear): Should probably ensure is unique
|
||||
|
@ -79,7 +82,9 @@ impl Action for SetupDefaultProfile {
|
|||
let nss_ca_cert_pkg = if let Some(nss_ca_cert_pkg) = found_nss_ca_cert_pkg {
|
||||
nss_ca_cert_pkg
|
||||
} else {
|
||||
return Err(Box::new(SetupDefaultProfileError::NoNssCacert));
|
||||
return Err(ActionError::Custom(Box::new(
|
||||
SetupDefaultProfileError::NoNssCacert,
|
||||
)));
|
||||
};
|
||||
|
||||
// Install `nix` itself into the store
|
||||
|
@ -93,7 +98,9 @@ impl Action for SetupDefaultProfile {
|
|||
.stdin(std::process::Stdio::null())
|
||||
.env(
|
||||
"HOME",
|
||||
dirs::home_dir().ok_or_else(|| SetupDefaultProfileError::NoRootHome.boxed())?,
|
||||
dirs::home_dir().ok_or_else(|| {
|
||||
ActionError::Custom(Box::new(SetupDefaultProfileError::NoRootHome))
|
||||
})?,
|
||||
)
|
||||
.env(
|
||||
"NIX_SSL_CERT_FILE",
|
||||
|
@ -101,7 +108,7 @@ impl Action for SetupDefaultProfile {
|
|||
), /* This is apparently load bearing... */
|
||||
)
|
||||
.await
|
||||
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
|
||||
// Install `nss-cacert` into the store
|
||||
// execute_command(
|
||||
|
@ -136,7 +143,7 @@ impl Action for SetupDefaultProfile {
|
|||
|
||||
execute_command(&mut command)
|
||||
.await
|
||||
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -152,7 +159,7 @@ impl Action for SetupDefaultProfile {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
channels = %self.channels.join(","),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
std::env::remove_var("NIX_SSL_CERT_FILE");
|
||||
|
||||
Ok(())
|
||||
|
@ -175,8 +182,8 @@ pub enum SetupDefaultProfileError {
|
|||
),
|
||||
#[error("Unarchived Nix store did not appear to include a `nss-cacert` location")]
|
||||
NoNssCacert,
|
||||
#[error("Failed to execute command")]
|
||||
Command(#[source] std::io::Error),
|
||||
#[error("Unarchived Nix store did not appear to include a `nix` location")]
|
||||
NoNix,
|
||||
#[error("No root home found to place channel configuration in")]
|
||||
NoRootHome,
|
||||
}
|
||||
|
|
|
@ -3,11 +3,10 @@ use crate::{
|
|||
base::SetupDefaultProfile,
|
||||
common::{ConfigureShellProfile, PlaceChannelConfiguration, PlaceNixConfiguration},
|
||||
linux::ConfigureNixDaemonService,
|
||||
Action, ActionDescription, StatefulAction,
|
||||
Action, ActionDescription, ActionError, StatefulAction,
|
||||
},
|
||||
channel_value::ChannelValue,
|
||||
settings::CommonSettings,
|
||||
BoxableError,
|
||||
};
|
||||
|
||||
use reqwest::Url;
|
||||
|
@ -26,9 +25,7 @@ pub struct ConfigureNix {
|
|||
|
||||
impl ConfigureNix {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(
|
||||
settings: &CommonSettings,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let channels: Vec<(String, Url)> = settings
|
||||
.channels
|
||||
.iter()
|
||||
|
@ -36,9 +33,7 @@ impl ConfigureNix {
|
|||
.collect();
|
||||
|
||||
let setup_default_profile =
|
||||
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect())
|
||||
.await
|
||||
.map_err(|e| e.boxed())?;
|
||||
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect()).await?;
|
||||
|
||||
let configure_shell_profile = if settings.modify_profile {
|
||||
Some(ConfigureShellProfile::plan().await?)
|
||||
|
@ -93,7 +88,7 @@ impl Action for ConfigureNix {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
setup_default_profile,
|
||||
configure_nix_daemon_service,
|
||||
|
@ -143,7 +138,7 @@ impl Action for ConfigureNix {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
setup_default_profile,
|
||||
configure_nix_daemon_service,
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use crate::action::base::{CreateOrAppendFile, CreateOrAppendFileError};
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
use crate::BoxableError;
|
||||
use crate::action::base::CreateOrAppendFile;
|
||||
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||
|
||||
use std::path::Path;
|
||||
use tokio::task::{JoinError, JoinSet};
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
const PROFILE_TARGETS: &[&str] = &[
|
||||
"/etc/bashrc",
|
||||
|
@ -25,7 +24,7 @@ pub struct ConfigureShellProfile {
|
|||
|
||||
impl ConfigureShellProfile {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||
let mut create_or_append_files = Vec::default();
|
||||
for profile_target in PROFILE_TARGETS {
|
||||
let path = Path::new(profile_target);
|
||||
|
@ -42,11 +41,8 @@ impl ConfigureShellProfile {
|
|||
# End Nix\n
|
||||
\n",
|
||||
);
|
||||
create_or_append_files.push(
|
||||
CreateOrAppendFile::plan(path, None, None, 0o0644, buf)
|
||||
.await
|
||||
.map_err(|e| e.boxed())?,
|
||||
);
|
||||
create_or_append_files
|
||||
.push(CreateOrAppendFile::plan(path, None, None, 0o0644, buf).await?);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
|
@ -71,7 +67,7 @@ impl Action for ConfigureShellProfile {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_or_append_files,
|
||||
} = self;
|
||||
|
@ -83,10 +79,7 @@ impl Action for ConfigureShellProfile {
|
|||
let mut create_or_append_file_clone = create_or_append_file.clone();
|
||||
let _abort_handle = set.spawn(async move {
|
||||
create_or_append_file_clone.try_execute().await?;
|
||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((
|
||||
idx,
|
||||
create_or_append_file_clone,
|
||||
))
|
||||
Result::<_, ActionError>::Ok((idx, create_or_append_file_clone))
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -95,8 +88,8 @@ impl Action for ConfigureShellProfile {
|
|||
Ok(Ok((idx, create_or_append_file))) => {
|
||||
create_or_append_files[idx] = create_or_append_file
|
||||
},
|
||||
Ok(Err(e)) => errors.push(e),
|
||||
Err(e) => return Err(e.boxed()),
|
||||
Ok(Err(e)) => errors.push(Box::new(e)),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -104,7 +97,7 @@ impl Action for ConfigureShellProfile {
|
|||
if errors.len() == 1 {
|
||||
return Err(errors.into_iter().next().unwrap().into());
|
||||
} else {
|
||||
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
|
||||
return Err(ActionError::Children(errors));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,22 +112,19 @@ impl Action for ConfigureShellProfile {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_or_append_files,
|
||||
} = self;
|
||||
|
||||
let mut set = JoinSet::new();
|
||||
let mut errors = Vec::default();
|
||||
let mut errors: Vec<Box<ActionError>> = Vec::default();
|
||||
|
||||
for (idx, create_or_append_file) in create_or_append_files.iter().enumerate() {
|
||||
let mut create_or_append_file_clone = create_or_append_file.clone();
|
||||
let _abort_handle = set.spawn(async move {
|
||||
create_or_append_file_clone.try_revert().await?;
|
||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((
|
||||
idx,
|
||||
create_or_append_file_clone,
|
||||
))
|
||||
Result::<_, _>::Ok((idx, create_or_append_file_clone))
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -143,8 +133,8 @@ impl Action for ConfigureShellProfile {
|
|||
Ok(Ok((idx, create_or_append_file))) => {
|
||||
create_or_append_files[idx] = create_or_append_file
|
||||
},
|
||||
Ok(Err(e)) => errors.push(e),
|
||||
Err(e) => return Err(e.boxed()),
|
||||
Ok(Err(e)) => errors.push(Box::new(e)),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -152,34 +142,10 @@ impl Action for ConfigureShellProfile {
|
|||
if errors.len() == 1 {
|
||||
return Err(errors.into_iter().next().unwrap().into());
|
||||
} else {
|
||||
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
|
||||
return Err(ActionError::Children(errors));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ConfigureShellProfileError {
|
||||
#[error("Creating or appending to file")]
|
||||
CreateOrAppendFile(
|
||||
#[from]
|
||||
#[source]
|
||||
CreateOrAppendFileError,
|
||||
),
|
||||
#[error("Multiple errors: {}", .0.iter().map(|v| {
|
||||
if let Some(source) = v.source() {
|
||||
format!("{v} ({source})")
|
||||
} else {
|
||||
format!("{v}")
|
||||
}
|
||||
}).collect::<Vec<_>>().join(" & "))]
|
||||
MultipleCreateOrAppendFile(Vec<Box<dyn std::error::Error + Send + Sync>>),
|
||||
#[error("Joining spawned async task")]
|
||||
Join(
|
||||
#[source]
|
||||
#[from]
|
||||
JoinError,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::action::base::{CreateDirectory, CreateDirectoryError};
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
use crate::action::base::CreateDirectory;
|
||||
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||
|
||||
const PATHS: &[&str] = &[
|
||||
"/nix/var",
|
||||
|
@ -27,7 +27,7 @@ pub struct CreateNixTree {
|
|||
|
||||
impl CreateNixTree {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||
let mut create_directories = Vec::default();
|
||||
for path in PATHS {
|
||||
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
|
||||
|
@ -65,7 +65,7 @@ impl Action for CreateNixTree {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { create_directories } = self;
|
||||
|
||||
// Just do sequential since parallelizing this will have little benefit
|
||||
|
@ -97,7 +97,7 @@ impl Action for CreateNixTree {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self { create_directories } = self;
|
||||
|
||||
// Just do sequential since parallelizing this will have little benefit
|
||||
|
@ -108,13 +108,3 @@ impl Action for CreateNixTree {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateNixTreeError {
|
||||
#[error("Creating directory")]
|
||||
CreateDirectory(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateDirectoryError,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use crate::{
|
||||
action::{
|
||||
base::{CreateGroup, CreateGroupError, CreateUser, CreateUserError},
|
||||
Action, ActionDescription, StatefulAction,
|
||||
base::{CreateGroup, CreateUser},
|
||||
Action, ActionDescription, ActionError, StatefulAction,
|
||||
},
|
||||
settings::CommonSettings,
|
||||
BoxableError,
|
||||
};
|
||||
use tokio::task::{JoinError, JoinSet};
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateUsersAndGroups {
|
||||
|
@ -21,9 +20,7 @@ pub struct CreateUsersAndGroups {
|
|||
|
||||
impl CreateUsersAndGroups {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(
|
||||
settings: CommonSettings,
|
||||
) -> Result<StatefulAction<Self>, CreateUsersAndGroupsError> {
|
||||
pub async fn plan(settings: CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||
// TODO(@hoverbear): CHeck if it exist, error if so
|
||||
let create_group = CreateGroup::plan(
|
||||
settings.nix_build_group_name.clone(),
|
||||
|
@ -101,7 +98,7 @@ impl Action for CreateUsersAndGroups {
|
|||
nix_build_user_prefix = self.nix_build_user_prefix,
|
||||
nix_build_user_id_base = self.nix_build_user_id_base,
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_users,
|
||||
create_group,
|
||||
|
@ -130,7 +127,7 @@ impl Action for CreateUsersAndGroups {
|
|||
},
|
||||
_ => {
|
||||
let mut set = JoinSet::new();
|
||||
let mut errors: Vec<Box<dyn std::error::Error + Send + Sync>> = Vec::new();
|
||||
let mut errors: Vec<Box<ActionError>> = Vec::new();
|
||||
for (idx, create_user) in create_users.iter_mut().enumerate() {
|
||||
let mut create_user_clone = create_user.clone();
|
||||
let _abort_handle = set.spawn(async move {
|
||||
|
@ -142,8 +139,8 @@ impl Action for CreateUsersAndGroups {
|
|||
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)?,
|
||||
Ok(Err(e)) => errors.push(Box::new(e)),
|
||||
Err(e) => return Err(ActionError::Join(e))?,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -151,7 +148,7 @@ impl Action for CreateUsersAndGroups {
|
|||
if errors.len() == 1 {
|
||||
return Err(errors.into_iter().next().unwrap().into());
|
||||
} else {
|
||||
return Err(CreateUsersAndGroupsError::CreateUsers(errors).boxed());
|
||||
return Err(ActionError::Children(errors));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -198,7 +195,7 @@ impl Action for CreateUsersAndGroups {
|
|||
nix_build_user_prefix = self.nix_build_user_prefix,
|
||||
nix_build_user_id_base = self.nix_build_user_id_base,
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_users,
|
||||
create_group,
|
||||
|
@ -216,15 +213,15 @@ impl Action for CreateUsersAndGroups {
|
|||
let mut create_user_clone = create_user.clone();
|
||||
let _abort_handle = set.spawn(async move {
|
||||
create_user_clone.try_revert().await?;
|
||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((idx, create_user_clone))
|
||||
Result::<_, ActionError>::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.boxed())?,
|
||||
Ok(Err(e)) => errors.push(Box::new(e)),
|
||||
Err(e) => return Err(ActionError::Join(e))?,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -232,7 +229,7 @@ impl Action for CreateUsersAndGroups {
|
|||
if errors.len() == 1 {
|
||||
return Err(errors.into_iter().next().unwrap().into());
|
||||
} else {
|
||||
return Err(CreateUsersAndGroupsError::CreateUsers(errors).boxed());
|
||||
return Err(ActionError::Children(errors));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,33 +239,3 @@ impl Action for CreateUsersAndGroups {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateUsersAndGroupsError {
|
||||
#[error("Creating user")]
|
||||
CreateUser(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateUserError,
|
||||
),
|
||||
#[error("Multiple errors: {}", .0.iter().map(|v| {
|
||||
if let Some(source) = v.source() {
|
||||
format!("{v} ({source})")
|
||||
} else {
|
||||
format!("{v}")
|
||||
}
|
||||
}).collect::<Vec<_>>().join(" & "))]
|
||||
CreateUsers(Vec<Box<dyn std::error::Error + Send + Sync>>),
|
||||
#[error("Creating group")]
|
||||
CreateGroup(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateGroupError,
|
||||
),
|
||||
#[error("Joining spawned async task")]
|
||||
Join(
|
||||
#[source]
|
||||
#[from]
|
||||
JoinError,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ mod provision_nix;
|
|||
|
||||
pub use configure_nix::ConfigureNix;
|
||||
pub use configure_shell_profile::ConfigureShellProfile;
|
||||
pub use create_nix_tree::{CreateNixTree, CreateNixTreeError};
|
||||
pub use create_users_and_groups::{CreateUsersAndGroups, CreateUsersAndGroupsError};
|
||||
pub use create_nix_tree::CreateNixTree;
|
||||
pub use create_users_and_groups::CreateUsersAndGroups;
|
||||
pub use place_channel_configuration::{PlaceChannelConfiguration, PlaceChannelConfigurationError};
|
||||
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationError};
|
||||
pub use provision_nix::{ProvisionNix, ProvisionNixError};
|
||||
pub use place_nix_configuration::PlaceNixConfiguration;
|
||||
pub use provision_nix::ProvisionNix;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use crate::action::base::{CreateFile, CreateFileError};
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, StatefulAction},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::base::CreateFile;
|
||||
use crate::action::ActionError;
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
use reqwest::Url;
|
||||
|
||||
/**
|
||||
|
@ -19,7 +17,7 @@ impl PlaceChannelConfiguration {
|
|||
pub async fn plan(
|
||||
channels: Vec<(String, Url)>,
|
||||
force: bool,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let buf = channels
|
||||
.iter()
|
||||
.map(|(name, url)| format!("{} {}", url, name))
|
||||
|
@ -27,7 +25,9 @@ impl PlaceChannelConfiguration {
|
|||
.join("\n");
|
||||
let create_file = CreateFile::plan(
|
||||
dirs::home_dir()
|
||||
.ok_or_else(|| PlaceChannelConfigurationError::NoRootHome.boxed())?
|
||||
.ok_or_else(|| {
|
||||
ActionError::Custom(Box::new(PlaceChannelConfigurationError::NoRootHome))
|
||||
})?
|
||||
.join(".nix-channels"),
|
||||
None,
|
||||
None,
|
||||
|
@ -61,7 +61,7 @@ impl Action for PlaceChannelConfiguration {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_file,
|
||||
channels: _,
|
||||
|
@ -85,7 +85,7 @@ impl Action for PlaceChannelConfiguration {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_file,
|
||||
channels: _,
|
||||
|
@ -99,12 +99,6 @@ impl Action for PlaceChannelConfiguration {
|
|||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PlaceChannelConfigurationError {
|
||||
#[error("Creating file")]
|
||||
CreateFile(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateFileError,
|
||||
),
|
||||
#[error("No root home found to place channel configuration in")]
|
||||
NoRootHome,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::action::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
use crate::action::base::{CreateDirectory, CreateFile};
|
||||
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||
|
||||
const NIX_CONF_FOLDER: &str = "/etc/nix";
|
||||
const NIX_CONF: &str = "/etc/nix/nix.conf";
|
||||
|
@ -19,7 +19,7 @@ impl PlaceNixConfiguration {
|
|||
nix_build_group_name: String,
|
||||
extra_conf: Vec<String>,
|
||||
force: bool,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let buf = format!(
|
||||
"\
|
||||
{extra_conf}\n\
|
||||
|
@ -61,7 +61,7 @@ impl Action for PlaceNixConfiguration {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_file,
|
||||
create_directory,
|
||||
|
@ -84,7 +84,7 @@ impl Action for PlaceNixConfiguration {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_file,
|
||||
create_directory,
|
||||
|
@ -96,19 +96,3 @@ impl Action for PlaceNixConfiguration {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PlaceNixConfigurationError {
|
||||
#[error("Creating file")]
|
||||
CreateFile(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateFileError,
|
||||
),
|
||||
#[error("Creating directory")]
|
||||
CreateDirectory(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateDirectoryError,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
use super::{CreateNixTree, CreateUsersAndGroups};
|
||||
use crate::{
|
||||
action::{
|
||||
base::{
|
||||
CreateDirectoryError, FetchAndUnpackNix, FetchUrlError, MoveUnpackedNix,
|
||||
MoveUnpackedNixError,
|
||||
},
|
||||
Action, ActionDescription, StatefulAction,
|
||||
base::{FetchAndUnpackNix, MoveUnpackedNix},
|
||||
Action, ActionDescription, ActionError, StatefulAction,
|
||||
},
|
||||
settings::CommonSettings,
|
||||
BoxableError,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use tokio::task::JoinError;
|
||||
|
||||
use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroups, CreateUsersAndGroupsError};
|
||||
|
||||
/**
|
||||
Place Nix and it's requirements onto the target
|
||||
|
@ -27,22 +21,16 @@ pub struct ProvisionNix {
|
|||
|
||||
impl ProvisionNix {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(
|
||||
settings: &CommonSettings,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let fetch_nix = FetchAndUnpackNix::plan(
|
||||
settings.nix_package_url.clone(),
|
||||
PathBuf::from("/nix/temp-install-dir"),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| e.boxed())?;
|
||||
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone())
|
||||
.await
|
||||
.map_err(|e| e.boxed())?;
|
||||
.await?;
|
||||
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone()).await?;
|
||||
let create_nix_tree = CreateNixTree::plan().await?;
|
||||
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir"))
|
||||
.await
|
||||
.map_err(|e| e.boxed())?;
|
||||
let move_unpacked_nix =
|
||||
MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?;
|
||||
Ok(Self {
|
||||
fetch_nix,
|
||||
create_users_and_group,
|
||||
|
@ -78,7 +66,7 @@ impl Action for ProvisionNix {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
fetch_nix,
|
||||
create_nix_tree,
|
||||
|
@ -90,13 +78,13 @@ impl Action for ProvisionNix {
|
|||
let mut fetch_nix_clone = fetch_nix.clone();
|
||||
let fetch_nix_handle = tokio::task::spawn(async {
|
||||
fetch_nix_clone.try_execute().await?;
|
||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok(fetch_nix_clone)
|
||||
Result::<_, ActionError>::Ok(fetch_nix_clone)
|
||||
});
|
||||
|
||||
create_users_and_group.try_execute().await?;
|
||||
create_nix_tree.try_execute().await?;
|
||||
|
||||
*fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??;
|
||||
*fetch_nix = fetch_nix_handle.await.map_err(ActionError::Join)??;
|
||||
move_unpacked_nix.try_execute().await?;
|
||||
|
||||
Ok(())
|
||||
|
@ -119,7 +107,7 @@ impl Action for ProvisionNix {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
fetch_nix,
|
||||
create_nix_tree,
|
||||
|
@ -131,7 +119,7 @@ impl Action for ProvisionNix {
|
|||
let mut fetch_nix_clone = fetch_nix.clone();
|
||||
let fetch_nix_handle = tokio::task::spawn(async {
|
||||
fetch_nix_clone.try_revert().await?;
|
||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok(fetch_nix_clone)
|
||||
Result::<_, ActionError>::Ok(fetch_nix_clone)
|
||||
});
|
||||
|
||||
if let Err(err) = create_users_and_group.try_revert().await {
|
||||
|
@ -143,49 +131,9 @@ impl Action for ProvisionNix {
|
|||
return Err(err);
|
||||
}
|
||||
|
||||
*fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??;
|
||||
*fetch_nix = fetch_nix_handle.await.map_err(ActionError::Join)??;
|
||||
move_unpacked_nix.try_revert().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ProvisionNixError {
|
||||
#[error("Fetching Nix")]
|
||||
FetchNix(
|
||||
#[source]
|
||||
#[from]
|
||||
FetchUrlError,
|
||||
),
|
||||
#[error("Joining spawned async task")]
|
||||
Join(
|
||||
#[source]
|
||||
#[from]
|
||||
JoinError,
|
||||
),
|
||||
#[error("Creating directory")]
|
||||
CreateDirectory(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateDirectoryError,
|
||||
),
|
||||
#[error("Creating users and group")]
|
||||
CreateUsersAndGroup(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateUsersAndGroupsError,
|
||||
),
|
||||
#[error("Creating nix tree")]
|
||||
CreateNixTree(
|
||||
#[source]
|
||||
#[from]
|
||||
CreateNixTreeError,
|
||||
),
|
||||
#[error("Moving unpacked nix")]
|
||||
MoveUnpackedNix(
|
||||
#[source]
|
||||
#[from]
|
||||
MoveUnpackedNixError,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::action::StatefulAction;
|
||||
use crate::action::{ActionError, StatefulAction};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription};
|
||||
|
||||
/**
|
||||
Bootstrap and kickstart an APFS volume
|
||||
|
@ -20,9 +17,7 @@ pub struct BootstrapApfsVolume {
|
|||
|
||||
impl BootstrapApfsVolume {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan(path: impl AsRef<Path>) -> Result<StatefulAction<Self>, ActionError> {
|
||||
Ok(Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
}
|
||||
|
@ -44,7 +39,7 @@ impl Action for BootstrapApfsVolume {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
path = %self.path.display(),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { path } = self;
|
||||
|
||||
execute_command(
|
||||
|
@ -55,7 +50,7 @@ impl Action for BootstrapApfsVolume {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
execute_command(
|
||||
Command::new("launchctl")
|
||||
.process_group(0)
|
||||
|
@ -63,7 +58,7 @@ impl Action for BootstrapApfsVolume {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -78,7 +73,7 @@ impl Action for BootstrapApfsVolume {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
path = %self.path.display(),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self { path } = self;
|
||||
|
||||
execute_command(
|
||||
|
@ -89,7 +84,7 @@ impl Action for BootstrapApfsVolume {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::action::StatefulAction;
|
||||
use crate::action::{ActionError, StatefulAction};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateApfsVolume {
|
||||
|
@ -23,7 +20,7 @@ impl CreateApfsVolume {
|
|||
disk: impl AsRef<Path>,
|
||||
name: String,
|
||||
case_sensitive: bool,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
Ok(Self {
|
||||
disk: disk.as_ref().to_path_buf(),
|
||||
name,
|
||||
|
@ -53,7 +50,7 @@ impl Action for CreateApfsVolume {
|
|||
name = %self.name,
|
||||
case_sensitive = %self.case_sensitive,
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
disk,
|
||||
name,
|
||||
|
@ -78,7 +75,7 @@ impl Action for CreateApfsVolume {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateVolumeError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -99,7 +96,7 @@ impl Action for CreateApfsVolume {
|
|||
name = %self.name,
|
||||
case_sensitive = %self.case_sensitive,
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
disk: _,
|
||||
name,
|
||||
|
@ -113,7 +110,7 @@ impl Action for CreateApfsVolume {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| CreateVolumeError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
use crate::{
|
||||
action::{
|
||||
base::{CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError},
|
||||
darwin::{
|
||||
BootstrapApfsVolume, BootstrapVolumeError, CreateApfsVolume, CreateSyntheticObjects,
|
||||
CreateSyntheticObjectsError, CreateVolumeError, EnableOwnership, EnableOwnershipError,
|
||||
EncryptApfsVolume, EncryptVolumeError, UnmountApfsVolume, UnmountVolumeError,
|
||||
},
|
||||
Action, ActionDescription, StatefulAction,
|
||||
use crate::action::{
|
||||
base::{CreateFile, CreateOrAppendFile},
|
||||
darwin::{
|
||||
BootstrapApfsVolume, CreateApfsVolume, CreateSyntheticObjects, EnableOwnership,
|
||||
EncryptApfsVolume, UnmountApfsVolume,
|
||||
},
|
||||
BoxableError,
|
||||
Action, ActionDescription, ActionError, StatefulAction,
|
||||
};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
|
@ -43,7 +39,7 @@ impl CreateNixVolume {
|
|||
name: String,
|
||||
case_sensitive: bool,
|
||||
encrypt: bool,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let disk = disk.as_ref();
|
||||
let create_or_append_synthetic_conf = CreateOrAppendFile::plan(
|
||||
"/etc/synthetic.conf",
|
||||
|
@ -53,7 +49,7 @@ impl CreateNixVolume {
|
|||
"nix\n".into(), /* The newline is required otherwise it segfaults */
|
||||
)
|
||||
.await
|
||||
.map_err(|e| e.boxed())?;
|
||||
.map_err(|e| ActionError::Child(Box::new(e)))?;
|
||||
|
||||
let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
|
||||
|
||||
|
@ -69,7 +65,7 @@ impl CreateNixVolume {
|
|||
format!("NAME=\"{name}\" /nix apfs rw,noauto,nobrowse,suid,owners"),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| e.boxed())?;
|
||||
.map_err(|e| ActionError::Child(Box::new(e)))?;
|
||||
|
||||
let encrypt_volume = if encrypt {
|
||||
Some(EncryptApfsVolume::plan(disk, &name).await?)
|
||||
|
@ -155,7 +151,7 @@ impl Action for CreateNixVolume {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(destination,))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
disk: _,
|
||||
name: _,
|
||||
|
@ -193,7 +189,7 @@ impl Action for CreateNixVolume {
|
|||
.stdout(std::process::Stdio::null())
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| CreateApfsVolumeError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
if status.success() || retry_tokens == 0 {
|
||||
break;
|
||||
} else {
|
||||
|
@ -218,7 +214,7 @@ impl Action for CreateNixVolume {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(disk, name))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
disk: _,
|
||||
name: _,
|
||||
|
@ -253,25 +249,3 @@ impl Action for CreateNixVolume {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateApfsVolumeError {
|
||||
#[error(transparent)]
|
||||
CreateFile(#[from] CreateFileError),
|
||||
#[error(transparent)]
|
||||
DarwinBootstrapVolume(#[from] BootstrapVolumeError),
|
||||
#[error(transparent)]
|
||||
DarwinCreateSyntheticObjects(#[from] CreateSyntheticObjectsError),
|
||||
#[error(transparent)]
|
||||
DarwinCreateVolume(#[from] CreateVolumeError),
|
||||
#[error(transparent)]
|
||||
DarwinEnableOwnership(#[from] EnableOwnershipError),
|
||||
#[error(transparent)]
|
||||
DarwinEncryptVolume(#[from] EncryptVolumeError),
|
||||
#[error(transparent)]
|
||||
DarwinUnmountVolume(#[from] UnmountVolumeError),
|
||||
#[error(transparent)]
|
||||
CreateOrAppendFile(#[from] CreateOrAppendFileError),
|
||||
#[error("Failed to execute command")]
|
||||
Command(#[source] std::io::Error),
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use tokio::process::Command;
|
|||
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||
|
||||
/// Create the synthetic objects defined in `/etc/syntethic.conf`
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
|
@ -10,7 +10,7 @@ pub struct CreateSyntheticObjects;
|
|||
|
||||
impl CreateSyntheticObjects {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||
Ok(Self.into())
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ impl Action for CreateSyntheticObjects {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields())]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
// Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
|
||||
execute_command(
|
||||
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
|
||||
|
@ -60,7 +60,7 @@ impl Action for CreateSyntheticObjects {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields())]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
// Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
|
||||
execute_command(
|
||||
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
|
||||
|
|
|
@ -3,14 +3,11 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::action::StatefulAction;
|
||||
use crate::action::{ActionError, StatefulAction};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::action::{Action, ActionDescription};
|
||||
use crate::os::darwin::DiskUtilOutput;
|
||||
use crate::{
|
||||
action::{Action, ActionDescription},
|
||||
BoxableError,
|
||||
};
|
||||
|
||||
/**
|
||||
Enable ownership on a volume
|
||||
|
@ -22,9 +19,7 @@ pub struct EnableOwnership {
|
|||
|
||||
impl EnableOwnership {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan(path: impl AsRef<Path>) -> Result<StatefulAction<Self>, ActionError> {
|
||||
Ok(Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
}
|
||||
|
@ -46,7 +41,7 @@ impl Action for EnableOwnership {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
path = %self.path.display(),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { path } = self;
|
||||
|
||||
let should_enable_ownership = {
|
||||
|
@ -57,7 +52,8 @@ impl Action for EnableOwnership {
|
|||
.arg(&path)
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await?
|
||||
.await
|
||||
.map_err(ActionError::Command)?
|
||||
.stdout;
|
||||
let the_plist: DiskUtilOutput = plist::from_reader(Cursor::new(buf)).unwrap();
|
||||
|
||||
|
@ -73,7 +69,7 @@ impl Action for EnableOwnership {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| EnableOwnershipError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -86,7 +82,7 @@ impl Action for EnableOwnership {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
path = %self.path.display(),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
// noop
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::{
|
||||
action::{darwin::NIX_VOLUME_MOUNTD_DEST, Action, ActionDescription, StatefulAction},
|
||||
action::{
|
||||
darwin::NIX_VOLUME_MOUNTD_DEST, Action, ActionDescription, ActionError, StatefulAction,
|
||||
},
|
||||
execute_command,
|
||||
};
|
||||
use rand::Rng;
|
||||
|
@ -20,7 +22,7 @@ impl EncryptApfsVolume {
|
|||
pub async fn plan(
|
||||
disk: impl AsRef<Path>,
|
||||
name: impl AsRef<str>,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let name = name.as_ref().to_owned();
|
||||
Ok(Self {
|
||||
name,
|
||||
|
@ -48,7 +50,7 @@ impl Action for EncryptApfsVolume {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
disk = %self.disk.display(),
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { disk, name } = self;
|
||||
|
||||
// Generate a random password.
|
||||
|
@ -69,7 +71,9 @@ impl Action for EncryptApfsVolume {
|
|||
|
||||
let disk_str = disk.to_str().expect("Could not turn disk into string"); /* Should not reasonably ever fail */
|
||||
|
||||
execute_command(Command::new("/usr/sbin/diskutil").arg("mount").arg(&name)).await?;
|
||||
execute_command(Command::new("/usr/sbin/diskutil").arg("mount").arg(&name))
|
||||
.await
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
// Add the password to the user keychain so they can unlock it later.
|
||||
execute_command(
|
||||
|
@ -99,7 +103,8 @@ impl Action for EncryptApfsVolume {
|
|||
"/Library/Keychains/System.keychain",
|
||||
]),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
// Encrypt the mounted volume
|
||||
execute_command(Command::new("/usr/sbin/diskutil").process_group(0).args([
|
||||
|
@ -111,7 +116,8 @@ impl Action for EncryptApfsVolume {
|
|||
"-passphrase",
|
||||
password.as_str(),
|
||||
]))
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
execute_command(
|
||||
Command::new("/usr/sbin/diskutil")
|
||||
|
@ -120,7 +126,8 @@ impl Action for EncryptApfsVolume {
|
|||
.arg("force")
|
||||
.arg(&name),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -138,7 +145,7 @@ impl Action for EncryptApfsVolume {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
disk = %self.disk.display(),
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self { disk, name } = self;
|
||||
|
||||
let disk_str = disk.to_str().expect("Could not turn disk into string"); /* Should not reasonably ever fail */
|
||||
|
@ -162,14 +169,9 @@ impl Action for EncryptApfsVolume {
|
|||
.as_str(),
|
||||
]),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum EncryptVolumeError {
|
||||
#[error("Failed to execute command")]
|
||||
Command(#[source] std::io::Error),
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
use tokio::process::Command;
|
||||
|
||||
use crate::action::StatefulAction;
|
||||
use crate::action::{ActionError, StatefulAction};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription};
|
||||
|
||||
/**
|
||||
Kickstart a `launchctl` service
|
||||
|
@ -18,9 +15,7 @@ pub struct KickstartLaunchctlService {
|
|||
|
||||
impl KickstartLaunchctlService {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(
|
||||
unit: String,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan(unit: String) -> Result<StatefulAction<Self>, ActionError> {
|
||||
Ok(Self { unit }.into())
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +35,7 @@ impl Action for KickstartLaunchctlService {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
unit = %self.unit,
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { unit } = self;
|
||||
|
||||
execute_command(
|
||||
|
@ -52,7 +47,7 @@ impl Action for KickstartLaunchctlService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| KickstartLaunchctlServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -64,14 +59,8 @@ impl Action for KickstartLaunchctlService {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
unit = %self.unit,
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
// noop
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum KickstartLaunchctlServiceError {
|
||||
#[error("Failed to execute command")]
|
||||
Command(#[source] std::io::Error),
|
||||
}
|
||||
|
|
|
@ -12,9 +12,9 @@ mod unmount_apfs_volume;
|
|||
|
||||
pub use bootstrap_apfs_volume::{BootstrapApfsVolume, BootstrapVolumeError};
|
||||
pub use create_apfs_volume::{CreateApfsVolume, CreateVolumeError};
|
||||
pub use create_nix_volume::{CreateApfsVolumeError, CreateNixVolume, NIX_VOLUME_MOUNTD_DEST};
|
||||
pub use create_nix_volume::{CreateNixVolume, NIX_VOLUME_MOUNTD_DEST};
|
||||
pub use create_synthetic_objects::{CreateSyntheticObjects, CreateSyntheticObjectsError};
|
||||
pub use enable_ownership::{EnableOwnership, EnableOwnershipError};
|
||||
pub use encrypt_apfs_volume::{EncryptApfsVolume, EncryptVolumeError};
|
||||
pub use kickstart_launchctl_service::{KickstartLaunchctlService, KickstartLaunchctlServiceError};
|
||||
pub use unmount_apfs_volume::{UnmountApfsVolume, UnmountVolumeError};
|
||||
pub use encrypt_apfs_volume::EncryptApfsVolume;
|
||||
pub use kickstart_launchctl_service::KickstartLaunchctlService;
|
||||
pub use unmount_apfs_volume::UnmountApfsVolume;
|
||||
|
|
|
@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::action::StatefulAction;
|
||||
use crate::action::{ActionError, StatefulAction};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription};
|
||||
|
||||
/**
|
||||
Unmount an APFS volume
|
||||
|
@ -24,7 +21,7 @@ impl UnmountApfsVolume {
|
|||
pub async fn plan(
|
||||
disk: impl AsRef<Path>,
|
||||
name: String,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let disk = disk.as_ref().to_owned();
|
||||
Ok(Self { disk, name }.into())
|
||||
}
|
||||
|
@ -45,7 +42,7 @@ impl Action for UnmountApfsVolume {
|
|||
disk = %self.disk.display(),
|
||||
name = %self.name,
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { disk: _, name } = self;
|
||||
|
||||
execute_command(
|
||||
|
@ -56,7 +53,7 @@ impl Action for UnmountApfsVolume {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| UnmountVolumeError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -69,7 +66,7 @@ impl Action for UnmountApfsVolume {
|
|||
disk = %self.disk.display(),
|
||||
name = %self.name,
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self { disk: _, name } = self;
|
||||
|
||||
execute_command(
|
||||
|
@ -80,14 +77,8 @@ impl Action for UnmountApfsVolume {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| UnmountVolumeError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Command(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum UnmountVolumeError {
|
||||
#[error("Failed to execute command")]
|
||||
Command(#[source] std::io::Error),
|
||||
}
|
||||
|
|
|
@ -4,13 +4,10 @@ use target_lexicon::OperatingSystem;
|
|||
use tokio::fs::remove_file;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::action::StatefulAction;
|
||||
use crate::action::{ActionError, StatefulAction};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription};
|
||||
|
||||
const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
|
||||
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
|
||||
|
@ -26,7 +23,7 @@ pub struct ConfigureNixDaemonService {}
|
|||
|
||||
impl ConfigureNixDaemonService {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX {
|
||||
major: _,
|
||||
|
@ -36,7 +33,9 @@ impl ConfigureNixDaemonService {
|
|||
| OperatingSystem::Darwin => (),
|
||||
_ => {
|
||||
if !Path::new("/run/systemd/system").exists() {
|
||||
return Err(ConfigureNixDaemonServiceError::InitNotSupported.boxed());
|
||||
return Err(ActionError::Custom(Box::new(
|
||||
ConfigureNixDaemonServiceError::InitNotSupported,
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -64,7 +63,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {} = self;
|
||||
|
||||
match OperatingSystem::host() {
|
||||
|
@ -78,12 +77,11 @@ impl Action for ConfigureNixDaemonService {
|
|||
tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
ConfigureNixDaemonServiceError::Copy(
|
||||
ActionError::Copy(
|
||||
src.to_path_buf(),
|
||||
PathBuf::from(DARWIN_NIX_DAEMON_DEST),
|
||||
e,
|
||||
)
|
||||
.boxed()
|
||||
})?;
|
||||
|
||||
execute_command(
|
||||
|
@ -94,19 +92,18 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
},
|
||||
_ => {
|
||||
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
|
||||
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
ConfigureNixDaemonServiceError::Symlink(
|
||||
ActionError::Symlink(
|
||||
PathBuf::from(TMPFILES_SRC),
|
||||
PathBuf::from(TMPFILES_DEST),
|
||||
e,
|
||||
)
|
||||
.boxed()
|
||||
})?;
|
||||
|
||||
execute_command(
|
||||
|
@ -117,7 +114,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
|
@ -127,7 +124,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
|
@ -137,7 +134,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
|
@ -146,7 +143,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
|
@ -156,7 +153,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.arg("nix-daemon.socket"),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -176,7 +173,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX {
|
||||
major: _,
|
||||
|
@ -191,7 +188,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.arg(DARWIN_NIX_DAEMON_DEST),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
},
|
||||
_ => {
|
||||
// We separate stop and disable (instead of using `--now`) to avoid cases where the service isn't started, but is enabled.
|
||||
|
@ -209,7 +206,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
}
|
||||
|
||||
if socket_is_enabled {
|
||||
|
@ -220,7 +217,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
}
|
||||
|
||||
if service_is_active {
|
||||
|
@ -231,7 +228,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
}
|
||||
|
||||
if service_is_enabled {
|
||||
|
@ -242,7 +239,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
}
|
||||
|
||||
execute_command(
|
||||
|
@ -253,12 +250,11 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
|
||||
remove_file(TMPFILES_DEST).await.map_err(|e| {
|
||||
ConfigureNixDaemonServiceError::RemoveFile(PathBuf::from(TMPFILES_DEST), e)
|
||||
.boxed()
|
||||
})?;
|
||||
remove_file(TMPFILES_DEST)
|
||||
.await
|
||||
.map_err(|e| ActionError::Remove(PathBuf::from(TMPFILES_DEST), e))?;
|
||||
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
|
@ -267,7 +263,7 @@ impl Action for ConfigureNixDaemonService {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||
.map_err(ActionError::Command)?;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -277,34 +273,17 @@ impl Action for ConfigureNixDaemonService {
|
|||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ConfigureNixDaemonServiceError {
|
||||
#[error("Symlinking from `{0}` to `{1}`")]
|
||||
Symlink(
|
||||
std::path::PathBuf,
|
||||
std::path::PathBuf,
|
||||
#[source] std::io::Error,
|
||||
),
|
||||
#[error("Set mode `{0}` on `{1}`")]
|
||||
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Command failed to execute")]
|
||||
Command(#[source] std::io::Error),
|
||||
#[error("Remove file `{0}`")]
|
||||
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Copying file `{0}` to `{1}`")]
|
||||
Copy(
|
||||
std::path::PathBuf,
|
||||
std::path::PathBuf,
|
||||
#[source] std::io::Error,
|
||||
),
|
||||
#[error("No supported init system found")]
|
||||
InitNotSupported,
|
||||
}
|
||||
|
||||
async fn is_active(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn is_active(unit: &str) -> Result<bool, ActionError> {
|
||||
let output = Command::new("systemctl")
|
||||
.arg("is-active")
|
||||
.arg(unit)
|
||||
.output()
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ActionError::Command)?;
|
||||
if String::from_utf8(output.stdout)?.starts_with("active") {
|
||||
tracing::trace!(%unit, "Is active");
|
||||
Ok(true)
|
||||
|
@ -314,12 +293,13 @@ async fn is_active(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send
|
|||
}
|
||||
}
|
||||
|
||||
async fn is_enabled(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn is_enabled(unit: &str) -> Result<bool, ActionError> {
|
||||
let output = Command::new("systemctl")
|
||||
.arg("is-enabled")
|
||||
.arg(unit)
|
||||
.output()
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ActionError::Command)?;
|
||||
let stdout = String::from_utf8(output.stdout)?;
|
||||
if stdout.starts_with("enabled") || stdout.starts_with("linked") {
|
||||
tracing::trace!(%unit, "Is enabled");
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
use tokio::process::Command;
|
||||
|
||||
use crate::action::{ActionState, StatefulAction};
|
||||
use crate::action::{ActionError, ActionState, StatefulAction};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription},
|
||||
BoxableError,
|
||||
};
|
||||
use crate::action::{Action, ActionDescription};
|
||||
|
||||
/**
|
||||
Start a given systemd unit
|
||||
|
@ -18,9 +15,7 @@ pub struct StartSystemdUnit {
|
|||
|
||||
impl StartSystemdUnit {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(
|
||||
unit: impl AsRef<str>,
|
||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan(unit: impl AsRef<str>) -> Result<StatefulAction<Self>, ActionError> {
|
||||
Ok(StatefulAction {
|
||||
action: Self {
|
||||
unit: unit.as_ref().to_string(),
|
||||
|
@ -44,7 +39,7 @@ impl Action for StartSystemdUnit {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
unit = %self.unit,
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self { unit, .. } = self;
|
||||
|
||||
// TODO(@Hoverbear): Handle proxy vars
|
||||
|
@ -57,7 +52,7 @@ impl Action for StartSystemdUnit {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -72,7 +67,7 @@ impl Action for StartSystemdUnit {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
unit = %self.unit,
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self { unit, .. } = self;
|
||||
|
||||
execute_command(
|
||||
|
@ -83,7 +78,7 @@ impl Action for StartSystemdUnit {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
|
||||
|
||||
// We do both to avoid an error doing `disable --now` if the user did stop it already somehow.
|
||||
execute_command(
|
||||
|
@ -94,7 +89,7 @@ impl Action for StartSystemdUnit {
|
|||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
|
||||
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ use harmonic::{
|
|||
InstallPlan,
|
||||
settings::{CommonSettings, InstallSettingsError},
|
||||
planner::{Planner, PlannerError, linux::SteamDeck},
|
||||
action::{Action, StatefulAction, ActionDescription},
|
||||
action::{Action, ActionError, StatefulAction, ActionDescription},
|
||||
};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
|
@ -59,7 +59,7 @@ pub struct MyAction {}
|
|||
|
||||
impl MyAction {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||
Ok(Self {}.into())
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ impl Action for MyAction {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
// Tracing fields...
|
||||
))]
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
// Execute steps ...
|
||||
Ok(())
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ impl Action for MyAction {
|
|||
#[tracing::instrument(skip_all, fields(
|
||||
// Tracing fields...
|
||||
))]
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
// Revert steps...
|
||||
Ok(())
|
||||
}
|
||||
|
@ -158,6 +158,8 @@ pub mod linux;
|
|||
mod stateful;
|
||||
|
||||
pub use stateful::{ActionState, StatefulAction};
|
||||
use std::error::Error;
|
||||
use tokio::task::JoinError;
|
||||
|
||||
/// An action which can be reverted or completed, with an action state
|
||||
///
|
||||
|
@ -186,13 +188,13 @@ pub trait Action: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
|
|||
/// If this action calls sub-[`Action`]s, care should be taken to call [`try_execute`][StatefulAction::try_execute], not [`execute`][Action::execute], so that [`ActionState`] is handled correctly and tracing is done.
|
||||
///
|
||||
/// This is called by [`InstallPlan::install`](crate::InstallPlan::install) through [`StatefulAction::try_execute`] which handles tracing as well as if the action needs to execute based on its `action_state`.
|
||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
||||
async fn execute(&mut self) -> Result<(), ActionError>;
|
||||
/// Perform any revert steps
|
||||
///
|
||||
/// If this action calls sub-[`Action`]s, care should be taken to call [`try_revert`][StatefulAction::try_revert], not [`revert`][Action::revert], so that [`ActionState`] is handled correctly and tracing is done.
|
||||
///
|
||||
/// /// This is called by [`InstallPlan::uninstall`](crate::InstallPlan::uninstall) through [`StatefulAction::try_revert`] which handles tracing as well as if the action needs to revert based on its `action_state`.
|
||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
||||
async fn revert(&mut self) -> Result<(), ActionError>;
|
||||
|
||||
fn stateful(self) -> StatefulAction<Self>
|
||||
where
|
||||
|
@ -203,7 +205,7 @@ pub trait Action: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
|
|||
state: ActionState::Uncompleted,
|
||||
}
|
||||
}
|
||||
// They should also have an `async fn plan(args...) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>>;`
|
||||
// They should also have an `async fn plan(args...) -> Result<StatefulAction<Self>, ActionError>;`
|
||||
}
|
||||
|
||||
dyn_clone::clone_trait_object!(Action);
|
||||
|
@ -225,3 +227,85 @@ impl ActionDescription {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error occurring during an action
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ActionError {
|
||||
/// A custom error
|
||||
#[error(transparent)]
|
||||
Custom(Box<dyn std::error::Error + Send + Sync>),
|
||||
/// A child error
|
||||
#[error(transparent)]
|
||||
Child(#[from] Box<ActionError>),
|
||||
/// Several child errors
|
||||
#[error("Multiple errors: {}", .0.iter().map(|v| {
|
||||
if let Some(source) = v.source() {
|
||||
format!("{v} ({source})")
|
||||
} else {
|
||||
format!("{v}")
|
||||
}
|
||||
}).collect::<Vec<_>>().join(" & "))]
|
||||
Children(Vec<Box<ActionError>>),
|
||||
/// The path already exists
|
||||
#[error("Path exists `{0}`")]
|
||||
Exists(std::path::PathBuf),
|
||||
#[error("Getting metadata for {0}`")]
|
||||
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Creating directory `{0}`")]
|
||||
CreateDirectory(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Symlinking from `{0}` to `{1}`")]
|
||||
Symlink(
|
||||
std::path::PathBuf,
|
||||
std::path::PathBuf,
|
||||
#[source] std::io::Error,
|
||||
),
|
||||
#[error("Set mode `{0}` on `{1}`")]
|
||||
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Remove file `{0}`")]
|
||||
Remove(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Copying file `{0}` to `{1}`")]
|
||||
Copy(
|
||||
std::path::PathBuf,
|
||||
std::path::PathBuf,
|
||||
#[source] std::io::Error,
|
||||
),
|
||||
#[error("Rename `{0}` to `{1}`")]
|
||||
Rename(
|
||||
std::path::PathBuf,
|
||||
std::path::PathBuf,
|
||||
#[source] std::io::Error,
|
||||
),
|
||||
#[error("Remove path `{0}`")]
|
||||
Read(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Open path `{0}`")]
|
||||
Open(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Write path `{0}`")]
|
||||
Write(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Seek path `{0}`")]
|
||||
Seek(std::path::PathBuf, #[source] std::io::Error),
|
||||
#[error("Getting uid for user `{0}`")]
|
||||
UserId(String, #[source] nix::errno::Errno),
|
||||
#[error("Getting user `{0}`")]
|
||||
NoUser(String),
|
||||
#[error("Getting gid for group `{0}`")]
|
||||
GroupId(String, #[source] nix::errno::Errno),
|
||||
#[error("Getting group `{0}`")]
|
||||
NoGroup(String),
|
||||
#[error("Chowning path `{0}`")]
|
||||
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
|
||||
/// Failed to execute command
|
||||
#[error("Failed to execute command")]
|
||||
Command(#[source] std::io::Error),
|
||||
#[error("Joining spawned async task")]
|
||||
Join(
|
||||
#[source]
|
||||
#[from]
|
||||
JoinError,
|
||||
),
|
||||
#[error("String from UTF-8 error")]
|
||||
FromUtf8(
|
||||
#[source]
|
||||
#[from]
|
||||
std::string::FromUtf8Error,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{Action, ActionDescription};
|
||||
use super::{Action, ActionDescription, ActionError};
|
||||
|
||||
/// A wrapper around an [`Action`](crate::action::Action) which tracks the [`ActionState`] and
|
||||
/// handles some tracing output
|
||||
|
@ -44,7 +44,7 @@ impl StatefulAction<Box<dyn Action>> {
|
|||
/// Perform any execution steps
|
||||
///
|
||||
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing
|
||||
pub async fn try_execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn try_execute(&mut self) -> Result<(), ActionError> {
|
||||
match self.state {
|
||||
ActionState::Completed => {
|
||||
tracing::trace!(
|
||||
|
@ -70,7 +70,7 @@ impl StatefulAction<Box<dyn Action>> {
|
|||
/// Perform any revert steps
|
||||
///
|
||||
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing
|
||||
pub async fn try_revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn try_revert(&mut self) -> Result<(), ActionError> {
|
||||
match self.state {
|
||||
ActionState::Uncompleted => {
|
||||
tracing::trace!(
|
||||
|
@ -129,7 +129,7 @@ where
|
|||
/// Perform any execution steps
|
||||
///
|
||||
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing
|
||||
pub async fn try_execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn try_execute(&mut self) -> Result<(), ActionError> {
|
||||
match self.state {
|
||||
ActionState::Completed => {
|
||||
tracing::trace!(
|
||||
|
@ -155,7 +155,7 @@ where
|
|||
/// Perform any revert steps
|
||||
///
|
||||
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing
|
||||
pub async fn try_revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn try_revert(&mut self) -> Result<(), ActionError> {
|
||||
match self.state {
|
||||
ActionState::Uncompleted => {
|
||||
tracing::trace!(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::{planner::PlannerError, settings::InstallSettingsError};
|
||||
use crate::{action::ActionError, planner::PlannerError, settings::InstallSettingsError};
|
||||
|
||||
/// An error occurring during a call defined in this crate
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -10,7 +10,7 @@ pub enum HarmonicError {
|
|||
Action(
|
||||
#[source]
|
||||
#[from]
|
||||
Box<dyn std::error::Error + Send + Sync>,
|
||||
ActionError,
|
||||
),
|
||||
/// An error while writing the [`InstallPlan`](crate::InstallPlan)
|
||||
#[error("Recording install receipt")]
|
||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -112,14 +112,3 @@ fn set_env(k: impl AsRef<OsStr>, v: impl AsRef<OsStr>) {
|
|||
tracing::trace!("Setting env");
|
||||
std::env::set_var(k.as_ref(), v.as_ref());
|
||||
}
|
||||
|
||||
trait BoxableError: std::error::Error + Send + Sync {
|
||||
fn boxed(self) -> Box<dyn std::error::Error + Send + Sync>
|
||||
where
|
||||
Self: Sized + 'static,
|
||||
{
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> BoxableError for E where E: std::error::Error + Send + Sized + Sync {}
|
||||
|
|
|
@ -80,7 +80,9 @@ pub mod linux;
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
action::StatefulAction, settings::InstallSettingsError, Action, HarmonicError, InstallPlan,
|
||||
action::{ActionError, StatefulAction},
|
||||
settings::InstallSettingsError,
|
||||
Action, HarmonicError, InstallPlan,
|
||||
};
|
||||
|
||||
/// Something which can be used to plan out an [`InstallPlan`]
|
||||
|
@ -165,7 +167,11 @@ pub enum PlannerError {
|
|||
UnsupportedArchitecture(target_lexicon::Triple),
|
||||
/// Error executing action
|
||||
#[error("Error executing action")]
|
||||
Action(#[source] Box<dyn std::error::Error + Send + Sync>),
|
||||
Action(
|
||||
#[source]
|
||||
#[from]
|
||||
ActionError,
|
||||
),
|
||||
/// An [`InstallSettingsError`]
|
||||
#[error(transparent)]
|
||||
InstallSettings(#[from] InstallSettingsError),
|
||||
|
|
Loading…
Reference in a new issue