From 78375a5c4e1a8ef5526aebb733a63d115950142a Mon Sep 17 00:00:00 2001 From: Matheus Garcias Date: Tue, 18 Jul 2023 10:09:57 -0300 Subject: [PATCH 1/5] Feature entrypoint per task --- src/actors/command.rs | 27 ++++++++++++++++++++++++++- src/config/mod.rs | 1 + whiz.yaml | 8 ++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/actors/command.rs b/src/actors/command.rs index dce8504..9a907f1 100644 --- a/src/actors/command.rs +++ b/src/actors/command.rs @@ -109,6 +109,7 @@ pub struct CommandActor { started_at: DateTime, env: Vec<(String, String)>, pipes: Vec, + entrypoint: Option, } impl CommandActor { @@ -164,6 +165,7 @@ impl CommandActor { verbose, env.into_iter().collect(), task_pipes, + op.entrypoint.clone(), ) .start(); @@ -187,6 +189,7 @@ impl CommandActor { verbose: bool, env: Vec<(String, String)>, pipes: Vec, + entrypoint: Option, ) -> Self { Self { op_name, @@ -203,6 +206,7 @@ impl CommandActor { started_at: Local::now(), env, pipes, + entrypoint, } } @@ -260,7 +264,28 @@ impl CommandActor { let exec = Exec::cmd("cmd").args(&["/c", args]); #[cfg(not(target_os = "windows"))] - let exec = Exec::cmd("bash").args(&["-c", args]); + let exec = { + // Defaults to bash if no entrypoint is provided. + let mut entrypoint = match &self.entrypoint { + Some(e) => e.to_string(), + None => "bash -c".to_string(), + }; + let mut nargs: Vec<&str> = vec![]; + + // This is needed because the shell can interpret "bash -c" as a single command rather than "bash" as a command then "-c" as an argument. + let t = &entrypoint.clone(); + if entrypoint.contains(' ') { + let c = t.split(' ').collect::>(); + let a = &c[0].clone(); + entrypoint = a.to_string(); + for i in &c[1..] { + nargs.push(*i); + } + nargs.push(&args); + } + + Exec::cmd(entrypoint).args(&nargs) + }; let mut p = exec .cwd(&self.cwd) diff --git a/src/config/mod.rs b/src/config/mod.rs index b74dd44..181670a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -38,6 +38,7 @@ impl Lift { pub struct Task { pub workdir: Option, pub command: String, + pub entrypoint: Option, #[serde(default)] pub watch: Lift, diff --git a/whiz.yaml b/whiz.yaml index 924d40f..b342ff9 100644 --- a/whiz.yaml +++ b/whiz.yaml @@ -45,6 +45,14 @@ dodo: color: command: python3 -c 'print(u"\u001b[31mHelloWorld\u001b[0m")' +color2: + command: 'print(u"\033[92mHelloWorld\033[0m")' + entrypoint: 'python3 -c' + +what_shell: + command: echo $(which $0) + entrypoint: 'sh -c' + watch_parent: workdir: src command: ls From a811f630721f0dd362de199cda4161d2411c1132 Mon Sep 17 00:00:00 2001 From: Matheus Garcias Date: Wed, 19 Jul 2023 12:40:27 -0300 Subject: [PATCH 2/5] Simple solution so it works on Windows for now --- src/actors/command.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/actors/command.rs b/src/actors/command.rs index 9a907f1..0234276 100644 --- a/src/actors/command.rs +++ b/src/actors/command.rs @@ -260,15 +260,16 @@ impl CommandActor { status: None, }); - #[cfg(target_os = "windows")] - let exec = Exec::cmd("cmd").args(&["/c", args]); - - #[cfg(not(target_os = "windows"))] let exec = { // Defaults to bash if no entrypoint is provided. let mut entrypoint = match &self.entrypoint { Some(e) => e.to_string(), + + #[cfg(not(target_os = "windows"))] None => "bash -c".to_string(), + + #[cfg(target_os = "windows")] + None => "cmd /c".to_string(), }; let mut nargs: Vec<&str> = vec![]; From 97d01a75ecfec154d8fb624c7451033dbc5510b6 Mon Sep 17 00:00:00 2001 From: Matheus Garcias Date: Wed, 19 Jul 2023 20:05:53 -0300 Subject: [PATCH 3/5] [WIP] fixing some edgecases where the entrypoint were bring splitted wrongly --- Cargo.lock | 23 +++++++++++++++-------- Cargo.toml | 1 + src/actors/command.rs | 30 +++++++++++++----------------- whiz.yaml | 4 ++++ 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69610ec..0425f77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.14" +version = "4.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98330784c494e49850cb23b8e2afcca13587d2500b2e3f1f78ae20248059c9be" +checksum = "5b0827b011f6f8ab38590295339817b0d26f344aa4932c3ced71b45b0c54b4a9" dependencies = [ "clap_builder", "clap_derive", @@ -437,9 +437,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.14" +version = "4.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182eb5f2562a67dda37e2c57af64d720a9e010c5e860ed87c056586aeafa52e" +checksum = "9441b403be87be858db6a23edb493e7f694761acdc3343d5a0fcaafd304cbc9e" dependencies = [ "anstream", "anstyle", @@ -2021,9 +2021,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.23" +version = "0.9.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6075b41c7e3b079e5f246eb6094a44850d3a4c25a67c581c80796c80134012" +checksum = "bd5f51e3fdb5b9cdd1577e1cb7a733474191b1aca6a72c2e50913241632c1180" dependencies = [ "indexmap 2.0.0", "itoa", @@ -2043,11 +2043,17 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "signal-hook" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b824b6e687aff278cdbf3b36f07aa52d4bd4099699324d5da86a2ebce3aa00b3" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", @@ -2661,6 +2667,7 @@ dependencies = [ "semver", "serde", "serde_yaml", + "shlex", "strip-ansi-escapes", "subprocess", "textwrap", diff --git a/Cargo.toml b/Cargo.toml index 3d5f151..7d73e80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,3 +41,4 @@ tui = {version = "0.22.0", package = "ratatui"} url = "2.4.0" assert_cmd = "2.0.12" semver = "1.0.18" +shlex = "1.1.0" \ No newline at end of file diff --git a/src/actors/command.rs b/src/actors/command.rs index 0234276..c7c25fd 100644 --- a/src/actors/command.rs +++ b/src/actors/command.rs @@ -18,6 +18,8 @@ use std::{ path::PathBuf, }; +use shlex; + use crate::config::{ pipe::{OutputRedirection, Pipe}, Config, Task, @@ -262,29 +264,23 @@ impl CommandActor { let exec = { // Defaults to bash if no entrypoint is provided. - let mut entrypoint = match &self.entrypoint { - Some(e) => e.to_string(), + let entrypoint_lex = match &self.entrypoint { + Some(e) => e.as_str(), #[cfg(not(target_os = "windows"))] - None => "bash -c".to_string(), + None => "bash -c", #[cfg(target_os = "windows")] - None => "cmd /c".to_string(), + None => "cmd /c", }; - let mut nargs: Vec<&str> = vec![]; - - // This is needed because the shell can interpret "bash -c" as a single command rather than "bash" as a command then "-c" as an argument. - let t = &entrypoint.clone(); - if entrypoint.contains(' ') { - let c = t.split(' ').collect::>(); - let a = &c[0].clone(); - entrypoint = a.to_string(); - for i in &c[1..] { - nargs.push(*i); - } - nargs.push(&args); - } + + let entrypoint_str = shlex::split(entrypoint_lex).unwrap(); + let entrypoint = &entrypoint_str[0]; + let mut nargs = entrypoint_str[1..].to_owned(); + nargs.push(args.to_owned()); + + Exec::cmd(entrypoint).args(&nargs) }; diff --git a/whiz.yaml b/whiz.yaml index b342ff9..cb140a4 100644 --- a/whiz.yaml +++ b/whiz.yaml @@ -16,6 +16,10 @@ once: A: world command: "echo hello $A" +entrypoint_only: + entrypoint: "ls ./src ./src/config" + command: '' + once_b: command: "sleep 2 && echo $B" depends_on: once From fc962c88bbb5a1bd07634f08adff981e65e0ec0f Mon Sep 17 00:00:00 2001 From: Matheus Garcias Date: Wed, 19 Jul 2023 20:32:19 -0300 Subject: [PATCH 4/5] handling no command at all and empty command --- src/actors/command.rs | 19 +++++++++++-------- src/config/mod.rs | 4 ++-- whiz.yaml | 8 +++++++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/actors/command.rs b/src/actors/command.rs index c7c25fd..df34e0f 100644 --- a/src/actors/command.rs +++ b/src/actors/command.rs @@ -256,15 +256,10 @@ impl CommandActor { fn reload(&mut self) -> Result<()> { let args = &self.operator.command; - self.log_debug(format!("EXEC: {} at {:?}", args, self.cwd)); - self.console.do_send(PanelStatus { - panel_name: self.op_name.clone(), - status: None, - }); - let exec = { // Defaults to bash if no entrypoint is provided. let entrypoint_lex = match &self.entrypoint { + Some(e) => e.as_str(), #[cfg(not(target_os = "windows"))] @@ -277,9 +272,17 @@ impl CommandActor { let entrypoint_str = shlex::split(entrypoint_lex).unwrap(); let entrypoint = &entrypoint_str[0]; let mut nargs = entrypoint_str[1..].to_owned(); - nargs.push(args.to_owned()); - + + match args { + Some(a) => { let a = a.clone(); if a.len() == 0 {} else { nargs.push(a) } }, + None => {}, + }; + self.log_debug(format!("EXEC: {} {:?} at {:?}", entrypoint_lex, nargs, self.cwd)); + self.console.do_send(PanelStatus { + panel_name: self.op_name.clone(), + status: None, + }); Exec::cmd(entrypoint).args(&nargs) }; diff --git a/src/config/mod.rs b/src/config/mod.rs index 181670a..80cb0d6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -37,7 +37,7 @@ impl Lift { #[serde(deny_unknown_fields)] pub struct Task { pub workdir: Option, - pub command: String, + pub command: Option, pub entrypoint: Option, #[serde(default)] @@ -398,7 +398,7 @@ mod tests { ); let job_with_alias = config.ops.get("with_alias").unwrap(); - assert_eq!(&job_with_alias.command, "echo with_alias"); + assert_eq!(&job_with_alias.command.clone().unwrap(), "echo with_alias"); } } diff --git a/whiz.yaml b/whiz.yaml index cb140a4..413864e 100644 --- a/whiz.yaml +++ b/whiz.yaml @@ -39,6 +39,12 @@ sleep_count: depends_on: - once +no_command: + entrypoint: python3 -c "print(\"No command specified\")" + +no_explicit_entrypoint: + command: echo "there's no entrypoint :(" + dodo: workdir: . watch: target @@ -50,7 +56,7 @@ color: command: python3 -c 'print(u"\u001b[31mHelloWorld\u001b[0m")' color2: - command: 'print(u"\033[92mHelloWorld\033[0m")' + command: 'print(u"\033[92mHello World\033[0m")' entrypoint: 'python3 -c' what_shell: From 4f668eede90f415a60eeae219f55db258454d58a Mon Sep 17 00:00:00 2001 From: Matheus Garcias Date: Fri, 21 Jul 2023 00:15:36 -0300 Subject: [PATCH 5/5] refactor: improving the code quality --- src/actors/command.rs | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/actors/command.rs b/src/actors/command.rs index df34e0f..27212fb 100644 --- a/src/actors/command.rs +++ b/src/actors/command.rs @@ -256,27 +256,38 @@ impl CommandActor { fn reload(&mut self) -> Result<()> { let args = &self.operator.command; - let exec = { - // Defaults to bash if no entrypoint is provided. - let entrypoint_lex = match &self.entrypoint { - - Some(e) => e.as_str(), + let default_entrypoint = { + #[cfg(not(target_os = "windows"))] + {"bash -c"} - #[cfg(not(target_os = "windows"))] - None => "bash -c", + #[cfg(target_os = "windows")] + {"cmd /c"} + }; - #[cfg(target_os = "windows")] - None => "cmd /c", + let exec = { + let entrypoint_lex = match &self.entrypoint { + Some(e) => if !e.is_empty() { e.as_str() } else { default_entrypoint }, + None => default_entrypoint, }; - let entrypoint_str = shlex::split(entrypoint_lex).unwrap(); - let entrypoint = &entrypoint_str[0]; - let mut nargs = entrypoint_str[1..].to_owned(); + let entrypoint_split = { + let mut s = shlex::split(entrypoint_lex).unwrap(); - match args { - Some(a) => { let a = a.clone(); if a.len() == 0 {} else { nargs.push(a) } }, - None => {}, + match args { + Some(a) => { + s.push(a.to_owned()); + s + }, + None => s, + } }; + + let entrypoint = &entrypoint_split[0]; + let nargs: Vec = entrypoint_split[1..] + .to_owned() + .into_iter() + .filter(|s| !s.is_empty()) + .collect(); self.log_debug(format!("EXEC: {} {:?} at {:?}", entrypoint_lex, nargs, self.cwd)); self.console.do_send(PanelStatus {