Skip to content

Commit

Permalink
feat(exec): allow non-npm installed commands, adopt execvp on unix
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanccn committed Apr 3, 2024
1 parent 06ec194 commit 6aa164f
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 124 deletions.
1 change: 0 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ ctrlc = "3.4.2"
dotenvy = "0.15.7"
indexmap = { version = "2.2.3", features = ["serde"] }
itoa = "1.0.10"
nix = { version = "0.28.0", features = ["signal"] }
owo-colors = { version = "4.0.0", features = ["supports-colors"] }
serde = { version = "1.0.197", features = ["derive"] }
shlex = "1.3.0"
Expand Down
12 changes: 6 additions & 6 deletions flake.lock

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

21 changes: 4 additions & 17 deletions src/cli/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fs;
use std::path::PathBuf;

use crate::package_json::PackageJson;
use crate::run::{has_exec, run_exec};
use crate::run::run_exec;

use super::ExecArgs;

Expand All @@ -15,19 +15,8 @@ pub fn handle(package_paths: impl Iterator<Item = PathBuf>, args: &ExecArgs) ->
if let Ok(Ok(package)) =
fs::read(&package_path).map(|mut raw| simd_json::from_slice::<PackageJson>(&mut raw))
{
if has_exec(&package_path, &args.bin) {
if !args.silent {
eprint!(
"{}",
package.make_prefix(
match crate::get_level() {
1 => None,
_ => Some(&args.bin),
},
Stream::Stderr
)
);
}
if !args.silent {
eprint!("{}", package.make_prefix(None, Stream::Stderr));

run_exec(&package_path, &package, args)?;

Expand All @@ -39,10 +28,8 @@ pub fn handle(package_paths: impl Iterator<Item = PathBuf>, args: &ExecArgs) ->

if !executed_exec {
eprintln!(
"{} No binary found with name {}.",
"{} No packages found!",
"error".if_supports_color(Stream::Stderr, |text| text.red()),
args.bin
.if_supports_color(Stream::Stderr, |text| text.bold()),
);

std::process::exit(1);
Expand Down
11 changes: 4 additions & 7 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ impl EnvFile {

#[derive(Parser, Clone)]
#[command(author, version, about, long_about = None)]
#[command(args_conflicts_with_subcommands = true)]
pub struct Cli {
#[clap(flatten)]
root_args: RootArgs,
Expand All @@ -49,7 +50,6 @@ pub struct NrxCli {
}

#[derive(Subcommand, Clone)]
#[command(args_conflicts_with_subcommands = true)]
enum Subcommands {
/// Run a script
#[clap(visible_alias = "run-script")]
Expand Down Expand Up @@ -110,12 +110,9 @@ pub struct RunArgs {

#[derive(Args, Clone, Debug)]
pub struct ExecArgs {
/// The name of the command
pub bin: String,

/// Extra arguments to pass to the command
#[clap(allow_hyphen_values = true)]
pub extra_args: Vec<String>,
/// The command to execute in a shell
#[clap(required = true, allow_hyphen_values = true)]
pub command: Vec<String>,

/// Disable printing package and command information
#[clap(short, long, env = "NRR_SILENT")]
Expand Down
62 changes: 22 additions & 40 deletions src/run/exec.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
#[cfg(unix)]
use nix::{
sys::signal::{kill, Signal},
unistd::Pid,
};

use std::{env, path::Path, process::Command};

use color_eyre::Result;
use owo_colors::{OwoColorize as _, Stream};

#[cfg(unix)]
use ctrlc::set_handler;
use std::os::unix::process::CommandExt as _;

use crate::{cli::ExecArgs, package_json::PackageJson, run::util};

pub fn run_exec(package_path: &Path, package_data: &PackageJson, args: &ExecArgs) -> Result<()> {
let package_folder = package_path.parent().unwrap();

let escaped_command = args
.command
.iter()
.map(|f| shlex::try_quote(f))
.collect::<Result<Vec<_>, _>>()?
.join(" ");

if !args.silent {
let cmd_prefix = "$".repeat(*crate::get_level());

eprintln!(
"{} {} {}",
"{} {}",
cmd_prefix
.if_supports_color(Stream::Stderr, |text| text.cyan())
.if_supports_color(Stream::Stderr, |text| text.dimmed()),
args.bin
.if_supports_color(Stream::Stderr, |text| text.dimmed()),
args.extra_args
.join(" ")
.if_supports_color(Stream::Stderr, |text| text.dimmed())
escaped_command.if_supports_color(Stream::Stderr, |text| text.dimmed()),
);
}

let mut subproc = Command::new(&args.bin);
subproc.current_dir(package_folder).args(&args.extra_args);
let mut command_iter = args.command.iter();
let mut subproc = Command::new(command_iter.next().unwrap());
subproc.current_dir(package_folder).args(command_iter);

if let Some(env_file) = &args.env_file {
subproc.envs(env_file.iter());
Expand All @@ -55,32 +53,16 @@ pub fn run_exec(package_path: &Path, package_data: &PackageJson, args: &ExecArgs
subproc.env("npm_package_version", version);
}

let mut child = subproc.spawn()?;

#[allow(clippy::cast_possible_wrap)]
#[cfg(unix)]
let pid = Pid::from_raw(child.id() as i32);

#[cfg(unix)]
set_handler(move || {
kill(pid, Signal::SIGINT).ok();
})?;

let status = child.wait()?;

if !status.success() {
let code = status.code().unwrap_or(1);

if !args.silent {
eprintln!(
"{} Exited with status {}!",
"error".if_supports_color(Stream::Stderr, |text| text.red()),
util::itoa(code).if_supports_color(Stream::Stderr, |text| text.bold()),
);
}

std::process::exit(code);
{
Err::<(), _>(subproc.exec())?;
Ok(())
}

Ok(())
#[cfg(windows)]
{
let _ = ctrlc::set_handler(|| {});
let status = subproc.status()?;
std::process::exit(status.code().unwrap_or(1));
}
}
64 changes: 12 additions & 52 deletions src/run/script.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,13 @@
#[cfg(unix)]
use nix::{
sys::signal::{kill, Signal},
unistd::Pid,
};

use std::{env, path::Path, process::Command};
use std::{env, path::Path};

use color_eyre::Result;
use owo_colors::{OwoColorize as _, Stream};

#[cfg(unix)]
use ctrlc::set_handler;
use std::os::unix::process::CommandExt as _;

use crate::{cli::RunArgs, package_json::PackageJson, run::util};

#[cfg(unix)]
#[allow(clippy::unnecessary_wraps)]
#[inline]
fn make_shell_cmd() -> Result<Command> {
let mut cmd = Command::new("/bin/sh");
cmd.arg("-c");
Ok(cmd)
}

#[cfg(windows)]
#[allow(clippy::unnecessary_wraps)]
#[inline]
fn make_shell_cmd() -> Result<Command> {
let mut cmd = Command::new(env::var("ComSpec")?);
cmd.args(["/d", "/s", "/c"]);
Ok(cmd)
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ScriptType {
Pre,
Expand Down Expand Up @@ -86,7 +62,7 @@ fn run_single_script(
);
}

let mut subproc = make_shell_cmd()?;
let mut subproc = util::make_shell_cmd()?;
subproc.current_dir(package_folder).arg(&full_cmd);

if let Some(env_file) = &args.env_file {
Expand All @@ -111,34 +87,18 @@ fn run_single_script(
subproc.env("npm_package_version", version);
}

let mut child = subproc.spawn()?;

#[allow(clippy::cast_possible_wrap)]
#[cfg(unix)]
let pid = Pid::from_raw(child.id() as i32);

#[cfg(unix)]
set_handler(move || {
kill(pid, Signal::SIGINT).ok();
})?;

let status = child.wait()?;

if !status.success() {
let code = status.code().unwrap_or(1);

if !args.silent {
eprintln!(
"{} Exited with status {}!",
"error".if_supports_color(Stream::Stderr, |text| text.red()),
util::itoa(code).if_supports_color(Stream::Stderr, |text| text.bold()),
);
}

std::process::exit(code);
{
Err::<(), _>(subproc.exec())?;
Ok(())
}

Ok(())
#[cfg(windows)]
{
let _ = ctrlc::set_handler(|| {});
let status = subproc.status()?;
std::process::exit(status.code().unwrap_or(1));
}
}

pub fn run_script(
Expand Down
19 changes: 19 additions & 0 deletions src/run/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{
env,
ffi::OsString,
path::{Path, PathBuf},
process::Command,
};

#[must_use]
Expand Down Expand Up @@ -36,3 +37,21 @@ pub fn has_exec(package_path: &Path, bin: &str) -> bool {
.map(|p| p.join(bin))
.any(|p| p.is_file())
}

#[cfg(unix)]
#[allow(clippy::unnecessary_wraps)]
#[inline]
pub fn make_shell_cmd() -> Result<Command> {
let mut cmd = Command::new("/bin/sh");
cmd.arg("-c");
Ok(cmd)
}

#[cfg(windows)]
#[allow(clippy::unnecessary_wraps)]
#[inline]
pub fn make_shell_cmd() -> Result<Command> {
let mut cmd = Command::new(env::var("ComSpec")?);
cmd.args(["/d", "/s", "/c"]);
Ok(cmd)
}

0 comments on commit 6aa164f

Please sign in to comment.