diff options
author | stuebinm | 2025-05-04 16:04:07 +0200 |
---|---|---|
committer | stuebinm | 2025-05-04 17:05:05 +0200 |
commit | 98b947c731f4f74119e208cfe16df90181991a48 (patch) | |
tree | bf50d87ef79e1251d2f7384e078f06341af804c0 | |
parent | aa07eb05537d4cd025e2310397a6adcedfe72c76 (diff) |
allow non-flake builds with flake-enabled nixnonflake-build-flag
This adds a --file option, which causes deploy-rs to always use its
non-flake logic. Before this change, we would ask nix if it understood
flakes, and only if not fall back to the non-flake logic. This has
utility assuming one has a nix flake repository with flake-compat set
up, but prevents use with alternative source-managing tools such as niv
or npins if nix has the "flakes" experimental feature enabled.
-rw-r--r-- | src/cli.rs | 33 | ||||
-rw-r--r-- | src/lib.rs | 59 |
2 files changed, 87 insertions, 5 deletions
@@ -30,6 +30,9 @@ pub struct Opts { /// A list of flakes to deploy alternatively #[clap(long, group = "deploy")] targets: Option<Vec<String>>, + /// Treat targets as files instead of flakes + #[clap(short, long)] + file: Option<String>, /// Check signatures when using `nix copy` #[clap(short, long)] checksigs: bool, @@ -672,15 +675,28 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> { error!("Cannot use both --dry-activate & --boot!"); } + // if opts.file.is_some() && (opts.targets.is_some() || opts.target.is_some()) { + // error!("When using --file, only one target is supported!") + // } + let deploys = opts .clone() .targets .unwrap_or_else(|| vec![opts.clone().target.unwrap_or_else(|| ".".to_string())]); - let deploy_flakes: Vec<DeployFlake> = deploys + let deploy_flakes: Vec<DeployFlake> = + if let Some(file) = &opts.file { + deploys + .iter() + .map(|f| deploy::parse_file(file.as_str(), f.as_str())) + .collect::<Result<Vec<DeployFlake>, ParseFlakeError>>()? + } + else { + deploys .iter() .map(|f| deploy::parse_flake(f.as_str())) - .collect::<Result<Vec<DeployFlake>, ParseFlakeError>>()?; + .collect::<Result<Vec<DeployFlake>, ParseFlakeError>>()? + }; let cmd_overrides = deploy::CmdOverrides { ssh_user: opts.ssh_user, @@ -700,22 +716,29 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> { }; let supports_flakes = test_flake_support().await.map_err(RunError::FlakeTest)?; + let do_not_want_flakes = opts.file.is_some(); if !supports_flakes { warn!("A Nix version without flakes support was detected, support for this is work in progress"); } + if do_not_want_flakes { + warn!("The --file option for deployments without flakes is experimental"); + } + + let using_flakes = supports_flakes && !do_not_want_flakes; + if !opts.skip_checks { for deploy_flake in &deploy_flakes { - check_deployment(supports_flakes, deploy_flake.repo, &opts.extra_build_args).await?; + check_deployment(using_flakes, deploy_flake.repo, &opts.extra_build_args).await?; } } let result_path = opts.result_path.as_deref(); - let data = get_deployment_data(supports_flakes, &deploy_flakes, &opts.extra_build_args).await?; + let data = get_deployment_data(using_flakes, &deploy_flakes, &opts.extra_build_args).await?; run_deploy( deploy_flakes, data, - supports_flakes, + using_flakes, opts.checksigs, opts.interactive, &cmd_overrides, @@ -315,6 +315,65 @@ fn test_parse_flake() { ); } +pub fn parse_file<'a>(file: &'a str, attribute: &'a str) -> Result<DeployFlake<'a>, ParseFlakeError> { + let repo = file; //format!("./{file}"); + + let mut node: Option<String> = None; + let mut profile: Option<String> = None; + + let ast = rnix::parse(attribute); + + let first_child = match ast.root().node().first_child() { + Some(x) => x, + None => { + return Ok(DeployFlake { + repo: &repo, + node: None, + profile: None, + }) + } + }; + + let mut node_over = false; + + for entry in first_child.children_with_tokens() { + let x: Option<String> = match (entry.kind(), node_over) { + (TOKEN_DOT, false) => { + node_over = true; + None + } + (TOKEN_DOT, true) => { + return Err(ParseFlakeError::PathTooLong); + } + (NODE_IDENT, _) => Some(entry.into_node().unwrap().text().to_string()), + (TOKEN_IDENT, _) => Some(entry.into_token().unwrap().text().to_string()), + (NODE_STRING, _) => { + let c = entry + .into_node() + .unwrap() + .children_with_tokens() + .nth(1) + .unwrap(); + + Some(c.into_token().unwrap().text().to_string()) + } + _ => return Err(ParseFlakeError::Unrecognized), + }; + + if !node_over { + node = x; + } else { + profile = x; + } + } + + Ok(DeployFlake { + repo: &repo, + node, + profile, + }) +} + #[derive(Debug, Clone)] pub struct DeployData<'a> { pub node_name: &'a str, |