2023-02-10 19:48:44 +00:00
|
|
|
/*! The Determinate [Nix](https://github.com/NixOS/nix) Installer
|
2022-11-28 22:57:35 +00:00
|
|
|
|
2022-12-19 18:26:58 +00:00
|
|
|
`nix-installer` breaks down into three main concepts:
|
2022-11-28 22:57:35 +00:00
|
|
|
|
2023-03-20 16:38:44 +00:00
|
|
|
* [`Action`]: An executable or revertable step, possibly orchestrating sub-[`Action`]s using things
|
2022-11-28 22:57:35 +00:00
|
|
|
like [`JoinSet`](tokio::task::JoinSet)s.
|
|
|
|
* [`InstallPlan`]: A set of [`Action`]s, along with some metadata, which can be carried out to
|
|
|
|
drive an install or revert.
|
|
|
|
* [`Planner`](planner::Planner): Something which can be used to plan out an [`InstallPlan`].
|
|
|
|
|
|
|
|
It is possible to create custom [`Action`]s and [`Planner`](planner::Planner)s to suit the needs of your project, team, or organization.
|
|
|
|
|
2022-12-19 18:26:58 +00:00
|
|
|
In the simplest case, `nix-installer` can be asked to determine a default plan for the platform and install
|
2022-11-28 22:57:35 +00:00
|
|
|
it, uninstalling if anything goes wrong:
|
|
|
|
|
|
|
|
```rust,no_run
|
|
|
|
use std::error::Error;
|
2022-12-19 18:26:58 +00:00
|
|
|
use nix_installer::InstallPlan;
|
2022-11-28 22:57:35 +00:00
|
|
|
|
|
|
|
# async fn default_install() -> color_eyre::Result<()> {
|
|
|
|
let mut plan = InstallPlan::default().await?;
|
|
|
|
match plan.install(None).await {
|
|
|
|
Ok(()) => tracing::info!("Done"),
|
|
|
|
Err(e) => {
|
|
|
|
match e.source() {
|
|
|
|
Some(source) => tracing::error!("{e}: {}", source),
|
|
|
|
None => tracing::error!("{e}"),
|
|
|
|
};
|
|
|
|
plan.uninstall(None).await?;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
#
|
|
|
|
# Ok(())
|
|
|
|
# }
|
|
|
|
```
|
|
|
|
|
2023-02-01 20:35:52 +00:00
|
|
|
Sometimes choosing a specific planner is desired:
|
2022-11-28 22:57:35 +00:00
|
|
|
|
|
|
|
```rust,no_run
|
|
|
|
use std::error::Error;
|
2023-02-01 20:35:52 +00:00
|
|
|
use nix_installer::{InstallPlan, planner::Planner};
|
2022-11-28 22:57:35 +00:00
|
|
|
|
|
|
|
# async fn chosen_planner_install() -> color_eyre::Result<()> {
|
2023-02-01 20:35:52 +00:00
|
|
|
#[cfg(target_os = "linux")]
|
2023-02-10 20:35:00 +00:00
|
|
|
let planner = nix_installer::planner::steam_deck::SteamDeck::default().await?;
|
2023-02-01 20:35:52 +00:00
|
|
|
#[cfg(target_os = "macos")]
|
2023-02-10 20:35:00 +00:00
|
|
|
let planner = nix_installer::planner::macos::Macos::default().await?;
|
2022-11-28 22:57:35 +00:00
|
|
|
|
|
|
|
// Or call `crate::planner::BuiltinPlanner::default()`
|
|
|
|
// Match on the result to customize.
|
|
|
|
|
|
|
|
// Customize any settings...
|
|
|
|
|
|
|
|
let mut plan = InstallPlan::plan(planner).await?;
|
|
|
|
match plan.install(None).await {
|
|
|
|
Ok(()) => tracing::info!("Done"),
|
|
|
|
Err(e) => {
|
|
|
|
match e.source() {
|
|
|
|
Some(source) => tracing::error!("{e}: {}", source),
|
|
|
|
None => tracing::error!("{e}"),
|
|
|
|
};
|
|
|
|
plan.uninstall(None).await?;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
#
|
|
|
|
# Ok(())
|
|
|
|
# }
|
|
|
|
```
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2022-10-26 22:13:42 +00:00
|
|
|
pub mod action;
|
2022-11-28 22:57:35 +00:00
|
|
|
#[cfg(feature = "cli")]
|
2022-10-25 18:57:09 +00:00
|
|
|
pub mod cli;
|
2023-02-24 18:11:12 +00:00
|
|
|
#[cfg(feature = "diagnostics")]
|
|
|
|
pub mod diagnostics;
|
2022-09-15 19:11:46 +00:00
|
|
|
mod error;
|
2022-10-19 22:12:50 +00:00
|
|
|
mod os;
|
2022-09-14 22:18:13 +00:00
|
|
|
mod plan;
|
2022-10-26 22:13:42 +00:00
|
|
|
pub mod planner;
|
2022-11-28 22:57:35 +00:00
|
|
|
pub mod settings;
|
2022-09-14 22:18:13 +00:00
|
|
|
|
2023-03-20 16:38:15 +00:00
|
|
|
use std::{ffi::OsStr, path::Path, process::Output};
|
2022-09-06 19:48:37 +00:00
|
|
|
|
2022-12-19 18:26:58 +00:00
|
|
|
pub use error::NixInstallerError;
|
2022-09-15 17:29:22 +00:00
|
|
|
pub use plan::InstallPlan;
|
2022-10-26 22:13:42 +00:00
|
|
|
use planner::BuiltinPlanner;
|
|
|
|
|
2023-03-20 16:38:15 +00:00
|
|
|
use reqwest::Certificate;
|
2022-09-26 21:07:53 +00:00
|
|
|
use tokio::process::Command;
|
2022-09-06 19:48:37 +00:00
|
|
|
|
2023-04-05 15:12:38 +00:00
|
|
|
use crate::action::{Action, ActionErrorKind};
|
|
|
|
|
2022-12-09 19:15:54 +00:00
|
|
|
#[tracing::instrument(level = "debug", skip_all, fields(command = %format!("{:?}", command.as_std())))]
|
2023-04-05 15:12:38 +00:00
|
|
|
async fn execute_command(command: &mut Command) -> Result<Output, ActionErrorKind> {
|
2023-03-27 20:28:47 +00:00
|
|
|
tracing::trace!("Executing");
|
2023-03-03 22:20:17 +00:00
|
|
|
let output = command
|
|
|
|
.output()
|
|
|
|
.await
|
2023-04-05 15:12:38 +00:00
|
|
|
.map_err(|e| ActionErrorKind::command(command, e))?;
|
2022-10-18 18:03:19 +00:00
|
|
|
match output.status.success() {
|
|
|
|
true => Ok(output),
|
2023-04-05 15:12:38 +00:00
|
|
|
false => Err(ActionErrorKind::command_output(command, output)),
|
2022-09-09 18:43:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-09 19:15:54 +00:00
|
|
|
#[tracing::instrument(level = "debug", skip_all, fields(
|
2022-09-09 18:43:35 +00:00
|
|
|
k = %k.as_ref().to_string_lossy(),
|
|
|
|
v = %v.as_ref().to_string_lossy(),
|
|
|
|
))]
|
2022-09-26 15:43:10 +00:00
|
|
|
fn set_env(k: impl AsRef<OsStr>, v: impl AsRef<OsStr>) {
|
|
|
|
tracing::trace!("Setting env");
|
|
|
|
std::env::set_var(k.as_ref(), v.as_ref());
|
2022-09-08 00:13:06 +00:00
|
|
|
}
|
2023-03-20 16:38:15 +00:00
|
|
|
|
|
|
|
async fn parse_ssl_cert(ssl_cert_file: &Path) -> Result<Certificate, CertificateError> {
|
|
|
|
let cert_buf = tokio::fs::read(ssl_cert_file)
|
|
|
|
.await
|
|
|
|
.map_err(|e| CertificateError::Read(ssl_cert_file.to_path_buf(), e))?;
|
|
|
|
// We actually try them since things could be `.crt` and `pem` format or `der` format
|
|
|
|
let cert = if let Ok(cert) = Certificate::from_pem(cert_buf.as_slice()) {
|
|
|
|
cert
|
|
|
|
} else if let Ok(cert) = Certificate::from_der(cert_buf.as_slice()) {
|
|
|
|
cert
|
|
|
|
} else {
|
|
|
|
return Err(CertificateError::UnknownCertFormat);
|
|
|
|
};
|
|
|
|
Ok(cert)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub enum CertificateError {
|
|
|
|
#[error(transparent)]
|
|
|
|
Reqwest(reqwest::Error),
|
|
|
|
#[error("Read path `{0}`")]
|
|
|
|
Read(std::path::PathBuf, #[source] std::io::Error),
|
|
|
|
#[error("Unknown certificate format, `der` and `pem` supported")]
|
|
|
|
UnknownCertFormat,
|
|
|
|
}
|