ALPHA: wire is a tool to deploy nixos systems wire.althaea.zone/

refactor command strings to a builder struct (#376)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

authored by

marshmallow
autofix-ci[bot]
and committed by
GitHub
5c9ab149 59082d96

+155 -46
+74
crates/core/src/commands/builder.rs
··· 1 + // SPDX-License-Identifier: AGPL-3.0-or-later 2 + // Copyright 2024-2025 wire Contributors 3 + 4 + use std::fmt; 5 + 6 + pub(crate) struct CommandStringBuilder { 7 + command: String, 8 + } 9 + 10 + impl CommandStringBuilder { 11 + pub(crate) fn nix() -> Self { 12 + Self { 13 + command: "nix".to_string(), 14 + } 15 + } 16 + 17 + pub(crate) fn new<S: AsRef<str>>(s: S) -> Self { 18 + Self { 19 + command: s.as_ref().trim().to_string(), 20 + } 21 + } 22 + 23 + pub(crate) fn arg<S: AsRef<str>>(&mut self, argument: S) { 24 + let argument = argument.as_ref().trim(); 25 + self.command.push(' '); 26 + self.command.push_str(argument); 27 + } 28 + 29 + pub(crate) fn opt_arg<S: AsRef<str>>(&mut self, opt: bool, argument: S) { 30 + if !opt { 31 + return; 32 + } 33 + 34 + self.arg(argument); 35 + } 36 + 37 + pub(crate) fn args<S: AsRef<str>>(&mut self, arguments: &[S]) { 38 + for arg in arguments { 39 + self.arg(arg); 40 + } 41 + } 42 + } 43 + 44 + impl fmt::Display for CommandStringBuilder { 45 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 46 + write!(f, "{}", self.command) 47 + } 48 + } 49 + 50 + impl AsRef<str> for CommandStringBuilder { 51 + fn as_ref(&self) -> &str { 52 + &self.command 53 + } 54 + } 55 + 56 + #[cfg(test)] 57 + mod tests { 58 + use crate::commands::builder::CommandStringBuilder; 59 + 60 + #[test] 61 + fn command_builder() { 62 + let mut builder = CommandStringBuilder::new("a"); 63 + builder.arg(" b "); 64 + builder.args(&[" c ", "d", "e"]); 65 + builder.opt_arg(false, "f"); 66 + builder.opt_arg(true, "g"); 67 + 68 + assert_eq!( 69 + builder.to_string(), 70 + std::convert::AsRef::<str>::as_ref(&builder) 71 + ); 72 + assert_eq!(builder.to_string(), "a b c d e g"); 73 + } 74 + }
+31 -23
crates/core/src/commands/common.rs
··· 7 7 8 8 use crate::{ 9 9 EvalGoal, SubCommandModifiers, 10 - commands::{CommandArguments, Either, WireCommandChip, run_command, run_command_with_env}, 10 + commands::{ 11 + CommandArguments, Either, WireCommandChip, builder::CommandStringBuilder, run_command, 12 + run_command_with_env, 13 + }, 11 14 errors::{CommandError, HiveInitialisationError, HiveLibError}, 12 15 hive::{ 13 16 HiveLocation, ··· 26 29 } 27 30 28 31 pub async fn push(context: &Context<'_>, push: Push<'_>) -> Result<(), HiveLibError> { 29 - let command_string = format!( 30 - "nix --extra-experimental-features nix-command \ 31 - copy {substitute} --to ssh://{user}@{host} {path}", 32 - user = context.node.target.user, 33 - host = context.node.target.get_preferred_host()?, 34 - path = match push { 32 + let mut command_string = CommandStringBuilder::nix(); 33 + 34 + command_string.args(&["--extra-experimental-features", "nix-command", "copy"]); 35 + command_string.opt_arg( 36 + context.substitute_on_destination, 37 + "--substitute-on-destination", 38 + ); 39 + command_string.arg("--to"); 40 + command_string.args(&[ 41 + format!( 42 + "ssh://{user}@{host}", 43 + user = context.node.target.user, 44 + host = context.node.target.get_preferred_host()?, 45 + ), 46 + match push { 35 47 Push::Derivation(drv) => format!("{drv} --derivation"), 36 48 Push::Path(path) => path.clone(), 37 49 }, 38 - substitute = if context.substitute_on_destination { 39 - "--substitute-on-destination" 40 - } else { 41 - "" 42 - } 43 - ); 50 + ]); 44 51 45 52 let child = run_command_with_env( 46 53 &CommandArguments::new(command_string, context.modifiers) ··· 130 137 } 131 138 }; 132 139 133 - let command_string = format!( 134 - "nix --extra-experimental-features nix-command \ 135 - --extra-experimental-features flakes \ 136 - eval --json {mods} {attribute}", 137 - mods = if modifiers.show_trace { 138 - "--show-trace" 139 - } else { 140 - "" 141 - }, 142 - ); 140 + let mut command_string = CommandStringBuilder::nix(); 141 + command_string.args(&[ 142 + "--extra-experimental-features", 143 + "nix-command", 144 + "--extra-experimental-features", 145 + "flakes", 146 + "eval", 147 + "--json", 148 + ]); 149 + command_string.opt_arg(modifiers.show_trace, "--show-trace"); 150 + command_string.arg(&attribute); 143 151 144 152 let child = run_command( 145 153 &CommandArguments::new(command_string, modifiers)
+1
crates/core/src/commands/mod.rs
··· 18 18 hive::node::{Node, Target}, 19 19 }; 20 20 21 + pub(crate) mod builder; 21 22 pub mod common; 22 23 pub(crate) mod noninteractive; 23 24 pub(crate) mod pty;
+15 -1
crates/core/src/hive/mod.rs
··· 18 18 use tracing::{debug, info, instrument}; 19 19 20 20 use crate::cache::InspectionCache; 21 + use crate::commands::builder::CommandStringBuilder; 21 22 use crate::commands::common::evaluate_hive_attribute; 22 23 use crate::commands::{CommandArguments, Either, WireCommandChip, run_command}; 23 24 use crate::errors::{HiveInitialisationError, HiveLocationError}; ··· 206 207 uri: String, 207 208 modifiers: SubCommandModifiers, 208 209 ) -> Result<HiveLocation, HiveLibError> { 210 + let mut command_string = CommandStringBuilder::nix(); 211 + command_string.args(&[ 212 + "nix", 213 + "flake", 214 + "prefetch", 215 + "--extra-experimental-features", 216 + "nix-command", 217 + "--extra-experimental-features", 218 + "flakes", 219 + "--json", 220 + ]); 221 + command_string.arg(&uri); 222 + 209 223 let command = run_command( 210 - &CommandArguments::new(format!("nix flake prefetch --extra-experimental-features nix-command --extra-experimental-features flakes --json {uri}"), modifiers) 224 + &CommandArguments::new(command_string, modifiers) 211 225 .mode(crate::commands::ChildOutputMode::Generic), 212 226 ) 213 227 .await?;
+5 -5
crates/core/src/hive/node.rs
··· 12 12 use tokio::sync::oneshot; 13 13 use tracing::{Instrument, Level, Span, debug, error, event, instrument, trace}; 14 14 15 + use crate::commands::builder::CommandStringBuilder; 15 16 use crate::commands::common::evaluate_hive_attribute; 16 17 use crate::commands::{CommandArguments, WireCommandChip, run_command}; 17 18 use crate::errors::NetworkError; ··· 213 214 pub async fn ping(&self, modifiers: SubCommandModifiers) -> Result<(), HiveLibError> { 214 215 let host = self.target.get_preferred_host()?; 215 216 216 - let command_string = format!( 217 - "ssh {}@{host} {} exit", 218 - self.target.user, 219 - self.target.create_ssh_opts(modifiers, true)? 220 - ); 217 + let mut command_string = CommandStringBuilder::new("ssh"); 218 + command_string.arg(format!("{}@{host}", self.target.user)); 219 + command_string.arg(self.target.create_ssh_opts(modifiers, true)?); 220 + command_string.arg("exit"); 221 221 222 222 let output = run_command( 223 223 &CommandArguments::new(command_string, modifiers)
+12 -11
crates/core/src/hive/steps/activate.rs
··· 7 7 8 8 use crate::{ 9 9 HiveLibError, 10 - commands::{CommandArguments, WireCommandChip, run_command}, 10 + commands::{CommandArguments, WireCommandChip, builder::CommandStringBuilder, run_command}, 11 11 errors::{ActivationError, NetworkError}, 12 12 hive::node::{Context, ExecuteStep, Goal, SwitchToConfigurationGoal}, 13 13 }; ··· 47 47 ) -> Result<(), HiveLibError> { 48 48 info!("Setting profiles in anticipation for switch-to-configuration {goal}"); 49 49 50 - let command_string = format!("nix-env -p /nix/var/nix/profiles/system/ --set {built_path}"); 50 + let mut command_string = CommandStringBuilder::new("nix-env"); 51 + command_string.args(&["-p", "/nix/var/nix/profiles/system", "--set"]); 52 + command_string.arg(built_path); 51 53 52 54 let child = run_command( 53 55 &CommandArguments::new(command_string, ctx.modifiers) ··· 95 97 96 98 info!("Running switch-to-configuration {goal}"); 97 99 98 - let command_string = format!( 99 - "{built_path}/bin/switch-to-configuration {}", 100 - match goal { 101 - SwitchToConfigurationGoal::Switch => "switch", 102 - SwitchToConfigurationGoal::Boot => "boot", 103 - SwitchToConfigurationGoal::Test => "test", 104 - SwitchToConfigurationGoal::DryActivate => "dry-activate", 105 - } 106 - ); 100 + let mut command_string = 101 + CommandStringBuilder::new(format!("{built_path}/bin/switch-to-configuration")); 102 + command_string.arg(match goal { 103 + SwitchToConfigurationGoal::Switch => "switch", 104 + SwitchToConfigurationGoal::Boot => "boot", 105 + SwitchToConfigurationGoal::Test => "test", 106 + SwitchToConfigurationGoal::DryActivate => "dry-activate", 107 + }); 107 108 108 109 let child = run_command( 109 110 &CommandArguments::new(command_string, ctx.modifiers)
+14 -5
crates/core/src/hive/steps/build.rs
··· 7 7 8 8 use crate::{ 9 9 HiveLibError, 10 - commands::{CommandArguments, Either, WireCommandChip, run_command_with_env}, 10 + commands::{ 11 + CommandArguments, Either, WireCommandChip, builder::CommandStringBuilder, 12 + run_command_with_env, 13 + }, 11 14 hive::node::{Context, ExecuteStep, Goal}, 12 15 }; 13 16 ··· 29 32 async fn execute(&self, ctx: &mut Context<'_>) -> Result<(), HiveLibError> { 30 33 let top_level = ctx.state.evaluation.as_ref().unwrap(); 31 34 32 - let command_string = format!( 33 - "nix --extra-experimental-features nix-command \ 34 - build --print-build-logs --no-link --print-out-paths {top_level}" 35 - ); 35 + let mut command_string = CommandStringBuilder::nix(); 36 + command_string.args(&[ 37 + "--extra-experimental-features", 38 + "nix-command", 39 + "build", 40 + "--print-build-logs", 41 + "--no-link", 42 + "--print-out-paths", 43 + ]); 44 + command_string.arg(top_level.to_string()); 36 45 37 46 let status = run_command_with_env( 38 47 &CommandArguments::new(command_string, ctx.modifiers)
+3 -1
crates/core/src/hive/steps/keys.rs
··· 27 27 use tracing::{debug, instrument}; 28 28 29 29 use crate::HiveLibError; 30 + use crate::commands::builder::CommandStringBuilder; 30 31 use crate::commands::common::push; 31 32 use crate::commands::{CommandArguments, WireCommandChip, run_command}; 32 33 use crate::errors::KeyError; ··· 252 253 return Ok(()); 253 254 } 254 255 255 - let command_string = format!("{agent_directory}/bin/wire-key-agent"); 256 + let command_string = 257 + CommandStringBuilder::new(format!("{agent_directory}/bin/wire-key-agent")); 256 258 257 259 let mut child = run_command( 258 260 &CommandArguments::new(command_string, ctx.modifiers)