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::{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)]
#[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))
}
pub fn build(flake_uri: &str) -> Result<StorePath> {
let full_flake_uri = find_flake_attr(flake_uri)?;
pub fn build(flake_uri: &str, nix_options: &NixOptions) -> Result<StorePath> {
let full_flake_uri = find_flake_attr(flake_uri, nix_options)?;
log::info!("Building new system-manager generation...");
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}");
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 {
format!("{flake}#{FLAKE_ATTR}.{attr}")
}
@ -85,7 +88,7 @@ fn find_flake_attr(flake_uri: &str) -> Result<String> {
}
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));
} else {
anyhow::bail!(
@ -99,18 +102,18 @@ fn find_flake_attr(flake_uri: &str) -> Result<String> {
let hostname = hostname_os.to_string_lossy();
let default = "default";
if try_flake_attr(flake, &hostname)? {
if try_flake_attr(flake, &hostname, nix_options)? {
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));
};
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}");
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 {
log::info!("Success, using {full_uri}");
} 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.")
}
fn run_nix_build(flake_uri: &str) -> Result<process::Output> {
let output = nix_cmd()
fn run_nix_build(flake_uri: &str, nix_options: &NixOptions) -> Result<process::Output> {
let output = nix_cmd(nix_options)
.arg("build")
.arg(flake_uri)
.arg("--json")
@ -156,8 +159,8 @@ fn run_nix_build(flake_uri: &str) -> Result<process::Output> {
Ok(output)
}
fn try_nix_eval(flake: &str, attr: &str) -> Result<bool> {
let output = nix_cmd()
fn try_nix_eval(flake: &str, attr: &str, nix_options: &NixOptions) -> Result<bool> {
let output = nix_cmd(nix_options)
.arg("eval")
.arg(format!("{flake}#{FLAKE_ATTR}"))
.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");
cmd.arg("--extra-experimental-features")
.arg("nix-command flakes");
nix_options.options.iter().for_each(|option| {
cmd.arg("--option").arg(&option.0).arg(&option.1);
});
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<()> {
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::process::{self, ExitCode};
use system_manager::StorePath;
use system_manager::{NixOptions, StorePath};
#[derive(clap::Parser, Debug)]
#[command(
@ -25,6 +25,9 @@ struct Args {
/// Invoke the remote command with sudo.
/// Only useful in combination with --target-host
use_remote_sudo: bool,
#[clap(long = "nix-option", num_args = 2)]
nix_options: Option<Vec<String>>,
}
#[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
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()))
}
@ -131,8 +134,26 @@ fn go(args: Args) -> Result<()> {
action,
target_host,
use_remote_sudo,
nix_options,
} = 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 {
Action::Activate {
store_path,
@ -149,24 +170,29 @@ fn go(args: Args) -> Result<()> {
ephemeral,
&target_host,
use_remote_sudo,
&nix_options,
)
.and_then(print_store_path),
Action::Build {
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 {
optional_store_path_args: OptionalStorePathArg { maybe_store_path },
} => deactivate(maybe_store_path, &target_host, use_remote_sudo),
Action::Generate {
store_or_flake_args,
} => {
generate(store_or_flake_args, &target_host, use_remote_sudo).and_then(print_store_path)
}
} => generate(
store_or_flake_args,
&target_host,
use_remote_sudo,
&nix_options,
)
.and_then(print_store_path),
Action::Switch {
build_args: BuildArgs { flake_uri },
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)?;
do_generate(&store_path, &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(())
}
fn build(flake_uri: &str, target_host: &Option<String>) -> Result<StorePath> {
let store_path = do_build(flake_uri)?;
fn build(
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)?;
Ok(store_path)
}
fn do_build(flake_uri: &str) -> Result<StorePath> {
system_manager::generate::build(flake_uri)
fn do_build(flake_uri: &str, nix_options: &NixOptions) -> Result<StorePath> {
system_manager::generate::build(flake_uri, nix_options)
}
fn generate(
args: StoreOrFlakeArgs,
target_host: &Option<String>,
use_remote_sudo: bool,
nix_options: &NixOptions,
) -> Result<StorePath> {
match args {
StoreOrFlakeArgs {
@ -206,7 +237,7 @@ fn generate(
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)?;
do_generate(&store_path, target_host, use_remote_sudo)?;
Ok(store_path)
@ -284,6 +315,7 @@ fn prepopulate(
ephemeral: bool,
target_host: &Option<String>,
use_remote_sudo: bool,
nix_options: &NixOptions,
) -> Result<StorePath> {
match args {
StoreOrFlakeArgs {
@ -296,7 +328,7 @@ fn prepopulate(
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)?;
do_generate(&store_path, target_host, use_remote_sudo)?;
do_prepopulate(&store_path, ephemeral, target_host, use_remote_sudo)?;