Allow passing options to nix.

This commit is contained in:
r-vdp 2023-05-25 18:36:58 +02:00
parent cecf202ff4
commit b0430700b5
No known key found for this signature in database
3 changed files with 76 additions and 28 deletions

View file

@ -5,7 +5,9 @@ use std::fs::DirBuilder;
use std::path::Path; use std::path::Path;
use std::{fs, process, str}; use std::{fs, process, str};
use super::{create_store_link, StorePath, FLAKE_ATTR, GCROOT_PATH, PROFILE_DIR, PROFILE_NAME}; use super::{
create_store_link, NixOptions, StorePath, FLAKE_ATTR, GCROOT_PATH, PROFILE_DIR, PROFILE_NAME,
};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -58,17 +60,18 @@ fn create_gcroot(gcroot_path: &str, profile_path: &Path) -> Result<()> {
create_store_link(&store_path, Path::new(gcroot_path)) create_store_link(&store_path, Path::new(gcroot_path))
} }
pub fn build(flake_uri: &str) -> Result<StorePath> { pub fn build(flake_uri: &str, nix_options: &NixOptions) -> Result<StorePath> {
let full_flake_uri = find_flake_attr(flake_uri)?; let full_flake_uri = find_flake_attr(flake_uri, nix_options)?;
log::info!("Building new system-manager generation..."); log::info!("Building new system-manager generation...");
log::info!("Running nix build..."); log::info!("Running nix build...");
let store_path = run_nix_build(full_flake_uri.as_ref()).and_then(get_store_path)?; let store_path =
run_nix_build(full_flake_uri.as_ref(), nix_options).and_then(get_store_path)?;
log::info!("Built system-manager profile {store_path}"); log::info!("Built system-manager profile {store_path}");
Ok(store_path) Ok(store_path)
} }
fn find_flake_attr(flake_uri: &str) -> Result<String> { fn find_flake_attr(flake_uri: &str, nix_options: &NixOptions) -> Result<String> {
fn full_uri(flake: &str, attr: &str) -> String { fn full_uri(flake: &str, attr: &str) -> String {
format!("{flake}#{FLAKE_ATTR}.{attr}") format!("{flake}#{FLAKE_ATTR}.{attr}")
} }
@ -85,7 +88,7 @@ fn find_flake_attr(flake_uri: &str) -> Result<String> {
} }
if let Some(attr) = attr { if let Some(attr) = attr {
if try_flake_attr(flake, attr)? { if try_flake_attr(flake, attr, nix_options)? {
return Ok(full_uri(flake, attr)); return Ok(full_uri(flake, attr));
} else { } else {
anyhow::bail!( anyhow::bail!(
@ -99,18 +102,18 @@ fn find_flake_attr(flake_uri: &str) -> Result<String> {
let hostname = hostname_os.to_string_lossy(); let hostname = hostname_os.to_string_lossy();
let default = "default"; let default = "default";
if try_flake_attr(flake, &hostname)? { if try_flake_attr(flake, &hostname, nix_options)? {
return Ok(full_uri(flake, &hostname)); return Ok(full_uri(flake, &hostname));
} else if try_flake_attr(flake, default)? { } else if try_flake_attr(flake, default, nix_options)? {
return Ok(full_uri(flake, default)); return Ok(full_uri(flake, default));
}; };
anyhow::bail!("No suitable flake attribute found, giving up."); anyhow::bail!("No suitable flake attribute found, giving up.");
} }
fn try_flake_attr(flake: &str, attr: &str) -> Result<bool> { fn try_flake_attr(flake: &str, attr: &str, nix_options: &NixOptions) -> Result<bool> {
let full_uri = format!("{flake}#{FLAKE_ATTR}.{attr}"); let full_uri = format!("{flake}#{FLAKE_ATTR}.{attr}");
log::info!("Trying flake URI: {full_uri}..."); log::info!("Trying flake URI: {full_uri}...");
let status = try_nix_eval(flake, attr)?; let status = try_nix_eval(flake, attr, nix_options)?;
if status { if status {
log::info!("Success, using {full_uri}"); log::info!("Success, using {full_uri}");
} else { } else {
@ -143,8 +146,8 @@ fn parse_nix_build_output(output: String) -> Result<StorePath> {
anyhow::bail!("Multiple build results were returned, we cannot handle that yet.") anyhow::bail!("Multiple build results were returned, we cannot handle that yet.")
} }
fn run_nix_build(flake_uri: &str) -> Result<process::Output> { fn run_nix_build(flake_uri: &str, nix_options: &NixOptions) -> Result<process::Output> {
let output = nix_cmd() let output = nix_cmd(nix_options)
.arg("build") .arg("build")
.arg(flake_uri) .arg(flake_uri)
.arg("--json") .arg("--json")
@ -156,8 +159,8 @@ fn run_nix_build(flake_uri: &str) -> Result<process::Output> {
Ok(output) Ok(output)
} }
fn try_nix_eval(flake: &str, attr: &str) -> Result<bool> { fn try_nix_eval(flake: &str, attr: &str, nix_options: &NixOptions) -> Result<bool> {
let output = nix_cmd() let output = nix_cmd(nix_options)
.arg("eval") .arg("eval")
.arg(format!("{flake}#{FLAKE_ATTR}")) .arg(format!("{flake}#{FLAKE_ATTR}"))
.arg("--json") .arg("--json")
@ -175,9 +178,12 @@ fn try_nix_eval(flake: &str, attr: &str) -> Result<bool> {
} }
} }
fn nix_cmd() -> process::Command { fn nix_cmd(nix_options: &NixOptions) -> process::Command {
let mut cmd = process::Command::new("nix"); let mut cmd = process::Command::new("nix");
cmd.arg("--extra-experimental-features") cmd.arg("--extra-experimental-features")
.arg("nix-command flakes"); .arg("nix-command flakes");
nix_options.options.iter().for_each(|option| {
cmd.arg("--option").arg(&option.0).arg(&option.1);
});
cmd cmd
} }

View file

@ -99,6 +99,16 @@ impl<'a> From<&'a StorePath> for Cow<'a, StorePath> {
} }
} }
pub struct NixOptions {
options: Vec<(String, String)>,
}
impl NixOptions {
pub fn new(options: Vec<(String, String)>) -> Self {
Self { options }
}
}
fn create_store_link(store_path: &StorePath, from: &Path) -> Result<()> { fn create_store_link(store_path: &StorePath, from: &Path) -> Result<()> {
create_link(Path::new(&store_path.store_path), from) create_link(Path::new(&store_path.store_path), from)
} }

View file

@ -4,7 +4,7 @@ use std::ffi::OsString;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{self, ExitCode}; use std::process::{self, ExitCode};
use system_manager::StorePath; use system_manager::{NixOptions, StorePath};
#[derive(clap::Parser, Debug)] #[derive(clap::Parser, Debug)]
#[command( #[command(
@ -25,6 +25,9 @@ struct Args {
/// Invoke the remote command with sudo. /// Invoke the remote command with sudo.
/// Only useful in combination with --target-host /// Only useful in combination with --target-host
use_remote_sudo: bool, use_remote_sudo: bool,
#[clap(long = "nix-option", num_args = 2)]
nix_options: Option<Vec<String>>,
} }
#[derive(clap::Args, Debug)] #[derive(clap::Args, Debug)]
@ -122,7 +125,7 @@ enum Action {
// TODO: create a general lock while we are running to avoid running system-manager concurrently // TODO: create a general lock while we are running to avoid running system-manager concurrently
fn main() -> ExitCode { fn main() -> ExitCode {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
handle_toplevel_error(go(Args::parse())) handle_toplevel_error(go(Args::parse()))
} }
@ -131,8 +134,26 @@ fn go(args: Args) -> Result<()> {
action, action,
target_host, target_host,
use_remote_sudo, use_remote_sudo,
nix_options,
} = args; } = args;
let nix_options = NixOptions::new(nix_options.map_or(Vec::new(), |vals| {
vals.chunks(2)
.map(|slice| {
(
slice
.get(0)
.expect("Error parsing nix-option values")
.to_owned(),
slice
.get(1)
.expect("Error parsing nix-option values")
.to_owned(),
)
})
.collect::<Vec<_>>()
}));
match action { match action {
Action::Activate { Action::Activate {
store_path, store_path,
@ -149,24 +170,29 @@ fn go(args: Args) -> Result<()> {
ephemeral, ephemeral,
&target_host, &target_host,
use_remote_sudo, use_remote_sudo,
&nix_options,
) )
.and_then(print_store_path), .and_then(print_store_path),
Action::Build { Action::Build {
build_args: BuildArgs { flake_uri }, build_args: BuildArgs { flake_uri },
} => build(&flake_uri, &target_host).and_then(print_store_path), } => build(&flake_uri, &target_host, &nix_options).and_then(print_store_path),
Action::Deactivate { Action::Deactivate {
optional_store_path_args: OptionalStorePathArg { maybe_store_path }, optional_store_path_args: OptionalStorePathArg { maybe_store_path },
} => deactivate(maybe_store_path, &target_host, use_remote_sudo), } => deactivate(maybe_store_path, &target_host, use_remote_sudo),
Action::Generate { Action::Generate {
store_or_flake_args, store_or_flake_args,
} => { } => generate(
generate(store_or_flake_args, &target_host, use_remote_sudo).and_then(print_store_path) store_or_flake_args,
} &target_host,
use_remote_sudo,
&nix_options,
)
.and_then(print_store_path),
Action::Switch { Action::Switch {
build_args: BuildArgs { flake_uri }, build_args: BuildArgs { flake_uri },
activation_args: ActivationArgs { ephemeral }, activation_args: ActivationArgs { ephemeral },
} => { } => {
let store_path = do_build(&flake_uri)?; let store_path = do_build(&flake_uri, &nix_options)?;
copy_closure(&store_path, &target_host)?; copy_closure(&store_path, &target_host)?;
do_generate(&store_path, &target_host, use_remote_sudo)?; do_generate(&store_path, &target_host, use_remote_sudo)?;
activate(&store_path, ephemeral, &target_host, use_remote_sudo) activate(&store_path, ephemeral, &target_host, use_remote_sudo)
@ -180,20 +206,25 @@ fn print_store_path<SP: AsRef<StorePath>>(store_path: SP) -> Result<()> {
Ok(()) Ok(())
} }
fn build(flake_uri: &str, target_host: &Option<String>) -> Result<StorePath> { fn build(
let store_path = do_build(flake_uri)?; flake_uri: &str,
target_host: &Option<String>,
nix_options: &NixOptions,
) -> Result<StorePath> {
let store_path = do_build(flake_uri, nix_options)?;
copy_closure(&store_path, target_host)?; copy_closure(&store_path, target_host)?;
Ok(store_path) Ok(store_path)
} }
fn do_build(flake_uri: &str) -> Result<StorePath> { fn do_build(flake_uri: &str, nix_options: &NixOptions) -> Result<StorePath> {
system_manager::generate::build(flake_uri) system_manager::generate::build(flake_uri, nix_options)
} }
fn generate( fn generate(
args: StoreOrFlakeArgs, args: StoreOrFlakeArgs,
target_host: &Option<String>, target_host: &Option<String>,
use_remote_sudo: bool, use_remote_sudo: bool,
nix_options: &NixOptions,
) -> Result<StorePath> { ) -> Result<StorePath> {
match args { match args {
StoreOrFlakeArgs { StoreOrFlakeArgs {
@ -206,7 +237,7 @@ fn generate(
maybe_flake_uri: Some(flake_uri), maybe_flake_uri: Some(flake_uri),
}, },
} => { } => {
let store_path = do_build(&flake_uri)?; let store_path = do_build(&flake_uri, nix_options)?;
copy_closure(&store_path, target_host)?; copy_closure(&store_path, target_host)?;
do_generate(&store_path, target_host, use_remote_sudo)?; do_generate(&store_path, target_host, use_remote_sudo)?;
Ok(store_path) Ok(store_path)
@ -284,6 +315,7 @@ fn prepopulate(
ephemeral: bool, ephemeral: bool,
target_host: &Option<String>, target_host: &Option<String>,
use_remote_sudo: bool, use_remote_sudo: bool,
nix_options: &NixOptions,
) -> Result<StorePath> { ) -> Result<StorePath> {
match args { match args {
StoreOrFlakeArgs { StoreOrFlakeArgs {
@ -296,7 +328,7 @@ fn prepopulate(
maybe_flake_uri: Some(flake_uri), maybe_flake_uri: Some(flake_uri),
}, },
} => { } => {
let store_path = do_build(&flake_uri)?; let store_path = do_build(&flake_uri, nix_options)?;
copy_closure(&store_path, target_host)?; copy_closure(&store_path, target_host)?;
do_generate(&store_path, target_host, use_remote_sudo)?; do_generate(&store_path, target_host, use_remote_sudo)?;
do_prepopulate(&store_path, ephemeral, target_host, use_remote_sudo)?; do_prepopulate(&store_path, ephemeral, target_host, use_remote_sudo)?;