From a2a1e2729e0c362f9a1dd76b2a86f95901122474 Mon Sep 17 00:00:00 2001
From: Itay Tsabary <itayt@starkware.co>
Date: Mon, 25 Nov 2024 11:14:34 +0200
Subject: [PATCH] chore: add shell command builder

commit-id:96859c27
---
 Cargo.lock                             |  8 +++++++
 crates/infra_utils/Cargo.toml          |  6 ++++++
 crates/infra_utils/src/command.rs      | 29 ++++++++++++++++++++++++++
 crates/infra_utils/src/command_test.rs | 15 +++++++++++++
 crates/infra_utils/src/lib.rs          |  1 +
 5 files changed, 59 insertions(+)
 create mode 100644 crates/infra_utils/src/command.rs
 create mode 100644 crates/infra_utils/src/command_test.rs

diff --git a/Cargo.lock b/Cargo.lock
index 5e7278717e8..5bad2ad0fa2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5163,6 +5163,14 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
 [[package]]
 name = "infra_utils"
 version = "0.0.0"
+<<<<<<< HEAD
+=======
+dependencies = [
+ "rstest",
+ "thiserror",
+ "tokio",
+]
+>>>>>>> 32f1a4b3b (chore: add shell command builder)
 
 [[package]]
 name = "inout"
diff --git a/crates/infra_utils/Cargo.toml b/crates/infra_utils/Cargo.toml
index 69c33687b69..6b42af283d4 100644
--- a/crates/infra_utils/Cargo.toml
+++ b/crates/infra_utils/Cargo.toml
@@ -8,3 +8,9 @@ description = "Infrastructure utility."
 
 [lints]
 workspace = true
+
+[dependencies]
+tokio = { workspace = true, features = ["process"] }
+
+[dev-dependencies]
+rstest.workspace = true
diff --git a/crates/infra_utils/src/command.rs b/crates/infra_utils/src/command.rs
new file mode 100644
index 00000000000..3d4e9550303
--- /dev/null
+++ b/crates/infra_utils/src/command.rs
@@ -0,0 +1,29 @@
+use std::env;
+
+use tokio::process::Command;
+
+use crate::path::project_path;
+
+#[cfg(test)]
+#[path = "command_test.rs"]
+mod command_test;
+
+/// Returns a shell command originating from the project root, with cargo environment variables
+/// filtered out.
+///
+/// # Arguments
+/// * `command_name` - The shell command name.
+///
+/// # Returns
+/// * A [`std::process::Command`] object with the current directory set to the project root, and
+///   cleared out cargo related environment variables.
+pub fn create_shell_command(command_name: &str) -> Command {
+    let project_path = project_path().expect("Failed to get project path");
+    let mut command = Command::new(command_name);
+    command.current_dir(&project_path);
+    // Filter out all CARGO_ environment variables.
+    env::vars().filter(|(key, _)| key.starts_with("CARGO_")).for_each(|(key, _)| {
+        command.env_remove(key);
+    });
+    command
+}
diff --git a/crates/infra_utils/src/command_test.rs b/crates/infra_utils/src/command_test.rs
new file mode 100644
index 00000000000..2d18e1cf0d8
--- /dev/null
+++ b/crates/infra_utils/src/command_test.rs
@@ -0,0 +1,15 @@
+use rstest::rstest;
+
+use crate::command::create_shell_command;
+
+#[rstest]
+#[tokio::test]
+async fn create_shell_command_example() {
+    let mut ls_command = create_shell_command("ls");
+    let output = ls_command.output().await.expect("Failed to execute command");
+    let stdout = String::from_utf8_lossy(&output.stdout);
+
+    assert!(output.status.success());
+    // Project root should contain the `crates` directory.
+    assert!(stdout.contains("crates"));
+}
diff --git a/crates/infra_utils/src/lib.rs b/crates/infra_utils/src/lib.rs
index 4da9789237c..f744151bf9a 100644
--- a/crates/infra_utils/src/lib.rs
+++ b/crates/infra_utils/src/lib.rs
@@ -1 +1,2 @@
+pub mod command;
 pub mod path;