From 8c9e56106ac5f1fbbd160c95f90ab5a28d386804 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Fri, 5 Dec 2025 20:01:50 -0800 Subject: [PATCH] Implement `--test` flag --- flake.nix | 4 ++++ src/bin/activate.rs | 7 +++++++ src/cli.rs | 16 +++++++++------- src/deploy.rs | 9 +++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 465f2123..d500c0b3 100644 --- a/flake.nix +++ b/flake.nix @@ -68,6 +68,9 @@ elif [[ "''${BOOT:-}" == "1" ]] then ${customSelf.boot or "echo ${final.writeScript "activate" activate}"} + elif [[ "''${TEST:-}" == "1" ]] + then + ${customSelf.test or "echo ${final.writeScript "activate" activate}"} else ${activate} fi @@ -92,6 +95,7 @@ (custom // { dryActivate = "$PROFILE/bin/switch-to-configuration dry-activate"; boot = "$PROFILE/bin/switch-to-configuration boot"; + test = "$PROFILE/bin/switch-to-configuration test"; }) base.config.system.build.toplevel '' diff --git a/src/bin/activate.rs b/src/bin/activate.rs index 9199e791..ab57fa8d 100644 --- a/src/bin/activate.rs +++ b/src/bin/activate.rs @@ -87,6 +87,10 @@ struct ActivateOpts { #[arg(long)] boot: bool, + /// Activate the configuration, but don't update the boot loader + #[arg(long)] + test: bool, + /// Path for any temporary files that may be needed during activation #[arg(long)] temp_path: PathBuf, @@ -390,6 +394,7 @@ pub async fn activate( magic_rollback: bool, dry_activate: bool, boot: bool, + test: bool, ) -> Result<(), ActivateError> { if !dry_activate { info!("Activating profile"); @@ -424,6 +429,7 @@ pub async fn activate( .env("PROFILE", activation_location) .env("DRY_ACTIVATE", if dry_activate { "1" } else { "0" }) .env("BOOT", if boot { "1" } else { "0" }) + .env("TEST", if test { "1" } else { "0" }) .current_dir(activation_location) .status() .await @@ -561,6 +567,7 @@ async fn main() -> Result<(), Box> { activate_opts.magic_rollback, activate_opts.dry_activate, activate_opts.boot, + activate_opts.test, ) .await .map_err(|x| Box::new(x) as Box), diff --git a/src/cli.rs b/src/cli.rs index 43990f5c..71b5954d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -95,11 +95,14 @@ pub struct Opts { #[arg(long)] temp_path: Option, /// Show what will be activated on the machines - #[arg(long)] + #[arg(long, conflicts_with_all = ["test", "boot"])] dry_activate: bool, /// Don't activate, but update the boot loader to boot into the new profile - #[arg(long)] + #[arg(long, conflicts_with_all = ["test", "dry_activate"])] boot: bool, + /// Activate the configuration, but don't update the boot loader + #[arg(long, conflicts_with_all = ["boot", "dry_activate"])] + test: bool, /// Revoke all previously succeeded deploys when deploying multiple profiles #[arg(long)] rollback_succeeded: Option, @@ -427,6 +430,7 @@ async fn run_deploy( debug_logs: bool, dry_activate: bool, boot: bool, + test: bool, log_dir: &Option, rollback_succeeded: bool, ) -> Result<(), RunDeployError> { @@ -611,7 +615,8 @@ async fn run_deploy( // Rollbacks adhere to the global seeting to auto_rollback and secondary // the profile's configuration for (_, deploy_data, deploy_defs) in &parts { - if let Err(e) = deploy::deploy::deploy_profile(deploy_data, deploy_defs, dry_activate, boot).await + if let Err(e) = + deploy::deploy::deploy_profile(deploy_data, deploy_defs, dry_activate, boot, test).await { error!("{}", e); if dry_activate { @@ -673,10 +678,6 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> { &deploy::LoggerType::Deploy, )?; - if opts.dry_activate && opts.boot { - error!("Cannot use both --dry-activate & --boot!"); - } - let deploys = opts .clone() .targets @@ -746,6 +747,7 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> { opts.debug_logs, opts.dry_activate, opts.boot, + opts.test, &opts.log_dir, opts.rollback_succeeded.unwrap_or(true), ) diff --git a/src/deploy.rs b/src/deploy.rs index fd535443..926d0bf9 100644 --- a/src/deploy.rs +++ b/src/deploy.rs @@ -23,6 +23,7 @@ struct ActivateCommandData<'a> { log_dir: Option<&'a str>, dry_activate: bool, boot: bool, + test: bool, } fn build_activate_command(data: &ActivateCommandData) -> String { @@ -75,6 +76,10 @@ fn build_activate_command(data: &ActivateCommandData) -> String { self_activate_command = format!("{} --boot", self_activate_command); } + if data.test { + self_activate_command = format!("{} --test", self_activate_command); + } + if let Some(sudo_cmd) = &data.sudo { self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); } @@ -92,6 +97,7 @@ fn test_activation_command_builder() { let auto_rollback = true; let dry_activate = false; let boot = false; + let test = false; let temp_path = Path::new("/tmp"); let confirm_timeout = 30; let magic_rollback = true; @@ -111,6 +117,7 @@ fn test_activation_command_builder() { log_dir, dry_activate, boot, + test, }), "sudo -u test /nix/store/blah/etc/activate-rs --debug-logs --log-dir /tmp/something.txt activate '/nix/store/blah/etc' --profile-path '/blah/profiles/test' --temp-path '/tmp' --confirm-timeout 30 --magic-rollback --auto-rollback" .to_string(), @@ -354,6 +361,7 @@ pub async fn deploy_profile( deploy_defs: &super::DeployDefs, dry_activate: bool, boot: bool, + test: bool, ) -> Result<(), DeployProfileError> { if !dry_activate { info!( @@ -387,6 +395,7 @@ pub async fn deploy_profile( log_dir: deploy_data.log_dir, dry_activate, boot, + test, }); debug!("Constructed activation command: {}", self_activate_command);