From 48e871d4004cb0161d1006c8fcfce2cfa47b5d71 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Mon, 3 Mar 2025 01:44:59 -0800 Subject: [PATCH] standardize running commands with interpolation / output sanitizations --- Cargo.lock | 3 + bin/core/src/api/execute/action.rs | 1 - bin/periphery/src/api/build.rs | 44 ++---- bin/periphery/src/api/compose.rs | 14 +- bin/periphery/src/api/container.rs | 68 +++------ bin/periphery/src/api/deploy.rs | 41 +++--- bin/periphery/src/api/image.rs | 7 +- bin/periphery/src/api/mod.rs | 4 +- bin/periphery/src/api/network.rs | 15 +- bin/periphery/src/api/volume.rs | 8 +- bin/periphery/src/compose.rs | 214 ++++++++--------------------- bin/periphery/src/docker.rs | 2 +- bin/periphery/src/helpers.rs | 11 -- lib/command/Cargo.toml | 5 +- lib/command/src/lib.rs | 86 ++++++++++-- lib/git/src/clone.rs | 147 ++++++-------------- lib/git/src/commit.rs | 8 +- lib/git/src/init.rs | 12 +- lib/git/src/pull.rs | 94 ++++--------- 19 files changed, 273 insertions(+), 511 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4381fe390..31dfd4398 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1069,8 +1069,11 @@ dependencies = [ name = "command" version = "1.17.0-dev-3" dependencies = [ + "anyhow", + "formatting", "komodo_client", "run_command", + "svi", ] [[package]] diff --git a/bin/core/src/api/execute/action.rs b/bin/core/src/api/execute/action.rs index 1a9cab3b9..0149fff98 100644 --- a/bin/core/src/api/execute/action.rs +++ b/bin/core/src/api/execute/action.rs @@ -123,7 +123,6 @@ impl Resolve for RunAction { "Execute Action", None, format!("deno run --allow-all {}", path.display()), - false, ) .await; diff --git a/bin/periphery/src/api/build.rs b/bin/periphery/src/api/build.rs index 504585dfd..01f8076f7 100644 --- a/bin/periphery/src/api/build.rs +++ b/bin/periphery/src/api/build.rs @@ -1,5 +1,7 @@ use anyhow::{anyhow, Context}; -use command::run_komodo_command; +use command::{ + run_komodo_command, run_komodo_command_with_interpolation, +}; use formatting::format_serror; use komodo_client::{ entities::{ @@ -67,7 +69,7 @@ impl Resolve for build::Build { Ok(should_push) => should_push, Err(e) => { logs.push(Log::error( - "docker login", + "Docker Login", format_serror( &e.context("failed to login to docker registry").into(), ), @@ -122,40 +124,23 @@ impl Resolve for build::Build { if *skip_secret_interp { let build_log = run_komodo_command( - "docker build", + "Docker Build", build_dir.as_ref(), command, - false, ) .await; logs.push(build_log); } else { - // Interpolate any missing secrets - let (command, mut replacers) = svi::interpolate_variables( - &command, - &periphery_config().secrets, - svi::Interpolator::DoubleBrackets, - true, - ) - .context( - "failed to interpolate secrets into docker build command", - )?; - replacers.extend(core_replacers); - - let mut build_log = run_komodo_command( - "docker build", + run_komodo_command_with_interpolation( + "Docker Build", build_dir.as_ref(), command, false, + &periphery_config().secrets, + &core_replacers, ) - .await; - build_log.command = - svi::replace_in_string(&build_log.command, &replacers); - build_log.stdout = - svi::replace_in_string(&build_log.stdout, &replacers); - build_log.stderr = - svi::replace_in_string(&build_log.stderr, &replacers); - logs.push(build_log); + .await + .map(|log| logs.push(log)); } cleanup_secret_env_vars(&secret_args); @@ -250,10 +235,7 @@ impl Resolve for PruneBuilders { #[instrument(name = "PruneBuilders", skip_all)] async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker builder prune -a -f"); - Ok( - run_komodo_command("prune builders", None, command, false) - .await, - ) + Ok(run_komodo_command("Prune Builders", None, command).await) } } @@ -263,6 +245,6 @@ impl Resolve for PruneBuildx { #[instrument(name = "PruneBuildx", skip_all)] async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker buildx prune -a -f"); - Ok(run_komodo_command("prune buildx", None, command, false).await) + Ok(run_komodo_command("Prune Buildx", None, command).await) } } diff --git a/bin/periphery/src/api/compose.rs b/bin/periphery/src/api/compose.rs index 23fd6c7cc..bf21e8d57 100644 --- a/bin/periphery/src/api/compose.rs +++ b/bin/periphery/src/api/compose.rs @@ -27,10 +27,9 @@ impl Resolve for ListComposeProjects { ) -> serror::Result> { let docker_compose = docker_compose(); let res = run_komodo_command( - "list projects", + "List Projects", None, format!("{docker_compose} ls --all --format json"), - false, ) .await; @@ -98,9 +97,7 @@ impl Resolve for GetComposeLog { "{docker_compose} -p {project} logs --tail {tail}{timestamps} {}", services.join(" ") ); - Ok( - run_komodo_command("get stack log", None, command, false).await, - ) + Ok(run_komodo_command("get stack log", None, command).await) } } @@ -123,10 +120,7 @@ impl Resolve for GetComposeLogSearch { "{docker_compose} -p {project} logs --tail 5000{timestamps} {} 2>&1 | {grep}", services.join(" ") ); - Ok( - run_komodo_command("get stack log grep", None, command, false) - .await, - ) + Ok(run_komodo_command("Get stack log grep", None, command).await) } } @@ -403,7 +397,6 @@ impl Resolve for ComposePull { format!( "{docker_compose} -p {project_name} -f {file_args}{additional_env_files}{env_file} pull{service_arg}", ), - false, ) .await; @@ -466,7 +459,6 @@ impl Resolve for ComposeExecution { "Compose Command", None, format!("{docker_compose} -p {project} {command}"), - false, ) .await; Ok(log) diff --git a/bin/periphery/src/api/container.rs b/bin/periphery/src/api/container.rs index a4e51858b..8ab421871 100644 --- a/bin/periphery/src/api/container.rs +++ b/bin/periphery/src/api/container.rs @@ -44,10 +44,7 @@ impl Resolve for GetContainerLog { timestamps.then_some(" --timestamps").unwrap_or_default(); let command = format!("docker logs {name} --tail {tail}{timestamps}"); - Ok( - run_komodo_command("get container log", None, command, false) - .await, - ) + Ok(run_komodo_command("Get container log", None, command).await) } } @@ -70,13 +67,8 @@ impl Resolve for GetContainerLogSearch { "docker logs {name} --tail 5000{timestamps} 2>&1 | {grep}" ); Ok( - run_komodo_command( - "get container log grep", - None, - command, - false, - ) - .await, + run_komodo_command("Get container log grep", None, command) + .await, ) } } @@ -117,10 +109,9 @@ impl Resolve for StartContainer { async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( - "docker start", + "Docker Start", None, format!("docker start {}", self.name), - false, ) .await, ) @@ -134,10 +125,9 @@ impl Resolve for RestartContainer { async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( - "docker restart", + "Docker Restart", None, format!("docker restart {}", self.name), - false, ) .await, ) @@ -151,10 +141,9 @@ impl Resolve for PauseContainer { async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( - "docker pause", + "Docker Pause", None, format!("docker pause {}", self.name), - false, ) .await, ) @@ -166,10 +155,9 @@ impl Resolve for UnpauseContainer { async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( - "docker unpause", + "Docker Unpause", None, format!("docker unpause {}", self.name), - false, ) .await, ) @@ -183,12 +171,11 @@ impl Resolve for StopContainer { async fn resolve(self, _: &super::Args) -> serror::Result { let StopContainer { name, signal, time } = self; let command = stop_container_command(&name, signal, time); - let log = - run_komodo_command("docker stop", None, command, false).await; + let log = run_komodo_command("Docker Stop", None, command).await; if log.stderr.contains("unknown flag: --signal") { let command = stop_container_command(&name, None, time); let mut log = - run_komodo_command("docker stop", None, command, false).await; + run_komodo_command("Docker Stop", None, command).await; log.stderr = format!( "old docker version: unable to use --signal flag{}", if !log.stderr.is_empty() { @@ -213,21 +200,18 @@ impl Resolve for RemoveContainer { let stop_command = stop_container_command(&name, signal, time); let command = format!("{stop_command} && docker container rm {name}"); - let log = run_komodo_command( - "docker stop and remove", - None, - command, - false, - ) - .await; + let log = + run_komodo_command("Docker Stop and Remove", None, command) + .await; if log.stderr.contains("unknown flag: --signal") { let stop_command = stop_container_command(&name, None, time); let command = format!("{stop_command} && docker container rm {name}"); let mut log = - run_komodo_command("docker stop", None, command, false).await; + run_komodo_command("Docker Stop and Remove", None, command) + .await; log.stderr = format!( - "old docker version: unable to use --signal flag{}", + "Old docker version: unable to use --signal flag{}", if !log.stderr.is_empty() { format!("\n\n{}", log.stderr) } else { @@ -252,9 +236,7 @@ impl Resolve for RenameContainer { } = self; let new = to_komodo_name(&new_name); let command = format!("docker rename {curr_name} {new}"); - Ok( - run_komodo_command("docker rename", None, command, false).await, - ) + Ok(run_komodo_command("Docker Rename", None, command).await) } } @@ -264,10 +246,7 @@ impl Resolve for PruneContainers { #[instrument(name = "PruneContainers", skip_all)] async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker container prune -f"); - Ok( - run_komodo_command("prune containers", None, command, false) - .await, - ) + Ok(run_komodo_command("Prune Containers", None, command).await) } } @@ -290,8 +269,7 @@ impl Resolve for StartAllContainers { } let command = format!("docker start {name}"); Some(async move { - run_komodo_command(&command.clone(), None, command, false) - .await + run_komodo_command(&command.clone(), None, command).await }) }, ); @@ -318,8 +296,7 @@ impl Resolve for RestartAllContainers { } let command = format!("docker restart {name}"); Some(async move { - run_komodo_command(&command.clone(), None, command, false) - .await + run_komodo_command(&command.clone(), None, command).await }) }, ); @@ -346,8 +323,7 @@ impl Resolve for PauseAllContainers { } let command = format!("docker pause {name}"); Some(async move { - run_komodo_command(&command.clone(), None, command, false) - .await + run_komodo_command(&command.clone(), None, command).await }) }, ); @@ -374,8 +350,7 @@ impl Resolve for UnpauseAllContainers { } let command = format!("docker unpause {name}"); Some(async move { - run_komodo_command(&command.clone(), None, command, false) - .await + run_komodo_command(&command.clone(), None, command).await }) }, ); @@ -405,7 +380,6 @@ impl Resolve for StopAllContainers { &format!("docker stop {name}"), None, stop_container_command(name, None, None), - false, ) .await }) diff --git a/bin/periphery/src/api/deploy.rs b/bin/periphery/src/api/deploy.rs index 109ea4998..b847cc2d2 100644 --- a/bin/periphery/src/api/deploy.rs +++ b/bin/periphery/src/api/deploy.rs @@ -1,5 +1,7 @@ use anyhow::Context; -use command::run_komodo_command; +use command::{ + run_komodo_command, run_komodo_command_with_interpolation, +}; use formatting::format_serror; use komodo_client::{ entities::{ @@ -88,33 +90,22 @@ impl Resolve for Deploy { debug!("docker run command: {command}"); if deployment.config.skip_secret_interp { - Ok(run_komodo_command("docker run", None, command, false).await) + Ok(run_komodo_command("Docker Run", None, command).await) } else { - let command = svi::interpolate_variables( - &command, + match run_komodo_command_with_interpolation( + "Docker Run", + None, + command, + false, &periphery_config().secrets, - svi::Interpolator::DoubleBrackets, - true, + &core_replacers, ) - .context( - "failed to interpolate secrets into docker run command", - ); - - let (command, mut replacers) = match command { - Ok(res) => res, - Err(e) => { - return Ok(Log::error("docker run", format!("{e:?}"))); - } - }; - - replacers.extend(core_replacers); - let mut log = - run_komodo_command("docker run", None, command, false).await; - log.command = svi::replace_in_string(&log.command, &replacers); - log.stdout = svi::replace_in_string(&log.stdout, &replacers); - log.stderr = svi::replace_in_string(&log.stderr, &replacers); - - Ok(log) + .await + { + Some(log) => Ok(log), + // The None case can not be reached, as the command is always non-empty + None => unreachable!(), + } } } } diff --git a/bin/periphery/src/api/image.rs b/bin/periphery/src/api/image.rs index aa215a9e7..a780e8d90 100644 --- a/bin/periphery/src/api/image.rs +++ b/bin/periphery/src/api/image.rs @@ -75,10 +75,9 @@ impl Resolve for PullImage { .await?; anyhow::Ok( run_komodo_command( - "docker pull", + "Docker Pull", None, format!("docker pull {name}"), - false, ) .await, ) @@ -99,7 +98,7 @@ impl Resolve for DeleteImage { #[instrument(name = "DeleteImage")] async fn resolve(self, _: &super::Args) -> serror::Result { let command = format!("docker image rm {}", self.name); - Ok(run_komodo_command("delete image", None, command, false).await) + Ok(run_komodo_command("Delete Image", None, command).await) } } @@ -109,6 +108,6 @@ impl Resolve for PruneImages { #[instrument(name = "PruneImages")] async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker image prune -a -f"); - Ok(run_komodo_command("prune images", None, command, false).await) + Ok(run_komodo_command("Prune Images", None, command).await) } } diff --git a/bin/periphery/src/api/mod.rs b/bin/periphery/src/api/mod.rs index 0076724c3..e9e3d876a 100644 --- a/bin/periphery/src/api/mod.rs +++ b/bin/periphery/src/api/mod.rs @@ -247,7 +247,7 @@ impl Resolve for RunCommand { } else { format!("cd {path} && {command}") }; - run_komodo_command("run command", None, command, false).await + run_komodo_command("run command", None, command).await }) .await .context("failure in spawned task")?; @@ -259,6 +259,6 @@ impl Resolve for PruneSystem { #[instrument(name = "PruneSystem", skip_all)] async fn resolve(self, _: &Args) -> serror::Result { let command = String::from("docker system prune -a -f --volumes"); - Ok(run_komodo_command("prune system", None, command, false).await) + Ok(run_komodo_command("Prune System", None, command).await) } } diff --git a/bin/periphery/src/api/network.rs b/bin/periphery/src/api/network.rs index 3146b73c8..e546b6eef 100644 --- a/bin/periphery/src/api/network.rs +++ b/bin/periphery/src/api/network.rs @@ -27,10 +27,7 @@ impl Resolve for CreateNetwork { None => String::new(), }; let command = format!("docker network create{driver} {name}"); - Ok( - run_komodo_command("create network", None, command, false) - .await, - ) + Ok(run_komodo_command("Create Network", None, command).await) } } @@ -40,10 +37,7 @@ impl Resolve for DeleteNetwork { #[instrument(name = "DeleteNetwork", skip(self))] async fn resolve(self, _: &super::Args) -> serror::Result { let command = format!("docker network rm {}", self.name); - Ok( - run_komodo_command("delete network", None, command, false) - .await, - ) + Ok(run_komodo_command("Delete Network", None, command).await) } } @@ -53,9 +47,6 @@ impl Resolve for PruneNetworks { #[instrument(name = "PruneNetworks", skip(self))] async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker network prune -f"); - Ok( - run_komodo_command("prune networks", None, command, false) - .await, - ) + Ok(run_komodo_command("Prune Networks", None, command).await) } } diff --git a/bin/periphery/src/api/volume.rs b/bin/periphery/src/api/volume.rs index ef72a1d1e..523e2ee78 100644 --- a/bin/periphery/src/api/volume.rs +++ b/bin/periphery/src/api/volume.rs @@ -20,9 +20,7 @@ impl Resolve for DeleteVolume { #[instrument(name = "DeleteVolume")] async fn resolve(self, _: &super::Args) -> serror::Result { let command = format!("docker volume rm {}", self.name); - Ok( - run_komodo_command("delete volume", None, command, false).await, - ) + Ok(run_komodo_command("Delete Volume", None, command).await) } } @@ -32,8 +30,6 @@ impl Resolve for PruneVolumes { #[instrument(name = "PruneVolumes")] async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker volume prune -a -f"); - Ok( - run_komodo_command("prune volumes", None, command, false).await, - ) + Ok(run_komodo_command("Prune Volumes", None, command).await) } } diff --git a/bin/periphery/src/compose.rs b/bin/periphery/src/compose.rs index 391cb0573..8fc757323 100644 --- a/bin/periphery/src/compose.rs +++ b/bin/periphery/src/compose.rs @@ -1,7 +1,10 @@ use std::{fmt::Write, path::PathBuf}; use anyhow::{anyhow, Context}; -use command::run_komodo_command; +use command::{ + run_komodo_command, run_komodo_command_multiline, + run_komodo_command_with_interpolation, +}; use formatting::format_serror; use git::environment; use komodo_client::entities::{ @@ -22,9 +25,8 @@ use resolver_api::Resolve; use tokio::fs; use crate::{ - config::periphery_config, - docker::docker_login, - helpers::{interpolate_variables, parse_extra_args}, + config::periphery_config, docker::docker_login, + helpers::parse_extra_args, }; pub fn docker_compose() -> &'static str { @@ -176,7 +178,6 @@ pub async fn compose_up( "Compose Config", run_directory.as_ref(), command, - false, ) .await; if !config_log.success { @@ -241,35 +242,20 @@ pub async fn compose_up( "Compose Build", run_directory.as_ref(), command, - false, ) .await; res.logs.push(log); } else { - let (command, mut build_replacers) = svi::interpolate_variables( - &command, - &periphery_config().secrets, - svi::Interpolator::DoubleBrackets, - true, - ).context("failed to interpolate periphery secrets into stack build command")?; - build_replacers.extend(replacers.clone()); - - let mut log = run_komodo_command( + run_komodo_command_with_interpolation( "Compose Build", run_directory.as_ref(), command, false, + &periphery_config().secrets, + &replacers, ) - .await; - - log.command = - svi::replace_in_string(&log.command, &build_replacers); - log.stdout = - svi::replace_in_string(&log.stdout, &build_replacers); - log.stderr = - svi::replace_in_string(&log.stderr, &build_replacers); - - res.logs.push(log); + .await + .map(|log| res.logs.push(log)); } if !all_logs_success(&res.logs) { @@ -289,7 +275,6 @@ pub async fn compose_up( format!( "{docker_compose} -p {project_name} -f {file_args}{env_file}{additional_env_files} pull{service_arg}", ), - false, ) .await; @@ -302,64 +287,32 @@ pub async fn compose_up( } } - if !stack.config.pre_deploy.command.is_empty() { - let pre_deploy_path = - run_directory.join(&stack.config.pre_deploy.path); - if !stack.config.skip_secret_interp { - let (full_command, mut pre_deploy_replacers) = - interpolate_variables(&stack.config.pre_deploy.command) - .context( - "failed to interpolate secrets into pre_deploy command", - )?; - pre_deploy_replacers.extend(replacers.to_owned()); - let mut pre_deploy_log = run_komodo_command( - "Pre Deploy", - pre_deploy_path.as_ref(), - &full_command, - true, - ) - .await; - - pre_deploy_log.command = svi::replace_in_string( - &pre_deploy_log.command, - &pre_deploy_replacers, - ); - pre_deploy_log.stdout = svi::replace_in_string( - &pre_deploy_log.stdout, - &pre_deploy_replacers, - ); - pre_deploy_log.stderr = svi::replace_in_string( - &pre_deploy_log.stderr, - &pre_deploy_replacers, - ); - - tracing::debug!( - "run Stack pre_deploy command | command: {} | cwd: {:?}", - pre_deploy_log.command, - pre_deploy_path - ); - - res.logs.push(pre_deploy_log); - } else { - let pre_deploy_log = run_komodo_command( - "Pre Deploy", - pre_deploy_path.as_ref(), - &stack.config.pre_deploy.command, - true, - ) - .await; - tracing::debug!( - "run Stack pre_deploy command | command: {} | cwd: {:?}", - &stack.config.pre_deploy.command, - pre_deploy_path - ); - res.logs.push(pre_deploy_log); - } - if !all_logs_success(&res.logs) { - return Err(anyhow!( - "Failed at running pre_deploy command, stopping the run." - )); - } + // Pre deploy command + let pre_deploy_path = + run_directory.join(&stack.config.pre_deploy.path); + if stack.config.skip_secret_interp { + run_komodo_command_multiline( + "Pre Deploy", + pre_deploy_path.as_ref(), + &stack.config.pre_deploy.command, + ) + .await + } else { + run_komodo_command_with_interpolation( + "Pre Deploy", + pre_deploy_path.as_ref(), + &stack.config.pre_deploy.command, + true, + &periphery_config().secrets, + &replacers, + ) + .await + } + .map(|log| res.logs.push(log)); + if !all_logs_success(&res.logs) { + return Err(anyhow!( + "Failed at running pre_deploy command, stopping the run." + )); } if stack.config.destroy_before_deploy @@ -380,38 +333,23 @@ pub async fn compose_up( ); let log = if stack.config.skip_secret_interp { - run_komodo_command( + run_komodo_command("Compose Up", run_directory.as_ref(), command) + .await + } else { + match run_komodo_command_with_interpolation( "Compose Up", run_directory.as_ref(), command, false, + &periphery_config().secrets, + &replacers, ) .await - } else { - let (command, mut compose_up_replacers) = svi::interpolate_variables( - &command, - &periphery_config().secrets, - svi::Interpolator::DoubleBrackets, - true, - ).context("failed to interpolate periphery secrets into stack run command")?; - compose_up_replacers.extend(replacers.clone()); - - let mut log = run_komodo_command( - "compose up", - run_directory.as_ref(), - command, - false, - ) - .await; - - log.command = - svi::replace_in_string(&log.command, &compose_up_replacers); - log.stdout = - svi::replace_in_string(&log.stdout, &compose_up_replacers); - log.stderr = - svi::replace_in_string(&log.stderr, &compose_up_replacers); - - log + { + Some(log) => log, + // The command is definitely non-empty, the result will never be None. + None => unreachable!(), + } }; res.deployed = log.success; @@ -419,59 +357,28 @@ pub async fn compose_up( // push the compose up command logs to keep the correct order res.logs.push(log); - if res.deployed && !stack.config.post_deploy.command.is_empty() { + if res.deployed { let post_deploy_path = run_directory.join(&stack.config.post_deploy.path); - if !stack.config.skip_secret_interp { - let (full_command, mut post_deploy_replacers) = - interpolate_variables(&stack.config.post_deploy.command) - .context( - "failed to interpolate secrets into post_deploy command", - )?; - post_deploy_replacers.extend(replacers); - let mut post_deploy_log = run_komodo_command( - "post deploy", + if stack.config.skip_secret_interp { + run_komodo_command_multiline( + "Post Deploy", post_deploy_path.as_ref(), - &full_command, - true, + &stack.config.post_deploy.command, ) - .await; - - post_deploy_log.command = svi::replace_in_string( - &post_deploy_log.command, - &post_deploy_replacers, - ); - post_deploy_log.stdout = svi::replace_in_string( - &post_deploy_log.stdout, - &post_deploy_replacers, - ); - post_deploy_log.stderr = svi::replace_in_string( - &post_deploy_log.stderr, - &post_deploy_replacers, - ); - - tracing::debug!( - "run Stack post_deploy command | command: {} | cwd: {:?}", - post_deploy_log.command, - post_deploy_path - ); - - res.logs.push(post_deploy_log); + .await } else { - let post_deploy_log = run_komodo_command( - "Post deploy", + run_komodo_command_with_interpolation( + "Post Deploy", post_deploy_path.as_ref(), &stack.config.post_deploy.command, true, + &periphery_config().secrets, + &replacers, ) - .await; - tracing::debug!( - "run Stack post_deploy command | command: {} | cwd: {:?}", - &stack.config.post_deploy.command, - post_deploy_path - ); - res.logs.push(post_deploy_log); + .await } + .map(|log| res.logs.push(log)); if !all_logs_success(&res.logs) { return Err(anyhow!( "Failed at running post_deploy command, stopping the run." @@ -781,7 +688,6 @@ async fn compose_down( "compose down", None, format!("{docker_compose} -p {project} down{service_arg}"), - false, ) .await; let success = log.success; diff --git a/bin/periphery/src/docker.rs b/bin/periphery/src/docker.rs index 6ccaa6ddf..3f32f85b3 100644 --- a/bin/periphery/src/docker.rs +++ b/bin/periphery/src/docker.rs @@ -969,7 +969,7 @@ pub async fn docker_login( #[instrument] pub async fn pull_image(image: &str) -> Log { let command = format!("docker pull {image}"); - run_komodo_command("docker pull", None, command, false).await + run_komodo_command("Docker Pull", None, command).await } pub fn stop_container_command( diff --git a/bin/periphery/src/helpers.rs b/bin/periphery/src/helpers.rs index 19ed9adb3..6a15a1263 100644 --- a/bin/periphery/src/helpers.rs +++ b/bin/periphery/src/helpers.rs @@ -86,17 +86,6 @@ pub fn log_grep( } } -pub fn interpolate_variables( - input: &str, -) -> svi::Result<(String, Vec<(String, String)>)> { - svi::interpolate_variables( - input, - &periphery_config().secrets, - svi::Interpolator::DoubleBrackets, - true, - ) -} - /// Returns path to root directory of the stack repo. pub async fn pull_or_clone_stack( stack: &Stack, diff --git a/lib/command/Cargo.toml b/lib/command/Cargo.toml index 2f28fd1d3..65568ca96 100644 --- a/lib/command/Cargo.toml +++ b/lib/command/Cargo.toml @@ -9,4 +9,7 @@ homepage.workspace = true [dependencies] komodo_client.workspace = true -run_command.workspace = true \ No newline at end of file +run_command.workspace = true +formatting.workspace = true +anyhow.workspace = true +svi.workspace = true \ No newline at end of file diff --git a/lib/command/src/lib.rs b/lib/command/src/lib.rs index 2cbf3700f..3c2df0ae9 100644 --- a/lib/command/src/lib.rs +++ b/lib/command/src/lib.rs @@ -1,36 +1,96 @@ -use std::path::Path; +use std::{collections::HashMap, path::Path}; +use anyhow::Context; +use formatting::format_serror; use komodo_client::{ entities::{komodo_timestamp, update::Log}, parsers::parse_multiline_command, }; use run_command::{async_run_command, CommandOutput}; +use svi::Interpolator; -/// If `parse_multiline: true`, parses commands out of multiline string -/// and chains them together with '&&'. -/// Supports full line and end of line comments. -/// See [parse_multiline_command]. pub async fn run_komodo_command( stage: &str, path: impl Into>, command: impl AsRef, - parse_multiline: bool, ) -> Log { - let command = if parse_multiline { - parse_multiline_command(command) + let command = if let Some(path) = path.into() { + format!("cd {} && {}", path.display(), command.as_ref()) } else { command.as_ref().to_string() }; - let command = if let Some(path) = path.into() { - format!("cd {} && {command}", path.display(),) - } else { - command - }; let start_ts = komodo_timestamp(); let output = async_run_command(&command).await; output_into_log(stage, command, start_ts, output) } +/// Parses commands out of multiline string +/// and chains them together with '&&'. +/// Supports full line and end of line comments. +/// See [parse_multiline_command]. +/// +/// The result may be None if the command is empty after parsing, +/// ie if all the lines are commented out. +pub async fn run_komodo_command_multiline( + stage: &str, + path: impl Into>, + command: impl AsRef, +) -> Option { + let command = parse_multiline_command(command); + if command.is_empty() { + return None; + } + Some(run_komodo_command(stage, path, command).await) +} + +/// Interpolates provided secrets into (potentially multiline) command, +/// executes the command, and sanitizes the output to avoid exposing the secrets. +/// +/// Checks to make sure the command is non-empty after being multiline-parsed. +/// +/// If `parse_multiline: true`, parses commands out of multiline string +/// and chains them together with '&&'. +/// Supports full line and end of line comments. +/// See [parse_multiline_command]. +pub async fn run_komodo_command_with_interpolation( + stage: &str, + path: impl Into>, + command: impl AsRef, + parse_multiline: bool, + secrets: &HashMap, + additional_replacers: &[(String, String)], +) -> Option { + let (command, mut replacers) = match svi::interpolate_variables( + command.as_ref(), + secrets, + Interpolator::DoubleBrackets, + true, + ) + .context("Failed to interpolate secrets") + { + Ok(res) => res, + Err(e) => { + return Some(Log::error( + &format!("{stage} - Interpolate Secrets"), + format_serror(&e.into()), + )) + } + }; + let mut log = if parse_multiline { + run_komodo_command_multiline(stage, path, command).await + } else { + run_komodo_command(stage, path, command).await.into() + }?; + + // Sanitize the command and output + replacers.extend_from_slice(additional_replacers); + log.command = svi::replace_in_string(&log.command, &replacers); + log.stdout = svi::replace_in_string(&log.stdout, &replacers); + log.stderr = svi::replace_in_string(&log.stderr, &replacers); + + Some(log) +} + pub fn output_into_log( stage: &str, command: String, diff --git a/lib/git/src/clone.rs b/lib/git/src/clone.rs index f018fb8cd..32e67c34d 100644 --- a/lib/git/src/clone.rs +++ b/lib/git/src/clone.rs @@ -1,7 +1,9 @@ use std::{collections::HashMap, path::Path}; -use anyhow::Context; -use command::run_komodo_command; +use command::{ + run_komodo_command, run_komodo_command_multiline, + run_komodo_command_with_interpolation, +}; use formatting::format_serror; use komodo_client::entities::{ all_logs_success, komodo_timestamp, update::Log, CloneArgs, @@ -100,112 +102,48 @@ where }; if let Some(command) = args.on_clone { - if !command.command.is_empty() { - let on_clone_path = repo_dir.join(&command.path); - if let Some(secrets) = secrets { - let (full_command, mut replacers) = - svi::interpolate_variables( - &command.command, - secrets, - svi::Interpolator::DoubleBrackets, - true, - ) - .context( - "failed to interpolate secrets into on_clone command", - )?; - replacers.extend(core_replacers.to_owned()); - let mut on_clone_log = run_komodo_command( - "on clone", - on_clone_path.as_ref(), - full_command, - true, - ) - .await; - - on_clone_log.command = - svi::replace_in_string(&on_clone_log.command, &replacers); - on_clone_log.stdout = - svi::replace_in_string(&on_clone_log.stdout, &replacers); - on_clone_log.stderr = - svi::replace_in_string(&on_clone_log.stderr, &replacers); - - tracing::debug!( - "run repo on_clone command | command: {} | cwd: {:?}", - on_clone_log.command, - on_clone_path - ); - - logs.push(on_clone_log); - } else { - let on_clone_log = run_komodo_command( - "on clone", - on_clone_path.as_ref(), - &command.command, - true, - ) - .await; - tracing::debug!( - "run repo on_clone command | command: {} | cwd: {:?}", - command.command, - on_clone_path - ); - logs.push(on_clone_log); - } + let on_clone_path = repo_dir.join(&command.path); + if let Some(secrets) = secrets { + run_komodo_command_with_interpolation( + "On Clone", + Some(on_clone_path.as_path()), + &command.command, + true, + secrets, + core_replacers, + ) + .await + } else { + run_komodo_command_multiline( + "On Clone", + Some(on_clone_path.as_path()), + &command.command, + ) + .await } + .map(|log| logs.push(log)); } if let Some(command) = args.on_pull { - if !command.command.is_empty() { - let on_pull_path = repo_dir.join(&command.path); - if let Some(secrets) = secrets { - let (full_command, mut replacers) = - svi::interpolate_variables( - &command.command, - secrets, - svi::Interpolator::DoubleBrackets, - true, - ) - .context( - "failed to interpolate secrets into on_pull command", - )?; - replacers.extend(core_replacers.to_owned()); - let mut on_pull_log = run_komodo_command( - "on pull", - on_pull_path.as_ref(), - &full_command, - true, - ) - .await; - - on_pull_log.command = - svi::replace_in_string(&on_pull_log.command, &replacers); - on_pull_log.stdout = - svi::replace_in_string(&on_pull_log.stdout, &replacers); - on_pull_log.stderr = - svi::replace_in_string(&on_pull_log.stderr, &replacers); - - tracing::debug!( - "run repo on_pull command | command: {} | cwd: {:?}", - on_pull_log.command, - on_pull_path - ); - - logs.push(on_pull_log); - } else { - let on_pull_log = run_komodo_command( - "on pull", - on_pull_path.as_ref(), - &command.command, - true, - ) - .await; - tracing::debug!( - "run repo on_pull command | command: {} | cwd: {:?}", - command.command, - on_pull_path - ); - logs.push(on_pull_log); - } + let on_pull_path = repo_dir.join(&command.path); + if let Some(secrets) = secrets { + run_komodo_command_with_interpolation( + "On Pull", + Some(on_pull_path.as_path()), + &command.command, + true, + secrets, + core_replacers, + ) + .await + } else { + run_komodo_command_multiline( + "On Pull", + Some(on_pull_path.as_path()), + &command.command, + ) + .await } + .map(|log| logs.push(log)); } Ok(GitRes { @@ -258,7 +196,6 @@ async fn clone_inner( "set commit", destination, format!("git reset --hard {commit}",), - false, ) .await; logs.push(reset_log); diff --git a/lib/git/src/commit.rs b/lib/git/src/commit.rs index d01d3cd8d..438ede796 100644 --- a/lib/git/src/commit.rs +++ b/lib/git/src/commit.rs @@ -71,7 +71,6 @@ pub async fn commit_file_inner( "Add Files", repo_dir, format!("git add {}", file.display()), - false, ) .await; res.logs.push(add_log); @@ -85,7 +84,6 @@ pub async fn commit_file_inner( format!( "git commit -m \"[Komodo] {commit_msg}: update {file:?}\"", ), - false, ) .await; @@ -118,7 +116,6 @@ pub async fn commit_file_inner( "Push", repo_dir, format!("git push -f --set-upstream origin {branch}"), - false, ) .await; res.logs.push(push_log); @@ -136,8 +133,7 @@ pub async fn commit_all( let mut res = GitRes::default(); let add_log = - run_komodo_command("Add Files", repo_dir, "git add -A", false) - .await; + run_komodo_command("Add Files", repo_dir, "git add -A").await; res.logs.push(add_log); if !all_logs_success(&res.logs) { return res; @@ -147,7 +143,6 @@ pub async fn commit_all( "Commit", repo_dir, format!("git commit -m \"[Komodo] {message}\""), - false, ) .await; res.logs.push(commit_log); @@ -174,7 +169,6 @@ pub async fn commit_all( "Push", repo_dir, format!("git push -f --set-upstream origin {branch}"), - false, ) .await; res.logs.push(push_log); diff --git a/lib/git/src/init.rs b/lib/git/src/init.rs index 887cc9e3e..81cb2c079 100644 --- a/lib/git/src/init.rs +++ b/lib/git/src/init.rs @@ -14,13 +14,9 @@ pub async fn init_folder_as_repo( ) { // let folder_path = args.path(repo_dir); // Initialize the folder as a git repo - let init_repo = run_komodo_command( - "Git Init", - folder_path.as_ref(), - "git init", - false, - ) - .await; + let init_repo = + run_komodo_command("Git Init", folder_path.as_ref(), "git init") + .await; logs.push(init_repo); if !all_logs_success(&logs) { return; @@ -40,7 +36,6 @@ pub async fn init_folder_as_repo( "Add git remote", folder_path.as_ref(), format!("git remote add origin {repo_url}"), - false, ) .await; // Sanitize the output @@ -59,7 +54,6 @@ pub async fn init_folder_as_repo( "Set Branch", folder_path.as_ref(), format!("git switch -c {}", args.branch), - false, ) .await; if !init_repo.success { diff --git a/lib/git/src/pull.rs b/lib/git/src/pull.rs index 4446562a7..039a39dcc 100644 --- a/lib/git/src/pull.rs +++ b/lib/git/src/pull.rs @@ -4,9 +4,11 @@ use std::{ sync::OnceLock, }; -use anyhow::Context; use cache::TimeoutCache; -use command::run_komodo_command; +use command::{ + run_komodo_command, run_komodo_command_multiline, + run_komodo_command_with_interpolation, +}; use formatting::format_serror; use komodo_client::entities::{ all_logs_success, komodo_timestamp, update::Log, CloneArgs, @@ -97,7 +99,6 @@ where "Set git remote", folder_path.as_ref(), format!("git remote set-url origin {repo_url}"), - false, ) .await; // Sanitize the output @@ -123,7 +124,6 @@ where "Checkout branch", folder_path.as_ref(), format!("git checkout -f {}", args.branch), - false, ) .await; logs.push(checkout); @@ -140,7 +140,6 @@ where "Git pull", folder_path.as_ref(), format!("git pull --rebase --force origin {}", args.branch), - false, ) .await; logs.push(pull_log); @@ -158,7 +157,6 @@ where "Set commit", folder_path.as_ref(), format!("git reset --hard {commit}"), - false, ) .await; logs.push(reset_log); @@ -200,72 +198,26 @@ where }; if let Some(command) = args.on_pull { - if !command.command.is_empty() { - let on_pull_path = folder_path.join(&command.path); - if let Some(secrets) = secrets { - let (full_command, mut replacers) = - match svi::interpolate_variables( - &command.command, - secrets, - svi::Interpolator::DoubleBrackets, - true, - ) - .context( - "failed to interpolate secrets into on_pull command", - ) { - Ok(res) => res, - Err(e) => { - logs.push(Log::error( - "interpolate secrets - on_pull", - format_serror(&e.into()), - )); - return Ok(GitRes { - logs, - hash, - message, - env_file_path: None, - }); - } - }; - replacers.extend(core_replacers.to_owned()); - let mut on_pull_log = run_komodo_command( - "On pull", - on_pull_path.as_ref(), - &full_command, - true, - ) - .await; - - on_pull_log.command = - svi::replace_in_string(&on_pull_log.command, &replacers); - on_pull_log.stdout = - svi::replace_in_string(&on_pull_log.stdout, &replacers); - on_pull_log.stderr = - svi::replace_in_string(&on_pull_log.stderr, &replacers); - - tracing::debug!( - "run repo on_pull command | command: {} | cwd: {:?}", - on_pull_log.command, - on_pull_path - ); - - logs.push(on_pull_log); - } else { - let on_pull_log = run_komodo_command( - "On pull", - on_pull_path.as_ref(), - &command.command, - true, - ) - .await; - tracing::debug!( - "run repo on_pull command | command: {} | cwd: {:?}", - command.command, - on_pull_path - ); - logs.push(on_pull_log); - } + let on_pull_path = repo_dir.join(&command.path); + if let Some(secrets) = secrets { + run_komodo_command_with_interpolation( + "On Pull", + Some(on_pull_path.as_path()), + &command.command, + true, + secrets, + core_replacers, + ) + .await + } else { + run_komodo_command_multiline( + "On Pull", + Some(on_pull_path.as_path()), + &command.command, + ) + .await } + .map(|log| logs.push(log)); } anyhow::Ok(GitRes {