From d35edb976f8da04c1f04471133814b62ab2c25b4 Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Thu, 6 Oct 2022 12:21:49 -0700 Subject: [PATCH] Tidy and flesh out README --- Cargo.lock | 20 ++---- Cargo.toml | 2 +- README.md | 67 +++++++++++++++++-- flake.nix | 7 +- src/actions/base/fetch_nix.rs | 2 +- src/cli/arg/channel_value.rs | 4 +- src/cli/arg/instrumentation.rs | 4 +- src/cli/mod.rs | 4 +- src/cli/subcommand/{execute.rs => install.rs} | 2 +- src/cli/subcommand/mod.rs | 10 +-- src/cli/subcommand/plan.rs | 2 +- .../subcommand/{revert.rs => uninstall.rs} | 6 +- 12 files changed, 89 insertions(+), 41 deletions(-) rename src/cli/subcommand/{execute.rs => install.rs} (96%) rename src/cli/subcommand/{revert.rs => uninstall.rs} (90%) diff --git a/Cargo.lock b/Cargo.lock index 37dccd1..2a3a9e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -264,26 +264,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.2.20" +version = "4.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" +checksum = "3b1a0a4208c6c483b952ad35c6eed505fc13b46f08f631b81e828084a9318d74" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", - "indexmap", "once_cell", "strsim", "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "4.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "db342ce9fda24fb191e2ed4e102055a4d381c1086a06630174cd8da8d5d917ce" dependencies = [ "heck", "proc-macro-error", @@ -294,9 +292,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" dependencies = [ "os_str_bytes", ] @@ -1443,12 +1441,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - [[package]] name = "thiserror" version = "1.0.33" diff --git a/Cargo.toml b/Cargo.toml index a1f1505..2109769 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ resolver = "2" async-tar = "0.4.2" async-trait = "0.1.57" atty = "0.2.14" -clap = { version = "3.2.20", features = ["derive", "env"] } +clap = { version = "4", features = ["derive", "env"] } color-eyre = "0.6.2" crossterm = { version = "0.25.0", features = ["event-stream"] } eyre = "0.6.8" diff --git a/README.md b/README.md index d62683a..180838c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,67 @@ # 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 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) aarch64 Linux with systemd init -* (Planned) x86_64 MacOS -* (Planned) aarch64 MacOS +Planned support: -Harmonic may also support some form of NixOS install in the future. \ No newline at end of file +* [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. \ No newline at end of file diff --git a/flake.nix b/flake.nix index 1a96705..470cc93 100644 --- a/flake.nix +++ b/flake.nix @@ -121,17 +121,18 @@ }; in rec { - default = harmonic; harmonic = naerskLib.buildPackage (sharedAttrs // { }); - } // lib.optionalAttrs (system == "x86_64-linux") { + } // lib.optionalAttrs (system == "x86_64-linux") rec { + default = harmonicStatic; harmonicStatic = naerskLib.buildPackage (sharedAttrs // { CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; OPENSSL_LIB_DIR = "${pkgs.pkgsStatic.openssl.out}/lib"; OPENSSL_INCLUDE_DIR = "${pkgs.pkgsStatic.openssl.dev}"; }); - } // lib.optionalAttrs (system == "aarch64-linux") { + } // lib.optionalAttrs (system == "aarch64-linux") rec { + default = harmonicStatic; harmonicStatic = naerskLib.buildPackage (sharedAttrs // { CARGO_BUILD_TARGET = "aarch64-unknown-linux-musl"; diff --git a/src/actions/base/fetch_nix.rs b/src/actions/base/fetch_nix.rs index d2cac3d..1b32bc0 100644 --- a/src/actions/base/fetch_nix.rs +++ b/src/actions/base/fetch_nix.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use bytes::Buf; use reqwest::Url; use serde::Serialize; -use tokio::task::{spawn_blocking, JoinError}; +use tokio::task::{JoinError}; use crate::actions::{Action, ActionDescription, ActionState, Actionable}; diff --git a/src/cli/arg/channel_value.rs b/src/cli/arg/channel_value.rs index 9cd30ee..8193079 100644 --- a/src/cli/arg/channel_value.rs +++ b/src/cli/arg/channel_value.rs @@ -22,11 +22,11 @@ impl clap::builder::TypedValueParser for ChannelValueParser { value: &std::ffi::OsStr, ) -> Result { 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(|| { clap::Error::raw( - clap::ErrorKind::InvalidValue, + clap::error::ErrorKind::InvalidValue, "Should be formatted `name=url`", ) })?; diff --git a/src/cli/arg/instrumentation.rs b/src/cli/arg/instrumentation.rs index 272535e..9ca2b96 100644 --- a/src/cli/arg/instrumentation.rs +++ b/src/cli/arg/instrumentation.rs @@ -8,8 +8,8 @@ use valuable::Valuable; #[derive(clap::Args, Debug, Valuable)] pub struct Instrumentation { /// Enable debug logs, -vv for trace - #[clap(short = 'v', long, parse(from_occurrences), global = true)] - pub(crate) verbose: usize, + #[clap(short = 'v', long, action = clap::ArgAction::Count, global = true)] + pub(crate) verbose: u8, } impl<'a> Instrumentation { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index abb6cea..86d08df 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -15,6 +15,8 @@ pub(crate) trait CommandExecute { } /// An opinionated, experimental Nix installer +/// +/// Plans a Nix install, prompts for confirmation, then executes it #[derive(Debug, Parser)] #[clap(version)] pub(crate) struct HarmonicCli { @@ -80,7 +82,7 @@ impl CommandExecute for HarmonicCli { match subcommand { Some(HarmonicSubcommand::Plan(plan)) => plan.execute().await, Some(HarmonicSubcommand::Execute(execute)) => execute.execute().await, - Some(HarmonicSubcommand::Revert(revert)) => revert.execute().await, + Some(HarmonicSubcommand::Uninstall(revert)) => revert.execute().await, None => { let mut settings = InstallSettings::default(); diff --git a/src/cli/subcommand/execute.rs b/src/cli/subcommand/install.rs similarity index 96% rename from src/cli/subcommand/execute.rs rename to src/cli/subcommand/install.rs index b925584..7803e22 100644 --- a/src/cli/subcommand/execute.rs +++ b/src/cli/subcommand/install.rs @@ -6,7 +6,7 @@ use harmonic::InstallPlan; use crate::{cli::CommandExecute, interaction}; -/// An opinionated, experimental Nix installer +/// Execute an install using an existing plan #[derive(Debug, Parser)] pub(crate) struct Execute { #[clap( diff --git a/src/cli/subcommand/mod.rs b/src/cli/subcommand/mod.rs index 01256a7..231ac29 100644 --- a/src/cli/subcommand/mod.rs +++ b/src/cli/subcommand/mod.rs @@ -1,13 +1,13 @@ mod plan; use plan::Plan; -mod execute; -use execute::Execute; -mod revert; -use revert::Revert; +mod install; +use install::Execute; +mod uninstall; +use uninstall::Uninstall; #[derive(Debug, clap::Subcommand)] pub(crate) enum HarmonicSubcommand { Plan(Plan), Execute(Execute), - Revert(Revert), + Uninstall(Uninstall), } diff --git a/src/cli/subcommand/plan.rs b/src/cli/subcommand/plan.rs index 51b2d5e..ba392d8 100644 --- a/src/cli/subcommand/plan.rs +++ b/src/cli/subcommand/plan.rs @@ -7,7 +7,7 @@ use eyre::WrapErr; 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)] pub(crate) struct Plan { /// Channel(s) to add by default, pass multiple times for multiple channels diff --git a/src/cli/subcommand/revert.rs b/src/cli/subcommand/uninstall.rs similarity index 90% rename from src/cli/subcommand/revert.rs rename to src/cli/subcommand/uninstall.rs index fd510c8..f2e04b2 100644 --- a/src/cli/subcommand/revert.rs +++ b/src/cli/subcommand/uninstall.rs @@ -6,9 +6,9 @@ use harmonic::InstallPlan; use crate::{cli::CommandExecute, interaction}; -/// An opinionated, experimental Nix installer +/// Uninstall a previously installed Nix (only Harmonic done installs supported) #[derive(Debug, Parser)] -pub(crate) struct Revert { +pub(crate) struct Uninstall { #[clap( long, action(ArgAction::SetTrue), @@ -21,7 +21,7 @@ pub(crate) struct Revert { } #[async_trait::async_trait] -impl CommandExecute for Revert { +impl CommandExecute for Uninstall { #[tracing::instrument(skip_all, fields())] async fn execute(self) -> eyre::Result { let Self {