standardize running commands with interpolation / output sanitizations

This commit is contained in:
mbecker20
2025-03-12 22:21:05 -04:00
parent 723853e92d
commit 48e871d400
19 changed files with 273 additions and 511 deletions
Generated
+3
View File
@@ -1069,8 +1069,11 @@ dependencies = [
name = "command"
version = "1.17.0-dev-3"
dependencies = [
"anyhow",
"formatting",
"komodo_client",
"run_command",
"svi",
]
[[package]]
-1
View File
@@ -123,7 +123,6 @@ impl Resolve<ExecuteArgs> for RunAction {
"Execute Action",
None,
format!("deno run --allow-all {}", path.display()),
false,
)
.await;
+13 -31
View File
@@ -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<super::Args> 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<super::Args> 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<super::Args> for PruneBuilders {
#[instrument(name = "PruneBuilders", skip_all)]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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<super::Args> for PruneBuildx {
#[instrument(name = "PruneBuildx", skip_all)]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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)
}
}
+3 -11
View File
@@ -27,10 +27,9 @@ impl Resolve<super::Args> for ListComposeProjects {
) -> serror::Result<Vec<ComposeProject>> {
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<super::Args> 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<super::Args> 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<super::Args> 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<super::Args> for ComposeExecution {
"Compose Command",
None,
format!("{docker_compose} -p {project} {command}"),
false,
)
.await;
Ok(log)
+21 -47
View File
@@ -44,10 +44,7 @@ impl Resolve<super::Args> 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<super::Args> 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<super::Args> for StartContainer {
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
Ok(
run_komodo_command(
"docker start",
"Docker Start",
None,
format!("docker start {}", self.name),
false,
)
.await,
)
@@ -134,10 +125,9 @@ impl Resolve<super::Args> for RestartContainer {
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
Ok(
run_komodo_command(
"docker restart",
"Docker Restart",
None,
format!("docker restart {}", self.name),
false,
)
.await,
)
@@ -151,10 +141,9 @@ impl Resolve<super::Args> for PauseContainer {
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
Ok(
run_komodo_command(
"docker pause",
"Docker Pause",
None,
format!("docker pause {}", self.name),
false,
)
.await,
)
@@ -166,10 +155,9 @@ impl Resolve<super::Args> for UnpauseContainer {
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
Ok(
run_komodo_command(
"docker unpause",
"Docker Unpause",
None,
format!("docker unpause {}", self.name),
false,
)
.await,
)
@@ -183,12 +171,11 @@ impl Resolve<super::Args> for StopContainer {
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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<super::Args> 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<super::Args> 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<super::Args> for PruneContainers {
#[instrument(name = "PruneContainers", skip_all)]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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<super::Args> 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<super::Args> 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<super::Args> 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<super::Args> 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<super::Args> for StopAllContainers {
&format!("docker stop {name}"),
None,
stop_container_command(name, None, None),
false,
)
.await
})
+16 -25
View File
@@ -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<super::Args> 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!(),
}
}
}
}
+3 -4
View File
@@ -75,10 +75,9 @@ impl Resolve<super::Args> 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<super::Args> for DeleteImage {
#[instrument(name = "DeleteImage")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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<super::Args> for PruneImages {
#[instrument(name = "PruneImages")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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)
}
}
+2 -2
View File
@@ -247,7 +247,7 @@ impl Resolve<Args> 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<Args> for PruneSystem {
#[instrument(name = "PruneSystem", skip_all)]
async fn resolve(self, _: &Args) -> serror::Result<Log> {
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)
}
}
+3 -12
View File
@@ -27,10 +27,7 @@ impl Resolve<super::Args> 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<super::Args> for DeleteNetwork {
#[instrument(name = "DeleteNetwork", skip(self))]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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<super::Args> for PruneNetworks {
#[instrument(name = "PruneNetworks", skip(self))]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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)
}
}
+2 -6
View File
@@ -20,9 +20,7 @@ impl Resolve<super::Args> for DeleteVolume {
#[instrument(name = "DeleteVolume")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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<super::Args> for PruneVolumes {
#[instrument(name = "PruneVolumes")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
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)
}
}
+60 -154
View File
@@ -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;
+1 -1
View File
@@ -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(
-11
View File
@@ -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,
+4 -1
View File
@@ -9,4 +9,7 @@ homepage.workspace = true
[dependencies]
komodo_client.workspace = true
run_command.workspace = true
run_command.workspace = true
formatting.workspace = true
anyhow.workspace = true
svi.workspace = true
+73 -13
View File
@@ -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<Option<&Path>>,
command: impl AsRef<str>,
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<Option<&Path>>,
command: impl AsRef<str>,
) -> Option<Log> {
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<Option<&Path>>,
command: impl AsRef<str>,
parse_multiline: bool,
secrets: &HashMap<String, String>,
additional_replacers: &[(String, String)],
) -> Option<Log> {
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,
+42 -105
View File
@@ -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);
+1 -7
View File
@@ -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);
+3 -9
View File
@@ -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 {
+23 -71
View File
@@ -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 {