Find xpath crate, scaffold more
This commit is contained in:
parent
a29baa2b33
commit
da0219deb0
41
Cargo.lock
generated
41
Cargo.lock
generated
|
@ -758,6 +758,8 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
"sxd-document",
|
||||||
|
"sxd-xpath",
|
||||||
"tar",
|
"tar",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"tempdir",
|
"tempdir",
|
||||||
|
@ -1202,6 +1204,12 @@ version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peresil"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f658886ed52e196e850cfbbfddab9eaa7f6d90dd0929e264c31e5cec07e09e57"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
|
@ -1287,6 +1295,12 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
|
@ -1651,6 +1665,27 @@ dependencies = [
|
||||||
"is_ci",
|
"is_ci",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sxd-document"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94d82f37be9faf1b10a82c4bd492b74f698e40082f0f40de38ab275f31d42078"
|
||||||
|
dependencies = [
|
||||||
|
"peresil",
|
||||||
|
"typed-arena",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sxd-xpath"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36e39da5d30887b5690e29de4c5ebb8ddff64ebd9933f98a01daaa4fd11b36ea"
|
||||||
|
dependencies = [
|
||||||
|
"peresil",
|
||||||
|
"quick-error",
|
||||||
|
"sxd-document",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.99"
|
version = "1.0.99"
|
||||||
|
@ -1921,6 +1956,12 @@ version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typed-arena"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typetag"
|
name = "typetag"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -9,30 +9,32 @@ resolver = "2"
|
||||||
async-tar = "0.4.2"
|
async-tar = "0.4.2"
|
||||||
async-trait = "0.1.57"
|
async-trait = "0.1.57"
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
|
bytes = "1.2.1"
|
||||||
clap = { version = "4", features = ["derive", "env"] }
|
clap = { version = "4", features = ["derive", "env"] }
|
||||||
color-eyre = "0.6.2"
|
color-eyre = "0.6.2"
|
||||||
crossterm = { version = "0.25.0", features = ["event-stream"] }
|
crossterm = { version = "0.25.0", features = ["event-stream"] }
|
||||||
eyre = "0.6.8"
|
eyre = "0.6.8"
|
||||||
futures = "0.3.24"
|
futures = "0.3.24"
|
||||||
|
glob = "0.3.0"
|
||||||
|
nix = { version = "0.25.0", features = ["user", "fs"], default-features = false }
|
||||||
owo-colors = { version = "3.5.0", features = [ "supports-colors" ] }
|
owo-colors = { version = "3.5.0", features = [ "supports-colors" ] }
|
||||||
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls", "stream"] }
|
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls", "stream"] }
|
||||||
|
serde = { version = "1.0.144", features = ["derive"] }
|
||||||
|
serde_json = "1.0.85"
|
||||||
|
serde_with = "2.0.1"
|
||||||
|
tar = "0.4.38"
|
||||||
target-lexicon = "0.12.4"
|
target-lexicon = "0.12.4"
|
||||||
|
tempdir = { version = "0.3.7"}
|
||||||
thiserror = "1.0.33"
|
thiserror = "1.0.33"
|
||||||
tokio = { version = "1.21.0", features = ["time", "io-std", "process", "fs", "tracing", "rt-multi-thread", "macros", "io-util"] }
|
tokio = { version = "1.21.0", features = ["time", "io-std", "process", "fs", "tracing", "rt-multi-thread", "macros", "io-util"] }
|
||||||
tokio-util = { version = "0.7", features = ["io"] }
|
tokio-util = { version = "0.7", features = ["io"] }
|
||||||
tracing = { version = "0.1.36", features = [ "valuable" ] }
|
tracing = { version = "0.1.36", features = [ "valuable" ] }
|
||||||
tracing-error = "0.2.0"
|
tracing-error = "0.2.0"
|
||||||
tracing-subscriber = { version = "0.3.15", features = [ "env-filter", "valuable" ] }
|
tracing-subscriber = { version = "0.3.15", features = [ "env-filter", "valuable" ] }
|
||||||
valuable = { version = "0.1.0", features = ["derive"] }
|
|
||||||
tempdir = { version = "0.3.7"}
|
|
||||||
glob = "0.3.0"
|
|
||||||
xz2 = { version = "0.1.7", features = ["static", "tokio"] }
|
|
||||||
bytes = "1.2.1"
|
|
||||||
tar = "0.4.38"
|
|
||||||
nix = { version = "0.25.0", features = ["user", "fs"], default-features = false }
|
|
||||||
walkdir = "2.3.2"
|
|
||||||
serde = { version = "1.0.144", features = ["derive"] }
|
|
||||||
url = { version = "2.3.1", features = ["serde"] }
|
|
||||||
serde_json = "1.0.85"
|
|
||||||
typetag = "0.2.3"
|
typetag = "0.2.3"
|
||||||
serde_with = "2.0.1"
|
url = { version = "2.3.1", features = ["serde"] }
|
||||||
|
valuable = { version = "0.1.0", features = ["derive"] }
|
||||||
|
walkdir = "2.3.2"
|
||||||
|
sxd-xpath = "0.4.2"
|
||||||
|
xz2 = { version = "0.1.7", features = ["static", "tokio"] }
|
||||||
|
sxd-document = "0.3.2"
|
||||||
|
|
116
src/actions/base/darwin/bootstrap_volume.rs
Normal file
116
src/actions/base/darwin/bootstrap_volume.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
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 BootstrapVolume {
|
||||||
|
unit: String,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BootstrapVolume {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(unit: String) -> Result<Self, BootstrapVolumeError> {
|
||||||
|
Ok(Self {
|
||||||
|
unit,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for BootstrapVolume {
|
||||||
|
type Error = BootstrapVolumeError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
if self.action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
"Start 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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Starting systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Starting systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("enable")
|
||||||
|
.arg("--now")
|
||||||
|
.arg(format!("{unit}")),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(BootstrapVolumeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Started systemd unit");
|
||||||
|
*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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Stopping systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Stopping systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
|
||||||
|
.await
|
||||||
|
.map_err(BootstrapVolumeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Stopped systemd unit");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BootstrapVolume> for Action {
|
||||||
|
fn from(v: BootstrapVolume) -> Self {
|
||||||
|
Action::DarwinBootstrapVolume(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum BootstrapVolumeError {
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
}
|
116
src/actions/base/darwin/create_synthetic_objects.rs
Normal file
116
src/actions/base/darwin/create_synthetic_objects.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
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 CreateSyntheticObjects {
|
||||||
|
unit: String,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateSyntheticObjects {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(unit: String) -> Result<Self, CreateSyntheticObjectsError> {
|
||||||
|
Ok(Self {
|
||||||
|
unit,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for CreateSyntheticObjects {
|
||||||
|
type Error = CreateSyntheticObjectsError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
if self.action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
"Start 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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Starting systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Starting systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("enable")
|
||||||
|
.arg("--now")
|
||||||
|
.arg(format!("{unit}")),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(CreateSyntheticObjectsError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Started systemd unit");
|
||||||
|
*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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Stopping systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Stopping systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
|
||||||
|
.await
|
||||||
|
.map_err(CreateSyntheticObjectsError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Stopped systemd unit");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateSyntheticObjects> for Action {
|
||||||
|
fn from(v: CreateSyntheticObjects) -> Self {
|
||||||
|
Action::DarwinCreateSyntheticObjects(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum CreateSyntheticObjectsError {
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
}
|
116
src/actions/base/darwin/create_volume.rs
Normal file
116
src/actions/base/darwin/create_volume.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
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 CreateVolume {
|
||||||
|
unit: String,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateVolume {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(unit: String) -> Result<Self, CreateVolumeError> {
|
||||||
|
Ok(Self {
|
||||||
|
unit,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for CreateVolume {
|
||||||
|
type Error = CreateVolumeError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
if self.action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
"Start 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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Starting systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Starting systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("enable")
|
||||||
|
.arg("--now")
|
||||||
|
.arg(format!("{unit}")),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(CreateVolumeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Started systemd unit");
|
||||||
|
*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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Stopping systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Stopping systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
|
||||||
|
.await
|
||||||
|
.map_err(CreateVolumeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Stopped systemd unit");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateVolume> for Action {
|
||||||
|
fn from(v: CreateVolume) -> Self {
|
||||||
|
Action::DarwinCreateVolume(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum CreateVolumeError {
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
}
|
116
src/actions/base/darwin/enable_ownership.rs
Normal file
116
src/actions/base/darwin/enable_ownership.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
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 EnableOwnership {
|
||||||
|
unit: String,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnableOwnership {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(unit: String) -> Result<Self, EnableOwnershipError> {
|
||||||
|
Ok(Self {
|
||||||
|
unit,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for EnableOwnership {
|
||||||
|
type Error = EnableOwnershipError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
if self.action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
"Start 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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Starting systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Starting systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("enable")
|
||||||
|
.arg("--now")
|
||||||
|
.arg(format!("{unit}")),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(EnableOwnershipError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Started systemd unit");
|
||||||
|
*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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Stopping systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Stopping systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
|
||||||
|
.await
|
||||||
|
.map_err(EnableOwnershipError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Stopped systemd unit");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EnableOwnership> for Action {
|
||||||
|
fn from(v: EnableOwnership) -> Self {
|
||||||
|
Action::DarwinEnableOwnership(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum EnableOwnershipError {
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
}
|
116
src/actions/base/darwin/encrypt_volume.rs
Normal file
116
src/actions/base/darwin/encrypt_volume.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
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 EncryptVolume {
|
||||||
|
unit: String,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncryptVolume {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(unit: String) -> Result<Self, EncryptVolumeError> {
|
||||||
|
Ok(Self {
|
||||||
|
unit,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for EncryptVolume {
|
||||||
|
type Error = EncryptVolumeError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
if self.action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
"Start 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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Starting systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Starting systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("enable")
|
||||||
|
.arg("--now")
|
||||||
|
.arg(format!("{unit}")),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(EncryptVolumeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Started systemd unit");
|
||||||
|
*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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Stopping systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Stopping systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
|
||||||
|
.await
|
||||||
|
.map_err(EncryptVolumeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Stopped systemd unit");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EncryptVolume> for Action {
|
||||||
|
fn from(v: EncryptVolume) -> Self {
|
||||||
|
Action::DarwinEncryptVolume(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum EncryptVolumeError {
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
}
|
13
src/actions/base/darwin/mod.rs
Normal file
13
src/actions/base/darwin/mod.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
mod bootstrap_volume;
|
||||||
|
mod create_synthetic_objects;
|
||||||
|
mod create_volume;
|
||||||
|
mod enable_ownership;
|
||||||
|
mod encrypt_volume;
|
||||||
|
mod unmount_volume;
|
||||||
|
|
||||||
|
pub use bootstrap_volume::{BootstrapVolume, BootstrapVolumeError};
|
||||||
|
pub use create_synthetic_objects::{CreateSyntheticObjects, CreateSyntheticObjectsError};
|
||||||
|
pub use create_volume::{CreateVolume, CreateVolumeError};
|
||||||
|
pub use enable_ownership::{EnableOwnership, EnableOwnershipError};
|
||||||
|
pub use encrypt_volume::{EncryptVolume, EncryptVolumeError};
|
||||||
|
pub use unmount_volume::{UnmountVolume, UnmountVolumeError};
|
116
src/actions/base/darwin/unmount_volume.rs
Normal file
116
src/actions/base/darwin/unmount_volume.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
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 UnmountVolume {
|
||||||
|
unit: String,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnmountVolume {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(unit: String) -> Result<Self, UnmountVolumeError> {
|
||||||
|
Ok(Self {
|
||||||
|
unit,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for UnmountVolume {
|
||||||
|
type Error = UnmountVolumeError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
if self.action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
"Start 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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Starting systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Starting systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("enable")
|
||||||
|
.arg("--now")
|
||||||
|
.arg(format!("{unit}")),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(UnmountVolumeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Started systemd unit");
|
||||||
|
*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(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Stopping systemd unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Stopping systemd unit");
|
||||||
|
|
||||||
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
|
||||||
|
.await
|
||||||
|
.map_err(UnmountVolumeError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Stopped systemd unit");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnmountVolume> for Action {
|
||||||
|
fn from(v: UnmountVolume) -> Self {
|
||||||
|
Action::DarwinUnmountVolume(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum UnmountVolumeError {
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ mod create_file;
|
||||||
mod create_group;
|
mod create_group;
|
||||||
mod create_or_append_file;
|
mod create_or_append_file;
|
||||||
mod create_user;
|
mod create_user;
|
||||||
|
pub mod darwin;
|
||||||
mod fetch_nix;
|
mod fetch_nix;
|
||||||
mod move_unpacked_nix;
|
mod move_unpacked_nix;
|
||||||
mod setup_default_profile;
|
mod setup_default_profile;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::actions::base::{
|
||||||
CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError, CreateOrAppendFile,
|
CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError, CreateOrAppendFile,
|
||||||
CreateOrAppendFileError,
|
CreateOrAppendFileError,
|
||||||
};
|
};
|
||||||
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
|
use crate::actions::{base::darwin, Action, ActionDescription, ActionState, Actionable};
|
||||||
|
|
||||||
const NIX_VOLUME_MOUNTD_DEST: &str = "/Library/LaunchDaemons/org.nixos.darwin-store.plist";
|
const NIX_VOLUME_MOUNTD_DEST: &str = "/Library/LaunchDaemons/org.nixos.darwin-store.plist";
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ impl CreateApfsVolume {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let bootstrap_volume = BootstrapVolume::plan(NIX_VOLUME_MOUNTD_DEST).await?;
|
let bootstrap_volume = BootstrapVolume::plan(NIX_VOLUME_MOUNTD_DEST, disk, name).await?;
|
||||||
let enable_ownership = EnableOwnership::plan("/nix").await?;
|
let enable_ownership = EnableOwnership::plan("/nix").await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -116,7 +116,7 @@ impl Actionable for CreateApfsVolume {
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
} else {
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Create an APFS volume", destination.display()),
|
format!("Create an APFS volume `{name}` on `{}`", disk.display()),
|
||||||
vec![format!(
|
vec![format!(
|
||||||
"Create a writable, persistent systemd system extension.",
|
"Create a writable, persistent systemd system extension.",
|
||||||
)],
|
)],
|
||||||
|
@ -142,18 +142,21 @@ impl Actionable for CreateApfsVolume {
|
||||||
action_state,
|
action_state,
|
||||||
} = self;
|
} = self;
|
||||||
if *action_state == ActionState::Completed {
|
if *action_state == ActionState::Completed {
|
||||||
tracing::trace!("Already completed: Creating sysext");
|
tracing::trace!("Already completed: Creating APFS volume");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
tracing::debug!("Creating sysext");
|
tracing::debug!("Creating APFS volume");
|
||||||
|
|
||||||
for create_directory in create_directories {
|
create_or_append_synthetic_conf.execute().await?;
|
||||||
create_directory.execute().await?;
|
create_synthetic_objects.execute().await?;
|
||||||
}
|
unmount_volume.execute().await?;
|
||||||
create_extension_release.execute().await?;
|
create_volume.execute().await?;
|
||||||
create_bind_mount_unit.execute().await?;
|
create_or_append_fstab.execute().await?;
|
||||||
|
encrypt_volume.execute().await?;
|
||||||
|
bootstrap_volume.execute().await?;
|
||||||
|
enable_ownership.execute().await?;
|
||||||
|
|
||||||
tracing::trace!("Created sysext");
|
tracing::trace!("Created APFS volume");
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -178,19 +181,21 @@ impl Actionable for CreateApfsVolume {
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
} else {
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Remove the sysext located at `{}`", destination.display()),
|
format!("Remove the APFS volume `{name}` on `{}`", disk.display()),
|
||||||
vec![],
|
vec![format!(
|
||||||
|
"Create a writable, persistent systemd system extension.",
|
||||||
|
)],
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(destination,))]
|
#[tracing::instrument(skip_all, fields(disk, name))]
|
||||||
async fn revert(&mut self) -> Result<(), Self::Error> {
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
let Self {
|
let Self {
|
||||||
disk,
|
disk: _,
|
||||||
name,
|
name: _,
|
||||||
case_sensitive,
|
case_sensitive: _,
|
||||||
encrypt,
|
encrypt: _,
|
||||||
create_or_append_synthetic_conf,
|
create_or_append_synthetic_conf,
|
||||||
create_synthetic_objects,
|
create_synthetic_objects,
|
||||||
unmount_volume,
|
unmount_volume,
|
||||||
|
@ -202,20 +207,26 @@ impl Actionable for CreateApfsVolume {
|
||||||
action_state,
|
action_state,
|
||||||
} = self;
|
} = self;
|
||||||
if *action_state == ActionState::Uncompleted {
|
if *action_state == ActionState::Uncompleted {
|
||||||
tracing::trace!("Already reverted: Removing sysext");
|
tracing::trace!("Already reverted: Removing APFS volume");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
tracing::debug!("Removing sysext");
|
tracing::debug!("Removing APFS volume");
|
||||||
|
|
||||||
create_bind_mount_unit.revert().await?;
|
enable_ownership.revert().await?;
|
||||||
|
bootstrap_volume.revert().await?;
|
||||||
create_extension_release.revert().await?;
|
if let Some(encrypt_volume) = encrypt_volume {
|
||||||
|
encrypt_volume.revert().await?;
|
||||||
for create_directory in create_directories.iter_mut().rev() {
|
|
||||||
create_directory.revert().await?;
|
|
||||||
}
|
}
|
||||||
|
create_or_append_fstab.revert().await?;
|
||||||
|
|
||||||
tracing::trace!("Removed sysext");
|
unmount_volume.revert().await?;
|
||||||
|
create_volume.revert().await?;
|
||||||
|
|
||||||
|
// Purposefully not reversed
|
||||||
|
create_or_append_synthetic_conf.revert().await?;
|
||||||
|
create_synthetic_objects.revert().await?;
|
||||||
|
|
||||||
|
tracing::trace!("Removed APFS volume");
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -229,6 +240,18 @@ impl From<CreateApfsVolume> for Action {
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, Serialize)]
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
pub enum CreateApfsVolumeError {
|
pub enum CreateApfsVolumeError {
|
||||||
|
#[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)]
|
#[error(transparent)]
|
||||||
CreateOrAppendFile(#[from] CreateOrAppendFileError),
|
CreateOrAppendFile(#[from] CreateOrAppendFileError),
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,12 @@ impl ActionDescription {
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
|
DarwinBootstrapVolume(base::darwin::BootstrapVolume),
|
||||||
|
DarwinCreateSyntheticObjects(base::darwin::CreateSyntheticObjects),
|
||||||
|
DarwinCreateVolume(base::darwin::CreateVolume),
|
||||||
|
DarwinEnableOwnership(base::darwin::EnableOwnership),
|
||||||
|
DarwinEncryptVolume(base::darwin::EncryptVolume),
|
||||||
|
DarwinUnmountVolume(base::darwin::UnmountVolume),
|
||||||
ConfigureNix(ConfigureNix),
|
ConfigureNix(ConfigureNix),
|
||||||
ConfigureNixDaemonService(ConfigureNixDaemonService),
|
ConfigureNixDaemonService(ConfigureNixDaemonService),
|
||||||
ConfigureShellProfile(ConfigureShellProfile),
|
ConfigureShellProfile(ConfigureShellProfile),
|
||||||
|
@ -82,6 +88,18 @@ pub enum Action {
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, serde::Serialize)]
|
#[derive(Debug, thiserror::Error, serde::Serialize)]
|
||||||
pub enum ActionError {
|
pub enum ActionError {
|
||||||
|
#[error(transparent)]
|
||||||
|
DarwinBootstrapVolume(#[from] base::darwin::BootstrapVolumeError),
|
||||||
|
#[error(transparent)]
|
||||||
|
DarwinCreateSyntheticObjects(#[from] base::darwin::CreateSyntheticObjectsError),
|
||||||
|
#[error(transparent)]
|
||||||
|
DarwinCreateVolume(#[from] base::darwin::CreateVolumeError),
|
||||||
|
#[error(transparent)]
|
||||||
|
DarwinEnableOwnership(#[from] base::darwin::EnableOwnershipError),
|
||||||
|
#[error(transparent)]
|
||||||
|
DarwinEncryptVolume(#[from] base::darwin::EncryptVolumeError),
|
||||||
|
#[error(transparent)]
|
||||||
|
DarwinUnmountVolume(#[from] base::darwin::UnmountVolumeError),
|
||||||
#[error("Attempted to revert an unexecuted action")]
|
#[error("Attempted to revert an unexecuted action")]
|
||||||
NotExecuted(Action),
|
NotExecuted(Action),
|
||||||
#[error("Attempted to execute an already executed action")]
|
#[error("Attempted to execute an already executed action")]
|
||||||
|
@ -137,6 +155,12 @@ impl Actionable for Action {
|
||||||
type Error = ActionError;
|
type Error = ActionError;
|
||||||
fn describe_execute(&self) -> Vec<ActionDescription> {
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
match self {
|
match self {
|
||||||
|
Action::DarwinBootstrapVolume(i) => i.describe_execute(),
|
||||||
|
Action::DarwinCreateSyntheticObjects(i) => i.describe_execute(),
|
||||||
|
Action::DarwinCreateVolume(i) => i.describe_execute(),
|
||||||
|
Action::DarwinEnableOwnership(i) => i.describe_execute(),
|
||||||
|
Action::DarwinEncryptVolume(i) => i.describe_execute(),
|
||||||
|
Action::DarwinUnmountVolume(i) => i.describe_execute(),
|
||||||
Action::ConfigureNix(i) => i.describe_execute(),
|
Action::ConfigureNix(i) => i.describe_execute(),
|
||||||
Action::ConfigureNixDaemonService(i) => i.describe_execute(),
|
Action::ConfigureNixDaemonService(i) => i.describe_execute(),
|
||||||
Action::ConfigureShellProfile(i) => i.describe_execute(),
|
Action::ConfigureShellProfile(i) => i.describe_execute(),
|
||||||
|
@ -163,6 +187,12 @@ impl Actionable for Action {
|
||||||
|
|
||||||
async fn execute(&mut self) -> Result<(), Self::Error> {
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
match self {
|
match self {
|
||||||
|
Action::DarwinBootstrapVolume(i) => i.execute().await?,
|
||||||
|
Action::DarwinCreateSyntheticObjects(i) => i.execute().await?,
|
||||||
|
Action::DarwinCreateVolume(i) => i.execute().await?,
|
||||||
|
Action::DarwinEnableOwnership(i) => i.execute().await?,
|
||||||
|
Action::DarwinEncryptVolume(i) => i.execute().await?,
|
||||||
|
Action::DarwinUnmountVolume(i) => i.execute().await?,
|
||||||
Action::ConfigureNix(i) => i.execute().await?,
|
Action::ConfigureNix(i) => i.execute().await?,
|
||||||
Action::ConfigureNixDaemonService(i) => i.execute().await?,
|
Action::ConfigureNixDaemonService(i) => i.execute().await?,
|
||||||
Action::ConfigureShellProfile(i) => i.execute().await?,
|
Action::ConfigureShellProfile(i) => i.execute().await?,
|
||||||
|
@ -190,6 +220,12 @@ impl Actionable for Action {
|
||||||
|
|
||||||
fn describe_revert(&self) -> Vec<ActionDescription> {
|
fn describe_revert(&self) -> Vec<ActionDescription> {
|
||||||
match self {
|
match self {
|
||||||
|
Action::DarwinBootstrapVolume(i) => i.describe_revert(),
|
||||||
|
Action::DarwinCreateSyntheticObjects(i) => i.describe_revert(),
|
||||||
|
Action::DarwinCreateVolume(i) => i.describe_revert(),
|
||||||
|
Action::DarwinEnableOwnership(i) => i.describe_revert(),
|
||||||
|
Action::DarwinEncryptVolume(i) => i.describe_revert(),
|
||||||
|
Action::DarwinUnmountVolume(i) => i.describe_revert(),
|
||||||
Action::ConfigureNix(i) => i.describe_revert(),
|
Action::ConfigureNix(i) => i.describe_revert(),
|
||||||
Action::ConfigureNixDaemonService(i) => i.describe_revert(),
|
Action::ConfigureNixDaemonService(i) => i.describe_revert(),
|
||||||
Action::ConfigureShellProfile(i) => i.describe_revert(),
|
Action::ConfigureShellProfile(i) => i.describe_revert(),
|
||||||
|
@ -216,6 +252,12 @@ impl Actionable for Action {
|
||||||
|
|
||||||
async fn revert(&mut self) -> Result<(), Self::Error> {
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
match self {
|
match self {
|
||||||
|
Action::DarwinBootstrapVolume(i) => i.revert().await?,
|
||||||
|
Action::DarwinCreateSyntheticObjects(i) => i.revert().await?,
|
||||||
|
Action::DarwinCreateVolume(i) => i.revert().await?,
|
||||||
|
Action::DarwinEnableOwnership(i) => i.revert().await?,
|
||||||
|
Action::DarwinEncryptVolume(i) => i.revert().await?,
|
||||||
|
Action::DarwinUnmountVolume(i) => i.revert().await?,
|
||||||
Action::ConfigureNix(i) => i.revert().await?,
|
Action::ConfigureNix(i) => i.revert().await?,
|
||||||
Action::ConfigureNixDaemonService(i) => i.revert().await?,
|
Action::ConfigureNixDaemonService(i) => i.revert().await?,
|
||||||
Action::ConfigureShellProfile(i) => i.revert().await?,
|
Action::ConfigureShellProfile(i) => i.revert().await?,
|
||||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -4,7 +4,11 @@ mod plan;
|
||||||
mod planner;
|
mod planner;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
|
||||||
use std::{ffi::OsStr, fmt::Display, process::ExitStatus};
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
fmt::Display,
|
||||||
|
process::{ExitStatus, Output},
|
||||||
|
};
|
||||||
|
|
||||||
pub use error::HarmonicError;
|
pub use error::HarmonicError;
|
||||||
pub use plan::InstallPlan;
|
pub use plan::InstallPlan;
|
||||||
|
@ -15,12 +19,12 @@ pub use settings::InstallSettings;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(command = %format!("{:?}", command.as_std())))]
|
#[tracing::instrument(skip_all, fields(command = %format!("{:?}", command.as_std())))]
|
||||||
async fn execute_command(command: &mut Command) -> Result<ExitStatus, std::io::Error> {
|
async fn execute_command(command: &mut Command) -> Result<Output, std::io::Error> {
|
||||||
tracing::trace!("Executing");
|
tracing::trace!("Executing");
|
||||||
let command_str = format!("{:?}", command.as_std());
|
let command_str = format!("{:?}", command.as_std());
|
||||||
let status = command.status().await?;
|
let output = command.output().await?;
|
||||||
match status.success() {
|
match output.status.success() {
|
||||||
true => Ok(status),
|
true => Ok(output),
|
||||||
false => Err(std::io::Error::new(
|
false => Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::Other,
|
std::io::ErrorKind::Other,
|
||||||
format!("Command `{command_str}` failed status"),
|
format!("Command `{command_str}` failed status"),
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actions::{
|
actions::{
|
||||||
meta::{darwin::CreateApfsVolume, ConfigureNix, ProvisionNix, StartNixDaemon},
|
meta::{darwin::CreateApfsVolume, ConfigureNix, ProvisionNix, StartNixDaemon},
|
||||||
Action, ActionError,
|
Action, ActionError,
|
||||||
},
|
},
|
||||||
planner::Plannable,
|
execute_command,
|
||||||
|
planner::{Plannable, PlannerError},
|
||||||
InstallPlan, Planner,
|
InstallPlan, Planner,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,6 +21,28 @@ impl Plannable for DarwinMultiUser {
|
||||||
async fn plan(
|
async fn plan(
|
||||||
settings: crate::InstallSettings,
|
settings: crate::InstallSettings,
|
||||||
) -> Result<crate::InstallPlan, crate::planner::PlannerError> {
|
) -> Result<crate::InstallPlan, crate::planner::PlannerError> {
|
||||||
|
let root_disk = {
|
||||||
|
let root_disk_buf =
|
||||||
|
execute_command(Command::new("/usr/sbin/diskutil").args(["info", "-plist", "/"]))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.stdout;
|
||||||
|
let package =
|
||||||
|
sxd_document::parser::parse(&String::from_utf8(root_disk_buf).unwrap()).unwrap();
|
||||||
|
|
||||||
|
match sxd_xpath::evaluate_xpath(
|
||||||
|
&package.as_document(),
|
||||||
|
"/plist/dict/key[text()='ParentWholeDisk']/following-sibling::string[1]/text()",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
{
|
||||||
|
sxd_xpath::Value::String(s) => s,
|
||||||
|
_ => panic!("At the disk i/o!!!"),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let volume_label = "Nix Store".into();
|
||||||
|
|
||||||
Ok(InstallPlan {
|
Ok(InstallPlan {
|
||||||
planner: Self.into(),
|
planner: Self.into(),
|
||||||
settings: settings.clone(),
|
settings: settings.clone(),
|
||||||
|
@ -26,7 +51,7 @@ impl Plannable for DarwinMultiUser {
|
||||||
//
|
//
|
||||||
// setup_Synthetic -> create_synthetic_objects
|
// setup_Synthetic -> create_synthetic_objects
|
||||||
// Unmount -> create_volume -> Setup_fstab -> maybe encrypt_volume -> launchctl bootstrap -> launchctl kickstart -> await_volume -> maybe enableOwnership
|
// Unmount -> create_volume -> Setup_fstab -> maybe encrypt_volume -> launchctl bootstrap -> launchctl kickstart -> await_volume -> maybe enableOwnership
|
||||||
CreateApfsVolume::plan(settings.clone())
|
CreateApfsVolume::plan(root_disk, volume_label, false, None)
|
||||||
.await
|
.await
|
||||||
.map(Action::from)
|
.map(Action::from)
|
||||||
.map_err(ActionError::from)?,
|
.map_err(ActionError::from)?,
|
||||||
|
|
Loading…
Reference in a new issue