forked from lix-project/lix-installer
Merge branch 'main' into hoverbear/ds-431-ctrlc-should-be-handled-and-terminate-us
This commit is contained in:
commit
8c77b6eb38
54
.github/workflows/ci.yml
vendored
54
.github/workflows/ci.yml
vendored
|
@ -82,6 +82,33 @@ jobs:
|
||||||
path: |
|
path: |
|
||||||
result/bin/harmonic
|
result/bin/harmonic
|
||||||
|
|
||||||
|
RunX86Linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: BuildX86Linux
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: harmonic-x86_64-linux
|
||||||
|
- name: Set executable
|
||||||
|
run: chmod +x ./harmonic
|
||||||
|
- name: Initial install
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --no-confirm
|
||||||
|
- name: Test run
|
||||||
|
run: |
|
||||||
|
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||||
|
nix run nixpkgs#fortune
|
||||||
|
- name: Initial uninstall
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
- name: Repeated install
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --no-confirm
|
||||||
|
- name: Repeated test run
|
||||||
|
run: |
|
||||||
|
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||||
|
nix run nixpkgs#fortune
|
||||||
|
- name: Repeated uninstall
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
|
||||||
|
|
||||||
BuildX86Darwin:
|
BuildX86Darwin:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -99,3 +126,30 @@ jobs:
|
||||||
name: harmonic-x86_64-darwin
|
name: harmonic-x86_64-darwin
|
||||||
path: |
|
path: |
|
||||||
result/bin/harmonic
|
result/bin/harmonic
|
||||||
|
|
||||||
|
RunX86Darwin:
|
||||||
|
runs-on: macos-latest
|
||||||
|
needs: BuildX86Darwin
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: harmonic-x86_64-darwin
|
||||||
|
- name: Set executable
|
||||||
|
run: chmod +x ./harmonic
|
||||||
|
- name: Initial install
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --no-confirm
|
||||||
|
- name: Test run
|
||||||
|
run: |
|
||||||
|
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||||
|
nix run nixpkgs#fortune
|
||||||
|
- name: Initial uninstall
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
- name: Repeated install
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --no-confirm
|
||||||
|
- name: Repeated test run
|
||||||
|
run: |
|
||||||
|
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||||
|
nix run nixpkgs#fortune
|
||||||
|
- name: Repeated uninstall
|
||||||
|
run: sudo RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
description = "riff";
|
description = "harmonic";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05";
|
||||||
|
|
|
@ -148,6 +148,16 @@ impl Action for ConfigureNixDaemonService {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||||
|
|
||||||
|
execute_command(
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("enable")
|
||||||
|
.arg("--now")
|
||||||
|
.arg("nix-daemon.socket")
|
||||||
|
.stdin(std::process::Stdio::null()),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -199,7 +209,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
_ => {
|
_ => {
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
.args(["disable", SOCKET_SRC])
|
.args(["disable", SOCKET_SRC, "--now"])
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -207,7 +217,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
.args(["disable", SERVICE_SRC])
|
.args(["disable", SERVICE_SRC, "--now"])
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -241,6 +251,10 @@ impl Action for ConfigureNixDaemonService {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
|
@ -214,6 +214,10 @@ impl Action for CreateDirectory {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
|
@ -188,6 +188,10 @@ impl Action for CreateFile {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
|
@ -167,6 +167,10 @@ impl Action for CreateGroup {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
|
@ -223,6 +223,10 @@ impl Action for CreateOrAppendFile {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
|
@ -278,6 +278,10 @@ impl Action for CreateUser {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
|
@ -115,6 +115,10 @@ impl Action for FetchNix {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
21
src/action/base/mod.rs
Normal file
21
src/action/base/mod.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//! Base actions that themselves have no other actions as dependencies
|
||||||
|
|
||||||
|
mod configure_nix_daemon_service;
|
||||||
|
mod create_directory;
|
||||||
|
mod create_file;
|
||||||
|
mod create_group;
|
||||||
|
mod create_or_append_file;
|
||||||
|
mod create_user;
|
||||||
|
mod fetch_nix;
|
||||||
|
mod move_unpacked_nix;
|
||||||
|
mod setup_default_profile;
|
||||||
|
|
||||||
|
pub use configure_nix_daemon_service::{ConfigureNixDaemonService, ConfigureNixDaemonServiceError};
|
||||||
|
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 fetch_nix::{FetchNix, FetchNixError};
|
||||||
|
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
||||||
|
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
|
@ -106,6 +106,10 @@ impl Action for MoveUnpackedNix {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
|
@ -183,6 +183,10 @@ impl Action for SetupDefaultProfile {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
|
@ -1,17 +1,15 @@
|
||||||
use reqwest::Url;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
common::{
|
base::{ConfigureNixDaemonService, SetupDefaultProfile},
|
||||||
ConfigureNixDaemonService, ConfigureShellProfile, PlaceChannelConfiguration,
|
common::{ConfigureShellProfile, PlaceChannelConfiguration, PlaceNixConfiguration},
|
||||||
PlaceNixConfiguration, SetupDefaultProfile,
|
|
||||||
},
|
|
||||||
Action, ActionDescription, ActionState,
|
Action, ActionDescription, ActionState,
|
||||||
},
|
},
|
||||||
channel_value::ChannelValue,
|
channel_value::ChannelValue,
|
||||||
BoxableError, CommonSettings,
|
BoxableError, CommonSettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use reqwest::Url;
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct ConfigureNix {
|
pub struct ConfigureNix {
|
||||||
setup_default_profile: SetupDefaultProfile,
|
setup_default_profile: SetupDefaultProfile,
|
||||||
|
@ -184,4 +182,8 @@ impl Action for ConfigureNix {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
|
use crate::action::base::{CreateOrAppendFile, CreateOrAppendFileError};
|
||||||
|
use crate::action::{Action, ActionDescription, ActionState};
|
||||||
|
use crate::BoxableError;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use tokio::task::{JoinError, JoinSet};
|
use tokio::task::{JoinError, JoinSet};
|
||||||
|
|
||||||
use crate::action::common::{CreateOrAppendFile, CreateOrAppendFileError};
|
|
||||||
use crate::{
|
|
||||||
action::{Action, ActionDescription, ActionState},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
const PROFILE_TARGETS: &[&str] = &[
|
const PROFILE_TARGETS: &[&str] = &[
|
||||||
"/etc/bashrc",
|
"/etc/bashrc",
|
||||||
"/etc/profile.d/nix.sh",
|
"/etc/profile.d/nix.sh",
|
||||||
|
@ -181,6 +178,10 @@ impl Action for ConfigureShellProfile {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::action::common::{CreateDirectory, CreateDirectoryError};
|
use crate::action::base::{CreateDirectory, CreateDirectoryError};
|
||||||
use crate::action::{Action, ActionDescription, ActionState};
|
use crate::action::{Action, ActionDescription, ActionState};
|
||||||
|
|
||||||
const PATHS: &[&str] = &[
|
const PATHS: &[&str] = &[
|
||||||
|
@ -134,6 +134,10 @@ impl Action for CreateNixTree {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use tokio::task::{JoinError, JoinSet};
|
|
||||||
|
|
||||||
use crate::CommonSettings;
|
use crate::CommonSettings;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
common::{CreateGroup, CreateGroupError, CreateUser, CreateUserError},
|
base::{CreateGroup, CreateGroupError, CreateUser, CreateUserError},
|
||||||
Action, ActionDescription, ActionState,
|
Action, ActionDescription, ActionState,
|
||||||
},
|
},
|
||||||
BoxableError,
|
BoxableError,
|
||||||
};
|
};
|
||||||
|
use tokio::task::{JoinError, JoinSet};
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateUsersAndGroup {
|
pub struct CreateUsersAndGroup {
|
||||||
|
@ -229,6 +227,10 @@ impl Action for CreateUsersAndGroup {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,35 +1,17 @@
|
||||||
/*! Actions which only call other base plugins. */
|
/*! Actions which only call other base plugins. */
|
||||||
|
|
||||||
mod configure_nix;
|
mod configure_nix;
|
||||||
mod configure_nix_daemon_service;
|
|
||||||
mod configure_shell_profile;
|
mod configure_shell_profile;
|
||||||
mod create_directory;
|
|
||||||
mod create_file;
|
|
||||||
mod create_group;
|
|
||||||
mod create_nix_tree;
|
mod create_nix_tree;
|
||||||
mod create_or_append_file;
|
|
||||||
mod create_user;
|
|
||||||
mod create_users_and_group;
|
mod create_users_and_group;
|
||||||
mod fetch_nix;
|
|
||||||
mod move_unpacked_nix;
|
|
||||||
mod place_channel_configuration;
|
mod place_channel_configuration;
|
||||||
mod place_nix_configuration;
|
mod place_nix_configuration;
|
||||||
mod provision_nix;
|
mod provision_nix;
|
||||||
mod setup_default_profile;
|
|
||||||
|
|
||||||
pub use configure_nix::ConfigureNix;
|
pub use configure_nix::ConfigureNix;
|
||||||
pub use configure_nix_daemon_service::{ConfigureNixDaemonService, ConfigureNixDaemonServiceError};
|
|
||||||
pub use configure_shell_profile::ConfigureShellProfile;
|
pub use configure_shell_profile::ConfigureShellProfile;
|
||||||
pub use create_directory::{CreateDirectory, CreateDirectoryError};
|
|
||||||
pub use create_file::{CreateFile, CreateFileError};
|
|
||||||
pub use create_group::{CreateGroup, CreateGroupError};
|
|
||||||
pub use create_nix_tree::{CreateNixTree, CreateNixTreeError};
|
pub use create_nix_tree::{CreateNixTree, CreateNixTreeError};
|
||||||
pub use create_or_append_file::{CreateOrAppendFile, CreateOrAppendFileError};
|
|
||||||
pub use create_user::{CreateUser, CreateUserError};
|
|
||||||
pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupError};
|
pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupError};
|
||||||
pub use fetch_nix::{FetchNix, FetchNixError};
|
|
||||||
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
|
||||||
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};
|
||||||
pub use provision_nix::{ProvisionNix, ProvisionNixError};
|
pub use provision_nix::{ProvisionNix, ProvisionNixError};
|
||||||
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use reqwest::Url;
|
use crate::action::base::{CreateFile, CreateFileError};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{Action, ActionDescription, ActionState},
|
action::{Action, ActionDescription, ActionState},
|
||||||
BoxableError,
|
BoxableError,
|
||||||
};
|
};
|
||||||
|
use reqwest::Url;
|
||||||
use crate::action::common::{CreateFile, CreateFileError};
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct PlaceChannelConfiguration {
|
pub struct PlaceChannelConfiguration {
|
||||||
|
@ -130,6 +128,10 @@ impl Action for PlaceChannelConfiguration {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
use crate::action::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
|
||||||
use crate::action::{Action, ActionDescription, ActionState};
|
use crate::action::{Action, ActionDescription, ActionState};
|
||||||
|
|
||||||
use crate::action::common::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
|
|
||||||
|
|
||||||
const NIX_CONF_FOLDER: &str = "/etc/nix";
|
const NIX_CONF_FOLDER: &str = "/etc/nix";
|
||||||
const NIX_CONF: &str = "/etc/nix/nix.conf";
|
const NIX_CONF: &str = "/etc/nix/nix.conf";
|
||||||
|
|
||||||
|
@ -115,6 +114,10 @@ impl Action for PlaceNixConfiguration {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
use std::path::PathBuf;
|
use crate::action::base::{
|
||||||
|
|
||||||
use tokio::task::JoinError;
|
|
||||||
|
|
||||||
use crate::action::common::{
|
|
||||||
CreateDirectoryError, FetchNix, FetchNixError, MoveUnpackedNix, MoveUnpackedNixError,
|
CreateDirectoryError, FetchNix, FetchNixError, MoveUnpackedNix, MoveUnpackedNixError,
|
||||||
};
|
};
|
||||||
use crate::CommonSettings;
|
use crate::CommonSettings;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{Action, ActionDescription, ActionState},
|
action::{Action, ActionDescription, ActionState},
|
||||||
BoxableError,
|
BoxableError,
|
||||||
};
|
};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use tokio::task::JoinError;
|
||||||
|
|
||||||
use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersAndGroupError};
|
use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersAndGroupError};
|
||||||
|
|
||||||
|
@ -168,6 +165,10 @@ impl Action for ProvisionNix {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -108,6 +108,10 @@ impl Action for BootstrapVolume {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
use std::{
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
use tokio::process::Command;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
common::{CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError},
|
base::{CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError},
|
||||||
darwin::{
|
darwin::{
|
||||||
BootstrapVolume, BootstrapVolumeError, CreateSyntheticObjects,
|
BootstrapVolume, BootstrapVolumeError, CreateSyntheticObjects,
|
||||||
CreateSyntheticObjectsError, CreateVolume, CreateVolumeError, EnableOwnership,
|
CreateSyntheticObjectsError, CreateVolume, CreateVolumeError, EnableOwnership,
|
||||||
|
@ -17,6 +11,11 @@ use crate::{
|
||||||
},
|
},
|
||||||
BoxableError,
|
BoxableError,
|
||||||
};
|
};
|
||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
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";
|
||||||
|
|
||||||
|
@ -289,6 +288,10 @@ impl Action for CreateApfsVolume {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -102,6 +102,10 @@ impl Action for CreateSyntheticObjects {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -138,6 +138,10 @@ impl Action for CreateVolume {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -111,6 +111,10 @@ impl Action for EnableOwnership {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -86,6 +86,10 @@ impl Action for EncryptVolume {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -97,6 +97,10 @@ impl Action for KickstartLaunchctlService {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -119,6 +119,10 @@ impl Action for UnmountVolume {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use std::path::{Path, PathBuf};
|
use crate::action::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
|
||||||
|
|
||||||
use crate::action::common::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{Action, ActionDescription, ActionState},
|
action::{Action, ActionDescription, ActionState},
|
||||||
BoxableError,
|
BoxableError,
|
||||||
};
|
};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
const PATHS: &[&str] = &[
|
const PATHS: &[&str] = &[
|
||||||
"usr",
|
"usr",
|
||||||
|
@ -185,6 +184,10 @@ impl Action for CreateSystemdSysext {
|
||||||
*action_state = ActionState::Uncompleted;
|
*action_state = ActionState::Uncompleted;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -93,7 +93,7 @@ impl Action for StartSystemdUnit {
|
||||||
// TODO(@Hoverbear): Handle proxy vars
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
.arg("stop")
|
.arg("disable")
|
||||||
.arg(format!("{unit}"))
|
.arg(format!("{unit}"))
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
|
@ -104,6 +104,10 @@ impl Action for StartSystemdUnit {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use tokio::process::Command;
|
|
||||||
|
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{Action, ActionDescription, ActionState},
|
action::{Action, ActionDescription, ActionState},
|
||||||
BoxableError,
|
BoxableError,
|
||||||
};
|
};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct SystemdSysextMerge {
|
pub struct SystemdSysextMerge {
|
||||||
|
@ -112,6 +109,10 @@ impl Action for SystemdSysextMerge {
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn action_state(&self) -> ActionState {
|
||||||
|
self.action_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod base;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod darwin;
|
pub mod darwin;
|
||||||
pub mod linux;
|
pub mod linux;
|
||||||
|
@ -13,11 +14,12 @@ pub trait Action: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
|
||||||
// They should also have an `async fn plan(args...) -> Result<ActionState<Self>, Box<dyn std::error::Error + Send + Sync>>;`
|
// They should also have an `async fn plan(args...) -> Result<ActionState<Self>, Box<dyn std::error::Error + Send + Sync>>;`
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
fn action_state(&self) -> ActionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
dyn_clone::clone_trait_object!(Action);
|
dyn_clone::clone_trait_object!(Action);
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)]
|
||||||
pub enum ActionState {
|
pub enum ActionState {
|
||||||
Completed,
|
Completed,
|
||||||
// Only applicable to meta-actions that start multiple sub-actions.
|
// Only applicable to meta-actions that start multiple sub-actions.
|
||||||
|
|
|
@ -71,3 +71,7 @@ pub(crate) async fn signal_channel() -> eyre::Result<(Sender<()>, Receiver<()>)>
|
||||||
|
|
||||||
Ok((sender, reciever))
|
Ok((sender, reciever))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_root() -> bool {
|
||||||
|
nix::unistd::getuid() == nix::unistd::Uid::from_raw(0)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
use std::{path::PathBuf, process::ExitCode};
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::ExitCode,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{cli::signal_channel, BuiltinPlanner, HarmonicError};
|
use crate::{
|
||||||
|
action::ActionState, cli::is_root, cli::signal_channel, plan::RECEIPT_LOCATION, BuiltinPlanner,
|
||||||
|
InstallPlan, Planner,
|
||||||
|
};
|
||||||
use clap::{ArgAction, Parser};
|
use clap::{ArgAction, Parser};
|
||||||
use eyre::{eyre, WrapErr};
|
use eyre::{eyre, WrapErr};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
@ -9,7 +15,7 @@ use crate::{cli::CommandExecute, interaction};
|
||||||
|
|
||||||
/// Execute an install (possibly using an existing plan)
|
/// Execute an install (possibly using an existing plan)
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(args_conflicts_with_subcommands = true)]
|
#[command(args_conflicts_with_subcommands = true, arg_required_else_help = true)]
|
||||||
pub struct Install {
|
pub struct Install {
|
||||||
#[clap(
|
#[clap(
|
||||||
long,
|
long,
|
||||||
|
@ -30,7 +36,7 @@ pub struct Install {
|
||||||
pub plan: Option<PathBuf>,
|
pub plan: Option<PathBuf>,
|
||||||
|
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
pub planner: BuiltinPlanner,
|
pub planner: Option<BuiltinPlanner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -44,36 +50,79 @@ impl CommandExecute for Install {
|
||||||
explain,
|
explain,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let mut plan = match &plan {
|
if !is_root() {
|
||||||
Some(plan_path) => {
|
return Err(eyre!(
|
||||||
|
"`harmonic install` must be run as `root`, try `sudo harmonic install`"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let existing_receipt: Option<InstallPlan> = match Path::new(RECEIPT_LOCATION).exists() {
|
||||||
|
true => {
|
||||||
|
let install_plan_string = tokio::fs::read_to_string(&RECEIPT_LOCATION)
|
||||||
|
.await
|
||||||
|
.wrap_err("Reading plan")?;
|
||||||
|
Some(serde_json::from_str(&install_plan_string)?)
|
||||||
|
},
|
||||||
|
false => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut install_plan = match (planner, plan) {
|
||||||
|
(Some(planner), None) => {
|
||||||
|
let chosen_planner: Box<dyn Planner> = planner.clone().boxed();
|
||||||
|
|
||||||
|
match existing_receipt {
|
||||||
|
Some(existing_receipt) => {
|
||||||
|
if existing_receipt.planner.typetag_name() != chosen_planner.typetag_name() {
|
||||||
|
return Err(eyre!("Found existing plan in `{RECEIPT_LOCATION}` which used a different planner, try uninstalling the existing install"))
|
||||||
|
}
|
||||||
|
if existing_receipt.planner.settings().map_err(|e| eyre!(e))? != chosen_planner.settings().map_err(|e| eyre!(e))? {
|
||||||
|
return Err(eyre!("Found existing plan in `{RECEIPT_LOCATION}` which used different planner settings, try uninstalling the existing install"))
|
||||||
|
}
|
||||||
|
if existing_receipt.actions.iter().all(|v| v.action_state() == ActionState::Completed) {
|
||||||
|
return Err(eyre!("Found existing plan in `{RECEIPT_LOCATION}`, with the same settings, already completed, try uninstalling and reinstalling if Nix isn't working"))
|
||||||
|
}
|
||||||
|
existing_receipt
|
||||||
|
} ,
|
||||||
|
None => {
|
||||||
|
planner.plan().await.map_err(|e| eyre!(e))?
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(None, Some(plan_path)) => {
|
||||||
let install_plan_string = tokio::fs::read_to_string(&plan_path)
|
let install_plan_string = tokio::fs::read_to_string(&plan_path)
|
||||||
.await
|
.await
|
||||||
.wrap_err("Reading plan")?;
|
.wrap_err("Reading plan")?;
|
||||||
serde_json::from_str(&install_plan_string)?
|
serde_json::from_str(&install_plan_string)?
|
||||||
},
|
},
|
||||||
None => planner.plan().await.map_err(|e| eyre!(e))?,
|
(None, None) => return Err(eyre!("`--plan` or a planner is required")),
|
||||||
|
(Some(_), Some(_)) => return Err(eyre!("`--plan` conflicts with passing a planner, a planner creates plans, so passing an existing plan doesn't make sense")),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !no_confirm {
|
if !no_confirm {
|
||||||
if !interaction::confirm(plan.describe_execute(explain).map_err(|e| eyre!(e))?).await? {
|
if !interaction::confirm(
|
||||||
|
install_plan
|
||||||
|
.describe_execute(explain)
|
||||||
|
.map_err(|e| eyre!(e))?,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await;
|
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tx, rx1) = signal_channel().await?;
|
let (tx, rx1) = signal_channel().await?;
|
||||||
|
|
||||||
if let Err(err) = plan.install(rx1).await {
|
if let Err(err) = install_plan.install(rx1).await {
|
||||||
match err {
|
let error = eyre!(err).wrap_err("Install failure");
|
||||||
HarmonicError::Cancelled => {},
|
if !no_confirm {
|
||||||
err => {
|
tracing::error!("{:?}", error);
|
||||||
tracing::error!("{:?}", eyre!(err));
|
if !interaction::confirm(install_plan.describe_revert(explain)).await? {
|
||||||
if !interaction::confirm(plan.describe_revert(explain)).await? {
|
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await;
|
||||||
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!")
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
let rx2 = tx.subscribe();
|
let rx2 = tx.subscribe();
|
||||||
plan.revert(rx2).await?
|
install_plan.revert(rx2).await?
|
||||||
},
|
} else {
|
||||||
|
return Err(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@ use crate::cli::CommandExecute;
|
||||||
|
|
||||||
/// Plan an install that can be repeated on an identical host later
|
/// Plan an install that can be repeated on an identical host later
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(args_conflicts_with_subcommands = true, arg_required_else_help = true)]
|
#[command(arg_required_else_help = true)]
|
||||||
pub struct Plan {
|
pub struct Plan {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
pub planner: Option<BuiltinPlanner>,
|
pub planner: Option<BuiltinPlanner>,
|
||||||
#[clap(env = "HARMONIC_PLAN")]
|
#[clap(env = "HARMONIC_PLAN", default_value = "/dev/stdout")]
|
||||||
pub output: PathBuf,
|
pub output: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use std::{path::PathBuf, process::ExitCode};
|
use std::{path::PathBuf, process::ExitCode};
|
||||||
|
|
||||||
use crate::{cli::signal_channel, InstallPlan};
|
use crate::{
|
||||||
|
cli::{is_root, signal_channel},
|
||||||
|
plan::RECEIPT_LOCATION,
|
||||||
|
InstallPlan,
|
||||||
|
};
|
||||||
use clap::{ArgAction, Parser};
|
use clap::{ArgAction, Parser};
|
||||||
use eyre::WrapErr;
|
use eyre::{eyre, WrapErr};
|
||||||
|
|
||||||
use crate::{cli::CommandExecute, interaction};
|
use crate::{cli::CommandExecute, interaction};
|
||||||
|
|
||||||
|
@ -23,7 +27,7 @@ pub struct Uninstall {
|
||||||
global = true
|
global = true
|
||||||
)]
|
)]
|
||||||
pub explain: bool,
|
pub explain: bool,
|
||||||
#[clap(default_value = "/nix/receipt.json")]
|
#[clap(default_value = RECEIPT_LOCATION)]
|
||||||
pub receipt: PathBuf,
|
pub receipt: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +41,12 @@ impl CommandExecute for Uninstall {
|
||||||
explain,
|
explain,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
if !is_root() {
|
||||||
|
return Err(eyre!(
|
||||||
|
"`harmonic install` must be run as `root`, try `sudo harmonic install`"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let install_receipt_string = tokio::fs::read_to_string(receipt)
|
let install_receipt_string = tokio::fs::read_to_string(receipt)
|
||||||
.await
|
.await
|
||||||
.wrap_err("Reading receipt")?;
|
.wrap_err("Reading receipt")?;
|
||||||
|
|
|
@ -10,6 +10,8 @@ use crate::{
|
||||||
HarmonicError,
|
HarmonicError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const RECEIPT_LOCATION: &str = "/nix/receipt.json";
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct InstallPlan {
|
pub struct InstallPlan {
|
||||||
pub(crate) actions: Vec<Box<dyn Action>>,
|
pub(crate) actions: Vec<Box<dyn Action>>,
|
||||||
|
@ -45,7 +47,7 @@ impl InstallPlan {
|
||||||
},
|
},
|
||||||
planner = planner.typetag_name(),
|
planner = planner.typetag_name(),
|
||||||
plan_settings = planner
|
plan_settings = planner
|
||||||
.describe()?
|
.settings()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| format!("* {k}: {v}", k = k.bold().white()))
|
.map(|(k, v)| format!("* {k}: {v}", k = k.bold().white()))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
@ -198,7 +200,7 @@ async fn write_receipt(plan: InstallPlan) -> Result<(), HarmonicError> {
|
||||||
tokio::fs::create_dir_all("/nix")
|
tokio::fs::create_dir_all("/nix")
|
||||||
.await
|
.await
|
||||||
.map_err(|e| HarmonicError::RecordingReceipt(PathBuf::from("/nix"), e))?;
|
.map_err(|e| HarmonicError::RecordingReceipt(PathBuf::from("/nix"), e))?;
|
||||||
let install_receipt_path = PathBuf::from("/nix/receipt.json");
|
let install_receipt_path = PathBuf::from(RECEIPT_LOCATION);
|
||||||
let self_json =
|
let self_json =
|
||||||
serde_json::to_string_pretty(&plan).map_err(HarmonicError::SerializingReceipt)?;
|
serde_json::to_string_pretty(&plan).map_err(HarmonicError::SerializingReceipt)?;
|
||||||
tokio::fs::write(&install_receipt_path, self_json)
|
tokio::fs::write(&install_receipt_path, self_json)
|
||||||
|
|
|
@ -104,7 +104,7 @@ impl Planner for DarwinMulti {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe(
|
fn settings(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
|
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
|
||||||
let Self {
|
let Self {
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
common::{ConfigureNix, CreateDirectory, ProvisionNix},
|
base::CreateDirectory,
|
||||||
linux::StartSystemdUnit,
|
common::{ConfigureNix, ProvisionNix},
|
||||||
},
|
},
|
||||||
planner::Planner,
|
planner::Planner,
|
||||||
BuiltinPlanner, CommonSettings, InstallPlan,
|
BuiltinPlanner, CommonSettings, InstallPlan,
|
||||||
};
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone, clap::Parser, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, clap::Parser, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct LinuxMulti {
|
pub struct LinuxMulti {
|
||||||
|
@ -31,12 +30,11 @@ impl Planner for LinuxMulti {
|
||||||
Box::new(CreateDirectory::plan("/nix", None, None, 0o0755, true).await?),
|
Box::new(CreateDirectory::plan("/nix", None, None, 0o0755, true).await?),
|
||||||
Box::new(ProvisionNix::plan(self.settings.clone()).await?),
|
Box::new(ProvisionNix::plan(self.settings.clone()).await?),
|
||||||
Box::new(ConfigureNix::plan(self.settings).await?),
|
Box::new(ConfigureNix::plan(self.settings).await?),
|
||||||
Box::new(StartSystemdUnit::plan("nix-daemon.socket".into()).await?),
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe(
|
fn settings(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
|
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
|
||||||
let Self { settings } = self;
|
let Self { settings } = self;
|
||||||
|
|
|
@ -13,9 +13,15 @@ pub trait Planner: std::fmt::Debug + Send + Sync + dyn_clone::DynClone {
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
async fn plan(self) -> Result<InstallPlan, Box<dyn std::error::Error + Sync + Send>>;
|
async fn plan(self) -> Result<InstallPlan, Box<dyn std::error::Error + Sync + Send>>;
|
||||||
fn describe(
|
fn settings(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>>;
|
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>>;
|
||||||
|
fn boxed(self) -> Box<dyn Planner>
|
||||||
|
where
|
||||||
|
Self: Sized + 'static,
|
||||||
|
{
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dyn_clone::clone_trait_object!(Planner);
|
dyn_clone::clone_trait_object!(Planner);
|
||||||
|
@ -56,6 +62,13 @@ impl BuiltinPlanner {
|
||||||
BuiltinPlanner::SteamDeck(planner) => planner.plan().await,
|
BuiltinPlanner::SteamDeck(planner) => planner.plan().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn boxed(self) -> Box<dyn Planner> {
|
||||||
|
match self {
|
||||||
|
BuiltinPlanner::LinuxMulti(i) => i.boxed(),
|
||||||
|
BuiltinPlanner::DarwinMulti(i) => i.boxed(),
|
||||||
|
BuiltinPlanner::SteamDeck(i) => i.boxed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
|
|
@ -2,7 +2,8 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
common::{CreateDirectory, ProvisionNix},
|
base::CreateDirectory,
|
||||||
|
common::ProvisionNix,
|
||||||
linux::{CreateSystemdSysext, StartSystemdUnit},
|
linux::{CreateSystemdSysext, StartSystemdUnit},
|
||||||
},
|
},
|
||||||
planner::Planner,
|
planner::Planner,
|
||||||
|
@ -36,7 +37,7 @@ impl Planner for SteamDeck {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe(
|
fn settings(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
|
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
|
||||||
let Self { settings } = self;
|
let Self { settings } = self;
|
||||||
|
|
Loading…
Reference in a new issue