Breaking changes for --shell

This commit is contained in:
Félix Saparelli 2024-04-20 22:15:56 +12:00
parent b72248a38c
commit 184ac9d463
No known key found for this signature in database
2 changed files with 51 additions and 45 deletions

View File

@ -434,18 +434,18 @@ pub struct Args {
/// Use a different shell /// Use a different shell
/// ///
/// By default, Watchexec will use 'sh' on unix and 'cmd' (CMD.EXE) on Windows. With this, you /// By default, Watchexec will use '$SHELL' if it's defined or a default of 'sh' on Unix-likes,
/// can override that and use a different shell, for example one with more features or one which /// and either 'pwsh', 'powershell', or 'cmd' (CMD.EXE) on Windows, depending on what Watchexec
/// has your custom aliases and functions. /// detects is the running shell.
///
/// With this option, you can override that and use a different shell, for example one with more
/// features or one which has your custom aliases and functions.
/// ///
/// If the value has spaces, it is parsed as a command line, and the first word used as the /// If the value has spaces, it is parsed as a command line, and the first word used as the
/// shell program, with the rest as arguments to the shell. /// shell program, with the rest as arguments to the shell.
/// ///
/// The command is run with the '-c' flag (except for 'cmd' on Windows, where it's '/C'). /// The command is run with the '-c' flag (except for 'cmd' on Windows, where it's '/C').
/// ///
/// Note that the default shell will change at the next major release: the value of '$SHELL'
/// will be respected, falling back to 'sh' on unix and to PowerShell on Windows.
///
/// The special value 'none' can be used to disable shell use entirely. In that case, the /// The special value 'none' can be used to disable shell use entirely. In that case, the
/// command provided to Watchexec will be parsed, with the first word being the executable and /// command provided to Watchexec will be parsed, with the first word being the executable and
/// the rest being the arguments, and executed directly. Note that this parsing is rudimentary, /// the rest being the arguments, and executed directly. Note that this parsing is rudimentary,
@ -465,7 +465,7 @@ pub struct Args {
/// ///
/// $ watchexec --shell=pwsh -- Test-Connection localhost /// $ watchexec --shell=pwsh -- Test-Connection localhost
/// ///
/// Use with cmd (default on Windows): /// Use with CMD.exe:
/// ///
/// $ watchexec --shell=cmd -- dir /// $ watchexec --shell=cmd -- dir
/// ///
@ -492,17 +492,6 @@ pub struct Args {
)] )]
pub no_shell: bool, pub no_shell: bool,
/// Don't use a shell
///
/// This is a deprecated alias for '--shell=none'.
#[arg(
long,
hide = true,
help_heading = OPTSET_COMMAND,
alias = "no-shell", // deprecated
)]
pub no_shell_long: bool,
/// Shorthand for '--emit-events=none' /// Shorthand for '--emit-events=none'
/// ///
/// This is the old way to disable event emission into the environment. See '--emit-events' for /// This is the old way to disable event emission into the environment. See '--emit-events' for

View File

@ -1,7 +1,7 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::HashMap, collections::HashMap,
env::current_dir, env::{current_dir, var},
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
fs::File, fs::File,
io::{IsTerminal, Write}, io::{IsTerminal, Write},
@ -469,35 +469,52 @@ fn interpret_command_args(args: &Args) -> Result<Arc<Command>> {
panic!("(clap) Bug: command is not present"); panic!("(clap) Bug: command is not present");
} }
let shell = match if args.no_shell || args.no_shell_long { let shell = if args.no_shell {
None None
} else { } else {
args.shell.as_deref().or(Some("default")) let shell = args.shell.clone().or_else(|| var("SHELL").ok());
} { match shell
Some("") => return Err(RuntimeError::CommandShellEmptyShell).into_diagnostic(), .as_deref()
.or_else(|| {
Some("none") | None => None, if cfg!(not(windows)) {
Some("sh")
#[cfg(windows)] } else if var("POWERSHELL_DISTRIBUTION_CHANNEL").is_ok()
Some("default") | Some("cmd") | Some("cmd.exe") | Some("CMD") | Some("CMD.EXE") => { && (which::which("pwsh").is_ok() || which::which("pwsh.exe").is_ok())
Some(Shell::cmd()) {
} trace!("detected pwsh");
Some("pwsh")
#[cfg(not(windows))] } else if var("PSModulePath").is_ok()
Some("default") => Some(Shell::new("sh")), && (which::which("powershell").is_ok()
|| which::which("powershell.exe").is_ok())
Some(other) => { {
let sh = other.split_ascii_whitespace().collect::<Vec<_>>(); trace!("detected powershell");
Some("powershell")
// UNWRAP: checked by Some("") } else {
#[allow(clippy::unwrap_used)] Some("cmd")
let (shprog, shopts) = sh.split_first().unwrap(); }
Some(Shell {
prog: shprog.into(),
options: shopts.iter().map(|s| (*s).to_string()).collect(),
program_option: Some(Cow::Borrowed(OsStr::new("-c"))),
}) })
.or(Some("default"))
{
Some("") => return Err(RuntimeError::CommandShellEmptyShell).into_diagnostic(),
Some("none") | None => None,
#[cfg(windows)]
Some("cmd") | Some("cmd.exe") | Some("CMD") | Some("CMD.EXE") => Some(Shell::cmd()),
Some(other) => {
let sh = other.split_ascii_whitespace().collect::<Vec<_>>();
// UNWRAP: checked by Some("")
#[allow(clippy::unwrap_used)]
let (shprog, shopts) = sh.split_first().unwrap();
Some(Shell {
prog: shprog.into(),
options: shopts.iter().map(|s| (*s).to_string()).collect(),
program_option: Some(Cow::Borrowed(OsStr::new("-c"))),
})
}
} }
}; };