Tidy and flesh out README

This commit is contained in:
Ana Hobden 2022-10-06 12:21:49 -07:00
parent d675647fc9
commit d35edb976f
12 changed files with 89 additions and 41 deletions

20
Cargo.lock generated
View file

@ -264,26 +264,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.2.20" version = "4.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" checksum = "3b1a0a4208c6c483b952ad35c6eed505fc13b46f08f631b81e828084a9318d74"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"clap_derive", "clap_derive",
"clap_lex", "clap_lex",
"indexmap",
"once_cell", "once_cell",
"strsim", "strsim",
"termcolor", "termcolor",
"textwrap",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "3.2.18" version = "4.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" checksum = "db342ce9fda24fb191e2ed4e102055a4d381c1086a06630174cd8da8d5d917ce"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@ -294,9 +292,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.2.4" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
@ -1443,12 +1441,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.33" version = "1.0.33"

View file

@ -9,7 +9,7 @@ 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"
clap = { version = "3.2.20", 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"

View file

@ -1,14 +1,67 @@
# Harmonic # Harmonic
> **Harmonic is pre-release and experimental.** Don't run it on machines you care about.
Harmonic is an opinionated, experimental Nix installer. Harmonic is an opinionated, experimental Nix installer.
> **Harmonic is pre-release, pre-early access.** Don't run it on machines you care about. ## Status
Currently it supports: Harmonic is **pre-release and experimental**. It is not ready for you to use! *Please* don't use it on a machine you are not planning to oblierate!
* x86_64 Linux with systemd init Planned support:
* (Planned) aarch64 Linux with systemd init
* (Planned) x86_64 MacOS
* (Planned) aarch64 MacOS
Harmonic may also support some form of NixOS install in the future. * [x] Multi-user x86_64 Linux with systemd init
* [ ] Multi-user aarch64 Linux with systemd init
* [ ] Multi-user x86_64 MacOS
* [ ] Single-user x86_64 Linux with systemd init
* [ ] Single-user aarch64 Linux with systemd init
* [ ] Multi-user aarch64 MacOS
* [ ] Others...
## Installation Differences
Differing from the current official Nix installer scripts:
* Nix is installed with the `nix-command` and `flakes` features enabled in the `nix.conf`
* Harmonic stores an installation receipt (for uninstalling) at `/nix/receipt.json`
## Motivations
The current Nix installer scripts do an excellent job, however they are difficult to maintain. Subtle differences in the shell implementations, and certain characteristics of bash scripts make it difficult to make meaningful changes to the installer.
Our team wishes to experiment with the idea of an installer in a more structured language and see if this is a worthwhile alternative. Along the way, we are also exploring a few other ideas, such as:
* offering users a chance to review an accurate, calculated install plan
* keeping an installation receipt for uninstallation
* offering users with a failing install the chance to revert
* doing whatever tasks we can in parallel
So far, our explorations have been quite fruitful, so we wanted to share and keep exploring.
## Building
Harmonic is pre-release and we do not provide binaries at this time.
Since you'll be using Harmonic to install Nix on systems without Nix, the default build is a static binary.
Build it on a system with Nix:
```bash
nix build github:determinatesystems/harmonic
```
Then copy the `result/bin/harmonic` to the machine you wish to run it on.
## Running
Harmonic must be run as `root` or via `sudo`, as it needs to alter the system and cannot elevate privileges without significant complexity.
Install Nix with default options:
```bash
./harmonic
```
To observe verbose logging, either use `harmonic -V`, for extremely verbose trace logging use `RUST_LOG=harmonic=trace harmonic`.
Harmonic supports many of the options the current official Nix installer scripts. Review `harmonic --help` for details.

View file

@ -121,17 +121,18 @@
}; };
in in
rec { rec {
default = harmonic;
harmonic = naerskLib.buildPackage harmonic = naerskLib.buildPackage
(sharedAttrs // { }); (sharedAttrs // { });
} // lib.optionalAttrs (system == "x86_64-linux") { } // lib.optionalAttrs (system == "x86_64-linux") rec {
default = harmonicStatic;
harmonicStatic = naerskLib.buildPackage harmonicStatic = naerskLib.buildPackage
(sharedAttrs // { (sharedAttrs // {
CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl";
OPENSSL_LIB_DIR = "${pkgs.pkgsStatic.openssl.out}/lib"; OPENSSL_LIB_DIR = "${pkgs.pkgsStatic.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.pkgsStatic.openssl.dev}"; OPENSSL_INCLUDE_DIR = "${pkgs.pkgsStatic.openssl.dev}";
}); });
} // lib.optionalAttrs (system == "aarch64-linux") { } // lib.optionalAttrs (system == "aarch64-linux") rec {
default = harmonicStatic;
harmonicStatic = naerskLib.buildPackage harmonicStatic = naerskLib.buildPackage
(sharedAttrs // { (sharedAttrs // {
CARGO_BUILD_TARGET = "aarch64-unknown-linux-musl"; CARGO_BUILD_TARGET = "aarch64-unknown-linux-musl";

View file

@ -3,7 +3,7 @@ use std::path::PathBuf;
use bytes::Buf; use bytes::Buf;
use reqwest::Url; use reqwest::Url;
use serde::Serialize; use serde::Serialize;
use tokio::task::{spawn_blocking, JoinError}; use tokio::task::{JoinError};
use crate::actions::{Action, ActionDescription, ActionState, Actionable}; use crate::actions::{Action, ActionDescription, ActionState, Actionable};

View file

@ -22,11 +22,11 @@ impl clap::builder::TypedValueParser for ChannelValueParser {
value: &std::ffi::OsStr, value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> { ) -> Result<Self::Value, clap::Error> {
let buf = value.to_str().ok_or_else(|| { let buf = value.to_str().ok_or_else(|| {
clap::Error::raw(clap::ErrorKind::InvalidValue, "Should be all UTF-8") clap::Error::raw(clap::error::ErrorKind::InvalidValue, "Should be all UTF-8")
})?; })?;
let (name, url) = buf.split_once('=').ok_or_else(|| { let (name, url) = buf.split_once('=').ok_or_else(|| {
clap::Error::raw( clap::Error::raw(
clap::ErrorKind::InvalidValue, clap::error::ErrorKind::InvalidValue,
"Should be formatted `name=url`", "Should be formatted `name=url`",
) )
})?; })?;

View file

@ -8,8 +8,8 @@ use valuable::Valuable;
#[derive(clap::Args, Debug, Valuable)] #[derive(clap::Args, Debug, Valuable)]
pub struct Instrumentation { pub struct Instrumentation {
/// Enable debug logs, -vv for trace /// Enable debug logs, -vv for trace
#[clap(short = 'v', long, parse(from_occurrences), global = true)] #[clap(short = 'v', long, action = clap::ArgAction::Count, global = true)]
pub(crate) verbose: usize, pub(crate) verbose: u8,
} }
impl<'a> Instrumentation { impl<'a> Instrumentation {

View file

@ -15,6 +15,8 @@ pub(crate) trait CommandExecute {
} }
/// An opinionated, experimental Nix installer /// An opinionated, experimental Nix installer
///
/// Plans a Nix install, prompts for confirmation, then executes it
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[clap(version)] #[clap(version)]
pub(crate) struct HarmonicCli { pub(crate) struct HarmonicCli {
@ -80,7 +82,7 @@ impl CommandExecute for HarmonicCli {
match subcommand { match subcommand {
Some(HarmonicSubcommand::Plan(plan)) => plan.execute().await, Some(HarmonicSubcommand::Plan(plan)) => plan.execute().await,
Some(HarmonicSubcommand::Execute(execute)) => execute.execute().await, Some(HarmonicSubcommand::Execute(execute)) => execute.execute().await,
Some(HarmonicSubcommand::Revert(revert)) => revert.execute().await, Some(HarmonicSubcommand::Uninstall(revert)) => revert.execute().await,
None => { None => {
let mut settings = InstallSettings::default(); let mut settings = InstallSettings::default();

View file

@ -6,7 +6,7 @@ use harmonic::InstallPlan;
use crate::{cli::CommandExecute, interaction}; use crate::{cli::CommandExecute, interaction};
/// An opinionated, experimental Nix installer /// Execute an install using an existing plan
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub(crate) struct Execute { pub(crate) struct Execute {
#[clap( #[clap(

View file

@ -1,13 +1,13 @@
mod plan; mod plan;
use plan::Plan; use plan::Plan;
mod execute; mod install;
use execute::Execute; use install::Execute;
mod revert; mod uninstall;
use revert::Revert; use uninstall::Uninstall;
#[derive(Debug, clap::Subcommand)] #[derive(Debug, clap::Subcommand)]
pub(crate) enum HarmonicSubcommand { pub(crate) enum HarmonicSubcommand {
Plan(Plan), Plan(Plan),
Execute(Execute), Execute(Execute),
Revert(Revert), Uninstall(Uninstall),
} }

View file

@ -7,7 +7,7 @@ use eyre::WrapErr;
use crate::cli::{arg::ChannelValue, CommandExecute}; use crate::cli::{arg::ChannelValue, CommandExecute};
/// An opinionated, experimental Nix installer /// Plan an install that can be repeated on an identical host later
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub(crate) struct Plan { pub(crate) struct Plan {
/// Channel(s) to add by default, pass multiple times for multiple channels /// Channel(s) to add by default, pass multiple times for multiple channels

View file

@ -6,9 +6,9 @@ use harmonic::InstallPlan;
use crate::{cli::CommandExecute, interaction}; use crate::{cli::CommandExecute, interaction};
/// An opinionated, experimental Nix installer /// Uninstall a previously installed Nix (only Harmonic done installs supported)
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub(crate) struct Revert { pub(crate) struct Uninstall {
#[clap( #[clap(
long, long,
action(ArgAction::SetTrue), action(ArgAction::SetTrue),
@ -21,7 +21,7 @@ pub(crate) struct Revert {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl CommandExecute for Revert { impl CommandExecute for Uninstall {
#[tracing::instrument(skip_all, fields())] #[tracing::instrument(skip_all, fields())]
async fn execute(self) -> eyre::Result<ExitCode> { async fn execute(self) -> eyre::Result<ExitCode> {
let Self { let Self {