diff --git a/Cargo.lock b/Cargo.lock index eb4d211..a48ce3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,7 +449,6 @@ dependencies = [ "dotenvy", "indexmap", "itoa", - "nix", "owo-colors 4.0.0", "serde", "shlex", diff --git a/Cargo.toml b/Cargo.toml index d54b444..9bb9b5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/flake.lock b/flake.lock index a8bac6c..1042fe6 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1710765496, - "narHash": "sha256-p7ryWEeQfMwTB6E0wIUd5V2cFTgq+DRRBz2hYGnJZyA=", + "lastModified": 1712026416, + "narHash": "sha256-N/3VR/9e1NlN49p7kCiATiEY6Tzdo+CbrAG8kqCQKcI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e367f7a1fb93137af22a3908f00b9a35e2d286a7", + "rev": "080a4a27f206d07724b88da096e27ef63401a504", "type": "github" }, "original": { @@ -51,11 +51,11 @@ ] }, "locked": { - "lastModified": 1710900660, - "narHash": "sha256-PcHmHQvKIOdvAxlqxZ/DPmUMhUUvfp16pRtyW148u/0=", + "lastModified": 1712110341, + "narHash": "sha256-8LU2IM4ctHz043hlzoFUwQS1QIdhiMGEH/oIfPCxoWU=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "549f4db17b5c0c143b1308fcfe9620129c387472", + "rev": "74deb67494783168f5b6d2071d73177e6bccab65", "type": "github" }, "original": { diff --git a/src/cli/exec.rs b/src/cli/exec.rs index 3420180..a5cbb61 100644 --- a/src/cli/exec.rs +++ b/src/cli/exec.rs @@ -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; @@ -15,19 +15,8 @@ pub fn handle(package_paths: impl Iterator, args: &ExecArgs) -> if let Ok(Ok(package)) = fs::read(&package_path).map(|mut raw| simd_json::from_slice::(&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)?; @@ -39,10 +28,8 @@ pub fn handle(package_paths: impl Iterator, 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); diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 7af46ac..19a0aed 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -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, @@ -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")] @@ -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, + /// The command to execute in a shell + #[clap(required = true, allow_hyphen_values = true)] + pub command: Vec, /// Disable printing package and command information #[clap(short, long, env = "NRR_SILENT")] diff --git a/src/run/exec.rs b/src/run/exec.rs index fe4e483..dfd7f8d 100644 --- a/src/run/exec.rs +++ b/src/run/exec.rs @@ -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::, _>>()? + .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()); @@ -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)); + } } diff --git a/src/run/script.rs b/src/run/script.rs index 94a8dfe..3a5bb36 100644 --- a/src/run/script.rs +++ b/src/run/script.rs @@ -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 { - let mut cmd = Command::new("/bin/sh"); - cmd.arg("-c"); - Ok(cmd) -} - -#[cfg(windows)] -#[allow(clippy::unnecessary_wraps)] -#[inline] -fn make_shell_cmd() -> Result { - let mut cmd = Command::new(env::var("ComSpec")?); - cmd.args(["/d", "/s", "/c"]); - Ok(cmd) -} - #[derive(Debug, Copy, Clone, PartialEq)] pub enum ScriptType { Pre, @@ -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 { @@ -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( diff --git a/src/run/util.rs b/src/run/util.rs index 593cdb7..0529c20 100644 --- a/src/run/util.rs +++ b/src/run/util.rs @@ -3,6 +3,7 @@ use std::{ env, ffi::OsString, path::{Path, PathBuf}, + process::Command, }; #[must_use] @@ -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 { + 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 { + let mut cmd = Command::new(env::var("ComSpec")?); + cmd.args(["/d", "/s", "/c"]); + Ok(cmd) +}