forked from lix-project/lix-installer
Explore planner pattern using steam deck example
This commit is contained in:
parent
144af153f6
commit
64e7423a0a
|
@ -14,6 +14,7 @@ pub struct CreateDirectory {
|
||||||
group: String,
|
group: String,
|
||||||
mode: u32,
|
mode: u32,
|
||||||
action_state: ActionState,
|
action_state: ActionState,
|
||||||
|
force_prune_on_revert: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateDirectory {
|
impl CreateDirectory {
|
||||||
|
@ -23,23 +24,37 @@ impl CreateDirectory {
|
||||||
user: String,
|
user: String,
|
||||||
group: String,
|
group: String,
|
||||||
mode: u32,
|
mode: u32,
|
||||||
force: bool,
|
force_prune_on_revert: bool,
|
||||||
) -> Result<Self, CreateDirectoryError> {
|
) -> Result<Self, CreateDirectoryError> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
||||||
if path.exists() && !force {
|
let action_state = if path.exists() {
|
||||||
return Err(CreateDirectoryError::Exists(std::io::Error::new(
|
let metadata = tokio::fs::metadata(path)
|
||||||
std::io::ErrorKind::AlreadyExists,
|
.await
|
||||||
format!("Directory `{}` already exists", path.display()),
|
.map_err(|e| CreateDirectoryError::GettingMetadata(path.to_path_buf(), e))?;
|
||||||
)));
|
if metadata.is_dir() {
|
||||||
}
|
// TODO: Validate owner/group...
|
||||||
|
ActionState::Completed
|
||||||
|
} else {
|
||||||
|
return Err(CreateDirectoryError::Exists(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::AlreadyExists,
|
||||||
|
format!(
|
||||||
|
"Path `{}` already exists and is not directory",
|
||||||
|
path.display()
|
||||||
|
),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ActionState::Uncompleted
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
user,
|
user,
|
||||||
group,
|
group,
|
||||||
mode,
|
mode,
|
||||||
action_state: ActionState::Uncompleted,
|
force_prune_on_revert,
|
||||||
|
action_state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +69,7 @@ impl Actionable for CreateDirectory {
|
||||||
user,
|
user,
|
||||||
group,
|
group,
|
||||||
mode,
|
mode,
|
||||||
|
force_prune_on_revert: _,
|
||||||
action_state: _,
|
action_state: _,
|
||||||
} = &self;
|
} = &self;
|
||||||
if self.action_state == ActionState::Completed {
|
if self.action_state == ActionState::Completed {
|
||||||
|
@ -81,6 +97,7 @@ impl Actionable for CreateDirectory {
|
||||||
user,
|
user,
|
||||||
group,
|
group,
|
||||||
mode,
|
mode,
|
||||||
|
force_prune_on_revert: _,
|
||||||
action_state,
|
action_state,
|
||||||
} = self;
|
} = self;
|
||||||
if *action_state == ActionState::Completed {
|
if *action_state == ActionState::Completed {
|
||||||
|
@ -118,13 +135,22 @@ impl Actionable for CreateDirectory {
|
||||||
user: _,
|
user: _,
|
||||||
group: _,
|
group: _,
|
||||||
mode: _,
|
mode: _,
|
||||||
|
force_prune_on_revert,
|
||||||
action_state: _,
|
action_state: _,
|
||||||
} = &self;
|
} = &self;
|
||||||
if self.action_state == ActionState::Uncompleted {
|
if self.action_state == ActionState::Uncompleted {
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
} else {
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Remove the directory `{}`", path.display()),
|
format!(
|
||||||
|
"Remove the directory `{}`{}",
|
||||||
|
path.display(),
|
||||||
|
if *force_prune_on_revert {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
" if no other contents exists"
|
||||||
|
}
|
||||||
|
),
|
||||||
vec![],
|
vec![],
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
|
@ -142,6 +168,7 @@ impl Actionable for CreateDirectory {
|
||||||
user: _,
|
user: _,
|
||||||
group: _,
|
group: _,
|
||||||
mode: _,
|
mode: _,
|
||||||
|
force_prune_on_revert,
|
||||||
action_state,
|
action_state,
|
||||||
} = self;
|
} = self;
|
||||||
if *action_state == ActionState::Uncompleted {
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
@ -151,9 +178,18 @@ impl Actionable for CreateDirectory {
|
||||||
tracing::debug!("Removing directory");
|
tracing::debug!("Removing directory");
|
||||||
|
|
||||||
tracing::trace!(path = %path.display(), "Removing directory");
|
tracing::trace!(path = %path.display(), "Removing directory");
|
||||||
remove_dir_all(path.clone())
|
|
||||||
.await
|
let is_empty = path
|
||||||
.map_err(|e| Self::Error::Removing(path.clone(), e))?;
|
.read_dir()
|
||||||
|
.map_err(|e| CreateDirectoryError::ReadDir(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| Self::Error::Removing(path.clone(), e))?,
|
||||||
|
(false, false) => {},
|
||||||
|
};
|
||||||
|
|
||||||
tracing::trace!("Removed directory");
|
tracing::trace!("Removed directory");
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
|
@ -185,6 +221,20 @@ pub enum CreateDirectoryError {
|
||||||
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
std::io::Error,
|
std::io::Error,
|
||||||
),
|
),
|
||||||
|
#[error("Getting metadata for {0}`")]
|
||||||
|
GettingMetadata(
|
||||||
|
std::path::PathBuf,
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
#[error("Reading directory `{0}``")]
|
||||||
|
ReadDir(
|
||||||
|
std::path::PathBuf,
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
#[error("Set mode `{0}` on `{1}`")]
|
#[error("Set mode `{0}` on `{1}`")]
|
||||||
SetPermissions(
|
SetPermissions(
|
||||||
u32,
|
u32,
|
||||||
|
|
|
@ -10,6 +10,7 @@ mod fetch_nix;
|
||||||
mod move_unpacked_nix;
|
mod move_unpacked_nix;
|
||||||
mod setup_default_profile;
|
mod setup_default_profile;
|
||||||
mod start_systemd_unit;
|
mod start_systemd_unit;
|
||||||
|
mod systemd_sysext_merge;
|
||||||
|
|
||||||
pub use configure_nix_daemon_service::{ConfigureNixDaemonService, ConfigureNixDaemonServiceError};
|
pub use configure_nix_daemon_service::{ConfigureNixDaemonService, ConfigureNixDaemonServiceError};
|
||||||
pub use create_directory::{CreateDirectory, CreateDirectoryError};
|
pub use create_directory::{CreateDirectory, CreateDirectoryError};
|
||||||
|
@ -21,3 +22,4 @@ pub use fetch_nix::{FetchNix, FetchNixError};
|
||||||
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
||||||
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
||||||
pub use start_systemd_unit::{StartSystemdUnit, StartSystemdUnitError};
|
pub use start_systemd_unit::{StartSystemdUnit, StartSystemdUnitError};
|
||||||
|
pub use systemd_sysext_merge::{SystemdSysextMerge, SystemdSysextMergeError};
|
||||||
|
|
120
src/actions/base/systemd_sysext_merge.rs
Normal file
120
src/actions/base/systemd_sysext_merge.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::execute_command;
|
||||||
|
|
||||||
|
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct SystemdSysextMerge {
|
||||||
|
device: PathBuf,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemdSysextMerge {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(device: PathBuf) -> Result<Self, SystemdSysextMergeError> {
|
||||||
|
Ok(Self {
|
||||||
|
device,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for SystemdSysextMerge {
|
||||||
|
type Error = SystemdSysextMergeError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
let Self {
|
||||||
|
action_state,
|
||||||
|
device,
|
||||||
|
} = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
format!("Run `systemd-sysext merge `{}`", device.display()),
|
||||||
|
vec![],
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(
|
||||||
|
device = %self.device.display(),
|
||||||
|
))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self {
|
||||||
|
device,
|
||||||
|
action_state,
|
||||||
|
} = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Merging systemd-sysext");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Merging systemd-sysext");
|
||||||
|
|
||||||
|
execute_command(Command::new("systemd-sysext").arg("merge").arg(device))
|
||||||
|
.await
|
||||||
|
.map_err(SystemdSysextMergeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Merged systemd-sysext");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn describe_revert(&self) -> Vec<ActionDescription> {
|
||||||
|
if self.action_state == ActionState::Uncompleted {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
"Stop the systemd Nix service and socket".to_string(),
|
||||||
|
vec![
|
||||||
|
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
||||||
|
]
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(
|
||||||
|
device = %self.device.display(),
|
||||||
|
))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self {
|
||||||
|
device,
|
||||||
|
action_state,
|
||||||
|
} = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Stopping systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Unmrging systemd-sysext");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(Command::new("systemd-sysext").arg("unmerge").arg(device))
|
||||||
|
.await
|
||||||
|
.map_err(SystemdSysextMergeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Unmerged systemd-sysext");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SystemdSysextMerge> for Action {
|
||||||
|
fn from(v: SystemdSysextMerge) -> Self {
|
||||||
|
Action::SystemdSysextMerge(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum SystemdSysextMergeError {
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
}
|
|
@ -27,12 +27,12 @@ pub struct CreateNixTree {
|
||||||
|
|
||||||
impl CreateNixTree {
|
impl CreateNixTree {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(force: bool) -> Result<Self, CreateNixTreeError> {
|
pub async fn plan() -> Result<Self, CreateNixTreeError> {
|
||||||
let mut create_directories = Vec::default();
|
let mut create_directories = Vec::default();
|
||||||
for path in PATHS {
|
for path in PATHS {
|
||||||
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
|
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
|
||||||
create_directories.push(
|
create_directories.push(
|
||||||
CreateDirectory::plan(path, "root".into(), "root".into(), 0o0755, force).await?,
|
CreateDirectory::plan(path, "root".into(), "root".into(), 0o0755, false).await?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
216
src/actions/meta/create_systemd_sysext.rs
Normal file
216
src/actions/meta/create_systemd_sysext.rs
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use crate::actions::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
|
||||||
|
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
|
||||||
|
|
||||||
|
const PATHS: &[&str] = &[
|
||||||
|
"usr",
|
||||||
|
"usr/lib",
|
||||||
|
"usr/lib/extension-release.d",
|
||||||
|
"usr/lib/systemd",
|
||||||
|
"usr/lib/systemd/system",
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct CreateSystemdSysext {
|
||||||
|
destination: PathBuf,
|
||||||
|
create_directories: Vec<CreateDirectory>,
|
||||||
|
create_extension_release: CreateFile,
|
||||||
|
create_bind_mount_unit: CreateFile,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateSystemdSysext {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(destination: impl AsRef<Path>) -> Result<Self, CreateSystemdSysextError> {
|
||||||
|
let destination = destination.as_ref();
|
||||||
|
|
||||||
|
let mut create_directories = vec![
|
||||||
|
CreateDirectory::plan(destination, "root".into(), "root".into(), 0o0755, true).await?,
|
||||||
|
];
|
||||||
|
for path in PATHS {
|
||||||
|
create_directories.push(
|
||||||
|
CreateDirectory::plan(
|
||||||
|
destination.join(path),
|
||||||
|
"root".into(),
|
||||||
|
"root".into(),
|
||||||
|
0o0755,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let version_id = tokio::fs::read_to_string("/etc/os-release")
|
||||||
|
.await
|
||||||
|
.map(|buf| {
|
||||||
|
buf.lines()
|
||||||
|
.find_map(|line| match line.starts_with("VERSION_ID") {
|
||||||
|
true => line.rsplit("=").next().map(|inner| inner.to_owned()),
|
||||||
|
false => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map_err(CreateSystemdSysextError::ReadingOsRelease)?
|
||||||
|
.ok_or(CreateSystemdSysextError::NoVersionId)?;
|
||||||
|
let extension_release_buf =
|
||||||
|
format!("SYSEXT_LEVEL=1.0\nID=steamos\nVERSION_ID={version_id}");
|
||||||
|
let create_extension_release = CreateFile::plan(
|
||||||
|
destination.join("usr/lib/extension-release.d/extension-release.nix"),
|
||||||
|
"root".into(),
|
||||||
|
"root".into(),
|
||||||
|
0o0755,
|
||||||
|
extension_release_buf,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let create_bind_mount_buf = format!(
|
||||||
|
"
|
||||||
|
[Mount]\n\
|
||||||
|
What={}\n\
|
||||||
|
Where=/nix\n\
|
||||||
|
Type=none\n\
|
||||||
|
Options=bind\n\
|
||||||
|
",
|
||||||
|
destination.display(),
|
||||||
|
);
|
||||||
|
let create_bind_mount_unit = CreateFile::plan(
|
||||||
|
destination.join("usr/lib/systemd/system/nix.mount"),
|
||||||
|
"root".into(),
|
||||||
|
"root".into(),
|
||||||
|
0o0755,
|
||||||
|
create_bind_mount_buf,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
destination: destination.to_path_buf(),
|
||||||
|
create_directories,
|
||||||
|
create_extension_release,
|
||||||
|
create_bind_mount_unit,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for CreateSystemdSysext {
|
||||||
|
type Error = CreateSystemdSysextError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
let Self {
|
||||||
|
action_state: _,
|
||||||
|
destination,
|
||||||
|
create_bind_mount_unit: _,
|
||||||
|
create_directories: _,
|
||||||
|
create_extension_release: _,
|
||||||
|
} = &self;
|
||||||
|
if self.action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
format!("Create a systemd sysext at `{}`", destination.display()),
|
||||||
|
vec![format!(
|
||||||
|
"Create a writable, persistent systemd system extension.",
|
||||||
|
)],
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(destination,))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self {
|
||||||
|
destination: _,
|
||||||
|
action_state,
|
||||||
|
create_directories,
|
||||||
|
create_extension_release,
|
||||||
|
create_bind_mount_unit,
|
||||||
|
} = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Creating sysext");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Creating sysext");
|
||||||
|
|
||||||
|
for create_directory in create_directories {
|
||||||
|
create_directory.execute().await?;
|
||||||
|
}
|
||||||
|
create_extension_release.execute().await?;
|
||||||
|
create_bind_mount_unit.execute().await?;
|
||||||
|
|
||||||
|
tracing::trace!("Created sysext");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn describe_revert(&self) -> Vec<ActionDescription> {
|
||||||
|
let Self {
|
||||||
|
destination,
|
||||||
|
action_state: _,
|
||||||
|
create_directories: _,
|
||||||
|
create_extension_release: _,
|
||||||
|
create_bind_mount_unit: _,
|
||||||
|
} = &self;
|
||||||
|
if self.action_state == ActionState::Uncompleted {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
format!("Remove the sysext located at `{}`", destination.display()),
|
||||||
|
vec![],
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(destination,))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self {
|
||||||
|
destination: _,
|
||||||
|
action_state,
|
||||||
|
create_directories,
|
||||||
|
create_extension_release,
|
||||||
|
create_bind_mount_unit,
|
||||||
|
} = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Removing sysext");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Removing sysext");
|
||||||
|
|
||||||
|
create_bind_mount_unit.revert().await?;
|
||||||
|
|
||||||
|
create_extension_release.revert().await?;
|
||||||
|
|
||||||
|
for create_directory in create_directories.iter_mut().rev() {
|
||||||
|
create_directory.revert().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::trace!("Removed sysext");
|
||||||
|
*action_state = ActionState::Uncompleted;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateSystemdSysext> for Action {
|
||||||
|
fn from(v: CreateSystemdSysext) -> Self {
|
||||||
|
Action::CreateSystemdSysext(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum CreateSystemdSysextError {
|
||||||
|
#[error(transparent)]
|
||||||
|
CreateDirectory(#[from] CreateDirectoryError),
|
||||||
|
#[error(transparent)]
|
||||||
|
CreateFile(#[from] CreateFileError),
|
||||||
|
#[error("Reading /etc/os-release")]
|
||||||
|
ReadingOsRelease(
|
||||||
|
#[source]
|
||||||
|
#[from]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
#[error("No `VERSION_ID` line in /etc/os-release")]
|
||||||
|
NoVersionId,
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
mod configure_nix;
|
mod configure_nix;
|
||||||
mod configure_shell_profile;
|
mod configure_shell_profile;
|
||||||
mod create_nix_tree;
|
mod create_nix_tree;
|
||||||
|
mod create_systemd_sysext;
|
||||||
mod create_users_and_group;
|
mod create_users_and_group;
|
||||||
mod place_channel_configuration;
|
mod place_channel_configuration;
|
||||||
mod place_nix_configuration;
|
mod place_nix_configuration;
|
||||||
|
@ -12,6 +13,7 @@ mod start_nix_daemon;
|
||||||
pub use configure_nix::{ConfigureNix, ConfigureNixError};
|
pub use configure_nix::{ConfigureNix, ConfigureNixError};
|
||||||
pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileError};
|
pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileError};
|
||||||
pub use create_nix_tree::{CreateNixTree, CreateNixTreeError};
|
pub use create_nix_tree::{CreateNixTree, CreateNixTreeError};
|
||||||
|
pub use create_systemd_sysext::{CreateSystemdSysext, CreateSystemdSysextError};
|
||||||
pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupError};
|
pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupError};
|
||||||
pub use place_channel_configuration::{PlaceChannelConfiguration, PlaceChannelConfigurationError};
|
pub use place_channel_configuration::{PlaceChannelConfiguration, PlaceChannelConfigurationError};
|
||||||
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationError};
|
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationError};
|
||||||
|
|
|
@ -27,8 +27,7 @@ impl ProvisionNix {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(settings: InstallSettings) -> Result<Self, ProvisionNixError> {
|
pub async fn plan(settings: InstallSettings) -> Result<Self, ProvisionNixError> {
|
||||||
let create_nix_dir =
|
let create_nix_dir =
|
||||||
CreateDirectory::plan("/nix", "root".into(), "root".into(), 0o0755, settings.force)
|
CreateDirectory::plan("/nix", "root".into(), "root".into(), 0o0755, true).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
let fetch_nix = FetchNix::plan(
|
let fetch_nix = FetchNix::plan(
|
||||||
settings.nix_package_url.clone(),
|
settings.nix_package_url.clone(),
|
||||||
|
@ -36,7 +35,7 @@ impl ProvisionNix {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()).await?;
|
let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()).await?;
|
||||||
let create_nix_tree = CreateNixTree::plan(settings.force).await?;
|
let create_nix_tree = CreateNixTree::plan().await?;
|
||||||
let move_unpacked_nix =
|
let move_unpacked_nix =
|
||||||
MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?;
|
MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
|
@ -6,19 +6,18 @@ use base::{
|
||||||
CreateDirectoryError, CreateFile, CreateFileError, CreateGroup, CreateGroupError,
|
CreateDirectoryError, CreateFile, CreateFileError, CreateGroup, CreateGroupError,
|
||||||
CreateOrAppendFile, CreateOrAppendFileError, CreateUser, CreateUserError, FetchNix,
|
CreateOrAppendFile, CreateOrAppendFileError, CreateUser, CreateUserError, FetchNix,
|
||||||
FetchNixError, MoveUnpackedNix, MoveUnpackedNixError, SetupDefaultProfile,
|
FetchNixError, MoveUnpackedNix, MoveUnpackedNixError, SetupDefaultProfile,
|
||||||
SetupDefaultProfileError,
|
SetupDefaultProfileError, StartSystemdUnit, StartSystemdUnitError, SystemdSysextMerge,
|
||||||
|
SystemdSysextMergeError,
|
||||||
};
|
};
|
||||||
use meta::{
|
use meta::{
|
||||||
ConfigureNix, ConfigureNixError, ConfigureShellProfile, ConfigureShellProfileError,
|
ConfigureNix, ConfigureNixError, ConfigureShellProfile, ConfigureShellProfileError,
|
||||||
CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersAndGroupError,
|
CreateNixTree, CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError,
|
||||||
PlaceChannelConfiguration, PlaceChannelConfigurationError, PlaceNixConfiguration,
|
CreateUsersAndGroup, CreateUsersAndGroupError, PlaceChannelConfiguration,
|
||||||
PlaceNixConfigurationError, ProvisionNix, ProvisionNixError, StartNixDaemon,
|
PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError,
|
||||||
StartNixDaemonError,
|
ProvisionNix, ProvisionNixError, StartNixDaemon, StartNixDaemonError,
|
||||||
};
|
};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
use self::base::{StartSystemdUnit, StartSystemdUnitError};
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait Actionable: DeserializeOwned + Serialize + Into<Action> {
|
pub trait Actionable: DeserializeOwned + Serialize + Into<Action> {
|
||||||
type Error: std::error::Error + std::fmt::Debug + Serialize + Into<ActionError>;
|
type Error: std::error::Error + std::fmt::Debug + Serialize + Into<ActionError>;
|
||||||
|
@ -61,6 +60,7 @@ pub enum Action {
|
||||||
ConfigureNix(ConfigureNix),
|
ConfigureNix(ConfigureNix),
|
||||||
ConfigureShellProfile(ConfigureShellProfile),
|
ConfigureShellProfile(ConfigureShellProfile),
|
||||||
CreateDirectory(CreateDirectory),
|
CreateDirectory(CreateDirectory),
|
||||||
|
CreateSystemdSysext(CreateSystemdSysext),
|
||||||
CreateFile(CreateFile),
|
CreateFile(CreateFile),
|
||||||
CreateGroup(CreateGroup),
|
CreateGroup(CreateGroup),
|
||||||
CreateOrAppendFile(CreateOrAppendFile),
|
CreateOrAppendFile(CreateOrAppendFile),
|
||||||
|
@ -74,6 +74,7 @@ pub enum Action {
|
||||||
SetupDefaultProfile(SetupDefaultProfile),
|
SetupDefaultProfile(SetupDefaultProfile),
|
||||||
StartNixDaemon(StartNixDaemon),
|
StartNixDaemon(StartNixDaemon),
|
||||||
StartSystemdUnit(StartSystemdUnit),
|
StartSystemdUnit(StartSystemdUnit),
|
||||||
|
SystemdSysextMerge(SystemdSysextMerge),
|
||||||
ProvisionNix(ProvisionNix),
|
ProvisionNix(ProvisionNix),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +95,8 @@ pub enum ActionError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
CreateDirectory(#[from] CreateDirectoryError),
|
CreateDirectory(#[from] CreateDirectoryError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
CreateSystemdSysext(#[from] CreateSystemdSysextError),
|
||||||
|
#[error(transparent)]
|
||||||
CreateFile(#[from] CreateFileError),
|
CreateFile(#[from] CreateFileError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
CreateGroup(#[from] CreateGroupError),
|
CreateGroup(#[from] CreateGroupError),
|
||||||
|
@ -120,6 +123,8 @@ pub enum ActionError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
StartSystemdUnit(#[from] StartSystemdUnitError),
|
StartSystemdUnit(#[from] StartSystemdUnitError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
SystemdSysExtMerge(#[from] SystemdSysextMergeError),
|
||||||
|
#[error(transparent)]
|
||||||
ProvisionNix(#[from] ProvisionNixError),
|
ProvisionNix(#[from] ProvisionNixError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +137,7 @@ impl Actionable for Action {
|
||||||
Action::ConfigureNix(i) => i.describe_execute(),
|
Action::ConfigureNix(i) => i.describe_execute(),
|
||||||
Action::ConfigureShellProfile(i) => i.describe_execute(),
|
Action::ConfigureShellProfile(i) => i.describe_execute(),
|
||||||
Action::CreateDirectory(i) => i.describe_execute(),
|
Action::CreateDirectory(i) => i.describe_execute(),
|
||||||
|
Action::CreateSystemdSysext(i) => i.describe_execute(),
|
||||||
Action::CreateFile(i) => i.describe_execute(),
|
Action::CreateFile(i) => i.describe_execute(),
|
||||||
Action::CreateGroup(i) => i.describe_execute(),
|
Action::CreateGroup(i) => i.describe_execute(),
|
||||||
Action::CreateOrAppendFile(i) => i.describe_execute(),
|
Action::CreateOrAppendFile(i) => i.describe_execute(),
|
||||||
|
@ -145,6 +151,7 @@ impl Actionable for Action {
|
||||||
Action::SetupDefaultProfile(i) => i.describe_execute(),
|
Action::SetupDefaultProfile(i) => i.describe_execute(),
|
||||||
Action::StartNixDaemon(i) => i.describe_execute(),
|
Action::StartNixDaemon(i) => i.describe_execute(),
|
||||||
Action::StartSystemdUnit(i) => i.describe_execute(),
|
Action::StartSystemdUnit(i) => i.describe_execute(),
|
||||||
|
Action::SystemdSysextMerge(i) => i.describe_execute(),
|
||||||
Action::ProvisionNix(i) => i.describe_execute(),
|
Action::ProvisionNix(i) => i.describe_execute(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,6 +162,7 @@ impl Actionable for Action {
|
||||||
Action::ConfigureNix(i) => i.execute().await?,
|
Action::ConfigureNix(i) => i.execute().await?,
|
||||||
Action::ConfigureShellProfile(i) => i.execute().await?,
|
Action::ConfigureShellProfile(i) => i.execute().await?,
|
||||||
Action::CreateDirectory(i) => i.execute().await?,
|
Action::CreateDirectory(i) => i.execute().await?,
|
||||||
|
Action::CreateSystemdSysext(i) => i.execute().await?,
|
||||||
Action::CreateFile(i) => i.execute().await?,
|
Action::CreateFile(i) => i.execute().await?,
|
||||||
Action::CreateGroup(i) => i.execute().await?,
|
Action::CreateGroup(i) => i.execute().await?,
|
||||||
Action::CreateOrAppendFile(i) => i.execute().await?,
|
Action::CreateOrAppendFile(i) => i.execute().await?,
|
||||||
|
@ -168,6 +176,7 @@ impl Actionable for Action {
|
||||||
Action::SetupDefaultProfile(i) => i.execute().await?,
|
Action::SetupDefaultProfile(i) => i.execute().await?,
|
||||||
Action::StartNixDaemon(i) => i.execute().await?,
|
Action::StartNixDaemon(i) => i.execute().await?,
|
||||||
Action::StartSystemdUnit(i) => i.execute().await?,
|
Action::StartSystemdUnit(i) => i.execute().await?,
|
||||||
|
Action::SystemdSysextMerge(i) => i.execute().await?,
|
||||||
Action::ProvisionNix(i) => i.execute().await?,
|
Action::ProvisionNix(i) => i.execute().await?,
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -179,6 +188,7 @@ impl Actionable for Action {
|
||||||
Action::ConfigureNix(i) => i.describe_revert(),
|
Action::ConfigureNix(i) => i.describe_revert(),
|
||||||
Action::ConfigureShellProfile(i) => i.describe_revert(),
|
Action::ConfigureShellProfile(i) => i.describe_revert(),
|
||||||
Action::CreateDirectory(i) => i.describe_revert(),
|
Action::CreateDirectory(i) => i.describe_revert(),
|
||||||
|
Action::CreateSystemdSysext(i) => i.describe_revert(),
|
||||||
Action::CreateFile(i) => i.describe_revert(),
|
Action::CreateFile(i) => i.describe_revert(),
|
||||||
Action::CreateGroup(i) => i.describe_revert(),
|
Action::CreateGroup(i) => i.describe_revert(),
|
||||||
Action::CreateOrAppendFile(i) => i.describe_revert(),
|
Action::CreateOrAppendFile(i) => i.describe_revert(),
|
||||||
|
@ -192,6 +202,7 @@ impl Actionable for Action {
|
||||||
Action::SetupDefaultProfile(i) => i.describe_revert(),
|
Action::SetupDefaultProfile(i) => i.describe_revert(),
|
||||||
Action::StartNixDaemon(i) => i.describe_revert(),
|
Action::StartNixDaemon(i) => i.describe_revert(),
|
||||||
Action::StartSystemdUnit(i) => i.describe_revert(),
|
Action::StartSystemdUnit(i) => i.describe_revert(),
|
||||||
|
Action::SystemdSysextMerge(i) => i.describe_revert(),
|
||||||
Action::ProvisionNix(i) => i.describe_revert(),
|
Action::ProvisionNix(i) => i.describe_revert(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,6 +213,7 @@ impl Actionable for Action {
|
||||||
Action::ConfigureNix(i) => i.revert().await?,
|
Action::ConfigureNix(i) => i.revert().await?,
|
||||||
Action::ConfigureShellProfile(i) => i.revert().await?,
|
Action::ConfigureShellProfile(i) => i.revert().await?,
|
||||||
Action::CreateDirectory(i) => i.revert().await?,
|
Action::CreateDirectory(i) => i.revert().await?,
|
||||||
|
Action::CreateSystemdSysext(i) => i.revert().await?,
|
||||||
Action::CreateFile(i) => i.revert().await?,
|
Action::CreateFile(i) => i.revert().await?,
|
||||||
Action::CreateGroup(i) => i.revert().await?,
|
Action::CreateGroup(i) => i.revert().await?,
|
||||||
Action::CreateOrAppendFile(i) => i.revert().await?,
|
Action::CreateOrAppendFile(i) => i.revert().await?,
|
||||||
|
@ -215,6 +227,7 @@ impl Actionable for Action {
|
||||||
Action::SetupDefaultProfile(i) => i.revert().await?,
|
Action::SetupDefaultProfile(i) => i.revert().await?,
|
||||||
Action::StartNixDaemon(i) => i.revert().await?,
|
Action::StartNixDaemon(i) => i.revert().await?,
|
||||||
Action::StartSystemdUnit(i) => i.revert().await?,
|
Action::StartSystemdUnit(i) => i.revert().await?,
|
||||||
|
Action::SystemdSysextMerge(i) => i.revert().await?,
|
||||||
Action::ProvisionNix(i) => i.revert().await?,
|
Action::ProvisionNix(i) => i.revert().await?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actions::{
|
actions::{Action, ActionDescription, ActionError, Actionable},
|
||||||
meta::{ConfigureNix, ProvisionNix, StartNixDaemon},
|
|
||||||
Action, ActionDescription, ActionError, Actionable,
|
|
||||||
},
|
|
||||||
planner::PlannerError,
|
planner::PlannerError,
|
||||||
settings::InstallSettings,
|
settings::InstallSettings,
|
||||||
HarmonicError, Planner,
|
HarmonicError, Planner,
|
||||||
|
|
|
@ -2,9 +2,7 @@ mod darwin;
|
||||||
mod linux;
|
mod linux;
|
||||||
mod specific;
|
mod specific;
|
||||||
|
|
||||||
use std::{ffi::OsStr, str::FromStr};
|
use crate::{actions::ActionError, InstallPlan, InstallSettings};
|
||||||
|
|
||||||
use crate::{actions::ActionError, HarmonicError, InstallPlan, InstallSettings};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, clap::ValueEnum, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, clap::ValueEnum, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Planner {
|
pub enum Planner {
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
use crate::{planner::Plannable, Planner};
|
use crate::{
|
||||||
|
actions::{
|
||||||
|
meta::{CreateSystemdSysext, ProvisionNix, StartNixDaemon},
|
||||||
|
Action, ActionError,
|
||||||
|
},
|
||||||
|
planner::Plannable,
|
||||||
|
InstallPlan, Planner,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||||
pub struct SteamDeck;
|
pub struct SteamDeck;
|
||||||
|
@ -11,7 +18,24 @@ impl Plannable for SteamDeck {
|
||||||
async fn plan(
|
async fn plan(
|
||||||
settings: crate::InstallSettings,
|
settings: crate::InstallSettings,
|
||||||
) -> Result<crate::InstallPlan, crate::planner::PlannerError> {
|
) -> Result<crate::InstallPlan, crate::planner::PlannerError> {
|
||||||
todo!()
|
Ok(InstallPlan {
|
||||||
|
planner: Self.into(),
|
||||||
|
settings: settings.clone(),
|
||||||
|
actions: vec![
|
||||||
|
CreateSystemdSysext::plan("/var/lib/extensions")
|
||||||
|
.await
|
||||||
|
.map(Action::from)
|
||||||
|
.map_err(ActionError::from)?,
|
||||||
|
ProvisionNix::plan(settings.clone())
|
||||||
|
.await
|
||||||
|
.map(Action::from)
|
||||||
|
.map_err(ActionError::from)?,
|
||||||
|
StartNixDaemon::plan()
|
||||||
|
.await
|
||||||
|
.map(Action::from)
|
||||||
|
.map_err(ActionError::from)?,
|
||||||
|
],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{planner, Planner};
|
use crate::planner;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue