Skip to content

Commit

Permalink
Add new helpers.
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj committed Nov 20, 2023
1 parent c0f421a commit 6fd5b49
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 42 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ miette = { workspace = true }
reqwest = { workspace = true, features = ["rustls-tls-native-roots", "stream"] }
semver = { workspace = true }
serde = { workspace = true }
shell-words = "1.1.0"
starbase = { workspace = true }
starbase_archive = { workspace = true }
starbase_styles = { workspace = true }
Expand Down
47 changes: 14 additions & 33 deletions crates/cli/src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use proto_pdk_api::{ExecutableConfig, RunHook};
use starbase::system;
use std::env;
use std::process::exit;
use system_env::is_command_on_path;
use system_env::create_process_command;
use tokio::process::Command;
use tracing::debug;

Expand Down Expand Up @@ -120,38 +120,19 @@ fn get_executable(tool: &Tool, args: &RunArgs) -> miette::Result<ExecutableConfi
fn create_command(exe_config: &ExecutableConfig, args: &[String]) -> Command {
let exe_path = exe_config.exe_path.as_ref().unwrap();

match exe_path.extension().map(|e| e.to_str().unwrap()) {
Some("ps1" | "cmd" | "bat") => {
let mut cmd = Command::new(if is_command_on_path("pwsh") {
"pwsh"
} else {
"powershell"
});
cmd.arg("-Command");
cmd.arg(
format!(
"{} {} {}",
exe_config.parent_exe_name.clone().unwrap_or_default(),
exe_path.display(),
shell_words::join(args)
)
.trim(),
);
cmd
}
_ => {
if let Some(parent_exe) = &exe_config.parent_exe_name {
let mut cmd = Command::new(parent_exe);
cmd.arg(exe_path);
cmd.args(args);
cmd
} else {
let mut cmd = Command::new(exe_path);
cmd.args(args);
cmd
}
}
}
let command = if let Some(parent_exe) = &exe_config.parent_exe_name {
let exe_path = exe_path.to_string_lossy().to_string();

let mut exe_args = vec![&exe_path];
exe_args.extend(args);

create_process_command(parent_exe, exe_args)
} else {
create_process_command(exe_path, args)
};

// Convert std to tokio
Command::from(command)
}

#[system]
Expand Down
1 change: 1 addition & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ repository = "https://github.com/moonrepo/proto"

[dependencies]
proto_pdk_api = { version = "0.10.4", path = "../pdk-api" }
system_env = { version = "0.1.4", path = "../system-env" }
version_spec = { version = "0.1.5", path = "../version-spec" }
warpgate = { version = "0.5.14", path = "../warpgate" }
cached = { workspace = true }
Expand Down
5 changes: 2 additions & 3 deletions crates/core/src/host_funcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use proto_pdk_api::{ExecCommandInput, ExecCommandOutput, HostLogInput, HostLogTa
use starbase_utils::fs;
use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;
use system_env::create_process_command;
use tracing::trace;
use warpgate::Id;

Expand Down Expand Up @@ -129,8 +129,7 @@ fn exec_command(
// let data = user_data.any().unwrap();
// let data = data.downcast_ref::<HostData>().unwrap();

let mut command = Command::new(&input.command);
command.args(&input.args);
let mut command = create_process_command(&input.command, &input.args);
command.envs(&input.env_vars);
// command.current_dir(&data.proto.cwd);

Expand Down
1 change: 1 addition & 0 deletions crates/system-env/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repository = "https://github.com/moonrepo/proto"
schematic = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true }
shell-words = "1.1.0"
thiserror = { workspace = true }

[features]
Expand Down
59 changes: 55 additions & 4 deletions crates/system-env/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::Command;

/// Return an absolute path to the provided program (without extension)
/// by checking `PATH` and cycling through `PATHEXT` extensions.
#[cfg(windows)]
pub fn find_command_on_path(name: &str) -> Option<PathBuf> {
pub fn find_command_on_path<T: AsRef<OsStr>>(name: T) -> Option<PathBuf> {
let Ok(system_path) = env::var("PATH") else {
return None;
};
let Ok(path_ext) = env::var("PATHEXT") else {
return None;
};

let exts = path_ext.split(';').collect::<Vec<_>>();
let name = name.as_ref();

for path_dir in env::split_paths(&system_path) {
for ext in &exts {
Expand All @@ -28,11 +32,13 @@ pub fn find_command_on_path(name: &str) -> Option<PathBuf> {

/// Return an absolute path to the provided command by checking `PATH`.
#[cfg(not(windows))]
pub fn find_command_on_path(name: &str) -> Option<PathBuf> {
pub fn find_command_on_path<T: AsRef<OsStr>>(name: T) -> Option<PathBuf> {
let Ok(system_path) = env::var("PATH") else {
return None;
};

let name = name.as_ref();

for path_dir in env::split_paths(&system_path) {
let path = path_dir.join(name);

Expand All @@ -46,6 +52,51 @@ pub fn find_command_on_path(name: &str) -> Option<PathBuf> {

/// Return true if the provided command/program (without extension)
/// is available on `PATH`.
pub fn is_command_on_path(name: &str) -> bool {
find_command_on_path(name).is_some()
pub fn is_command_on_path<T: AsRef<OsStr>>(name: T) -> bool {
find_command_on_path(name.as_ref()).is_some()
}

/// Create a new process [`Command`] and append the provided arguments. If the provided binary
/// name is not an absolute path, we'll attempt to find it on `PATH` using [`find_command_on_path`].
///
/// Furthermore, if the binary path is a Windows script (`.ps1`, `.cmd`, `.bat``), we'll wrap
/// the binary in a PowerShell command, and pass the original command via `-Command`.
pub fn create_process_command<T: AsRef<OsStr>, I: IntoIterator<Item = A>, A: AsRef<OsStr>>(
bin: T,
args: I,
) -> Command {
let bin = bin.as_ref();

// If an absolute path, use as-is, otherwise find the command
let bin_path = if bin
.as_encoded_bytes()
.iter()
.any(|b| b.eq_ignore_ascii_case(&b'/') || b.eq_ignore_ascii_case(&b'\\'))
{
PathBuf::from(bin)
} else {
find_command_on_path(bin).unwrap_or_else(|| bin.into())
};

// If a Windows script, we must execute the command through powershell
match bin_path.extension().map(|e| e.to_str().unwrap()) {
Some("ps1" | "cmd" | "bat") => {
// This conversion is unfortunate...
let args = args
.into_iter()
.map(|a| String::from_utf8_lossy(a.as_ref().as_encoded_bytes()).to_string())
.collect::<Vec<_>>();

let mut cmd =
Command::new(find_command_on_path("pwsh").unwrap_or_else(|| "powershell".into()));
cmd.arg("-Command");
cmd.arg(format!("{} {}", bin_path.display(), shell_words::join(args)).trim());
cmd
}
_ => {
let mut cmd = Command::new(bin_path);
cmd.args(args);
cmd
}
}
}

0 comments on commit 6fd5b49

Please sign in to comment.