New option: --signal-map (#710)
This commit is contained in:
parent
03460a6181
commit
709dbe5151
|
@ -49,7 +49,7 @@ More usage examples: [in the CLI README](./crates/cli/#usage-examples)!
|
|||
|
||||
All options in detail: [in the CLI README](./crates/cli/#installation),
|
||||
in the online help (`watchexec -h`, `watchexec --help`, or `watchexec --manual`),
|
||||
and [in the manual page](./doc/watchexec.1.md) ([PDF](./doc/watchexec.1.pdf)).
|
||||
and [in the manual page](./doc/watchexec.1.md).
|
||||
|
||||
|
||||
## Augment
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#!/bin/sh
|
||||
cargo run -p watchexec-cli -- --manual > doc/watchexec.1
|
||||
roff2pdf < doc/watchexec.1 > doc/watchexec.1.pdf
|
||||
pandoc doc/watchexec.1 -t markdown > doc/watchexec.1.md
|
||||
|
|
|
@ -19,7 +19,7 @@ _watchexec() {
|
|||
|
||||
case "${cmd}" in
|
||||
watchexec)
|
||||
opts="-w -c -o -W -r -s -k -d -p -n -E -1 -N -q -e -f -i -v -h -V --watch --clear --on-busy-update --watch-when-idle --restart --signal --kill --stop-signal --stop-timeout --debounce --stdin-quit --no-vcs-ignore --no-project-ignore --no-global-ignore --no-default-ignore --no-discover-ignore --ignore-nothing --postpone --delay-run --poll --shell --no-shell-long --no-environment --emit-events-to --only-emit-events --env --no-process-group --notify --color --timings --quiet --bell --project-origin --workdir --exts --filter --filter-file --ignore --ignore-file --fs-events --no-meta --print-events --verbose --log-file --manual --completions --help --version [COMMAND]..."
|
||||
opts="-w -c -o -W -r -s -k -d -p -n -E -1 -N -q -e -f -i -v -h -V --watch --clear --on-busy-update --watch-when-idle --restart --signal --kill --stop-signal --stop-timeout --map-signal --debounce --stdin-quit --no-vcs-ignore --no-project-ignore --no-global-ignore --no-default-ignore --no-discover-ignore --ignore-nothing --postpone --delay-run --poll --shell --no-shell-long --no-environment --emit-events-to --only-emit-events --env --no-process-group --notify --color --timings --quiet --bell --project-origin --workdir --exts --filter --filter-file --ignore --ignore-file --fs-events --no-meta --print-events --verbose --log-file --manual --completions --help --version [COMMAND]..."
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
@ -65,6 +65,10 @@ _watchexec() {
|
|||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--map-signal)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--debounce)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
|
|
|
@ -28,6 +28,7 @@ set edit:completion:arg-completer[watchexec] = {|@words|
|
|||
cand --signal 'Send a signal to the process when it''s still running'
|
||||
cand --stop-signal 'Signal to send to stop the command'
|
||||
cand --stop-timeout 'Time to wait for the command to exit gracefully'
|
||||
cand --map-signal 'Translate signals from the OS to signals to send to the command'
|
||||
cand -d 'Time to wait for new events before taking action'
|
||||
cand --debounce 'Time to wait for new events before taking action'
|
||||
cand --delay-run 'Sleep before running the command'
|
||||
|
|
|
@ -4,6 +4,7 @@ complete -c watchexec -s o -l on-busy-update -d 'What to do when receiving event
|
|||
complete -c watchexec -s s -l signal -d 'Send a signal to the process when it\'s still running' -r
|
||||
complete -c watchexec -l stop-signal -d 'Signal to send to stop the command' -r
|
||||
complete -c watchexec -l stop-timeout -d 'Time to wait for the command to exit gracefully' -r
|
||||
complete -c watchexec -l map-signal -d 'Translate signals from the OS to signals to send to the command' -r
|
||||
complete -c watchexec -s d -l debounce -d 'Time to wait for new events before taking action' -r
|
||||
complete -c watchexec -l delay-run -d 'Sleep before running the command' -r
|
||||
complete -c watchexec -l poll -d 'Poll for filesystem changes' -r
|
||||
|
|
|
@ -36,6 +36,7 @@ module completions {
|
|||
--kill(-k) # Hidden legacy shorthand for '--signal=kill'
|
||||
--stop-signal: string # Signal to send to stop the command
|
||||
--stop-timeout: string # Time to wait for the command to exit gracefully
|
||||
--map-signal: string # Translate signals from the OS to signals to send to the command
|
||||
--debounce(-d): string # Time to wait for new events before taking action
|
||||
--stdin-quit # Exit when stdin closes
|
||||
--no-vcs-ignore # Don't load gitignores
|
||||
|
|
|
@ -31,6 +31,7 @@ Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock {
|
|||
[CompletionResult]::new('--signal', 'signal', [CompletionResultType]::ParameterName, 'Send a signal to the process when it''s still running')
|
||||
[CompletionResult]::new('--stop-signal', 'stop-signal', [CompletionResultType]::ParameterName, 'Signal to send to stop the command')
|
||||
[CompletionResult]::new('--stop-timeout', 'stop-timeout', [CompletionResultType]::ParameterName, 'Time to wait for the command to exit gracefully')
|
||||
[CompletionResult]::new('--map-signal', 'map-signal', [CompletionResultType]::ParameterName, 'Translate signals from the OS to signals to send to the command')
|
||||
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Time to wait for new events before taking action')
|
||||
[CompletionResult]::new('--debounce', 'debounce', [CompletionResultType]::ParameterName, 'Time to wait for new events before taking action')
|
||||
[CompletionResult]::new('--delay-run', 'delay-run', [CompletionResultType]::ParameterName, 'Sleep before running the command')
|
||||
|
|
|
@ -25,6 +25,7 @@ _watchexec() {
|
|||
'(-r --restart -W --watch-when-idle)--signal=[Send a signal to the process when it'\''s still running]:SIGNAL: ' \
|
||||
'--stop-signal=[Signal to send to stop the command]:SIGNAL: ' \
|
||||
'--stop-timeout=[Time to wait for the command to exit gracefully]:TIMEOUT: ' \
|
||||
'*--map-signal=[Translate signals from the OS to signals to send to the command]:SIGNAL:SIGNAL: ' \
|
||||
'-d+[Time to wait for new events before taking action]:TIMEOUT: ' \
|
||||
'--debounce=[Time to wait for new events before taking action]:TIMEOUT: ' \
|
||||
'--delay-run=[Sleep before running the command]:DURATION: ' \
|
||||
|
|
|
@ -176,6 +176,6 @@ If not bundled, you can generate completions for your shell with `watchexec --co
|
|||
There's a manual page at `doc/watchexec.1`. Install it to `/usr/share/man/man1/`.
|
||||
If not bundled, you can generate a manual page with `watchexec --manual > /path/to/watchexec.1`, or view it inline with `watchexec --manual` (requires `man`).
|
||||
|
||||
You can also [read a text version](../../doc/watchexec.1.md) or a [PDF](../../doc/watchexec.1.pdf).
|
||||
You can also [read a text version](../../doc/watchexec.1.md).
|
||||
|
||||
Note that it is automatically generated from the help text, so it is not as pretty as a carefully hand-written one.
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::{path::PathBuf, str::FromStr, time::Duration};
|
||||
use std::{ffi::OsStr, path::PathBuf, str::FromStr, time::Duration};
|
||||
|
||||
use clap::{error::ErrorKind, ArgAction, CommandFactory, Parser, ValueEnum, ValueHint};
|
||||
use clap::{
|
||||
builder::TypedValueParser, error::ErrorKind, Arg, ArgAction, Command, CommandFactory, Parser,
|
||||
ValueEnum, ValueHint,
|
||||
};
|
||||
use watchexec::paths::PATH_SEPARATOR;
|
||||
use watchexec_signals::Signal;
|
||||
|
||||
|
@ -234,6 +237,22 @@ pub struct Args {
|
|||
)]
|
||||
pub stop_timeout: TimeSpan,
|
||||
|
||||
/// Translate signals from the OS to signals to send to the command
|
||||
///
|
||||
/// Takes a pair of signal names, separated by a colon, such as "TERM:INT" to map SIGTERM to
|
||||
/// SIGINT. The first signal is the one received by watchexec, and the second is the one sent to
|
||||
/// the command. The second can be omitted to discard the first signal, such as "TERM:" to
|
||||
/// not do anything on SIGTERM. Note this can make it hard to quit watchexec itself.
|
||||
///
|
||||
/// This option can be specified multiple times to map multiple signals.
|
||||
///
|
||||
/// Signal syntax is case-insensitive for short names (like "TERM", "USR2") and long names (like
|
||||
/// "SIGKILL", "SIGHUP"). Signal numbers are also supported (like "15", "31"). On Windows, the
|
||||
/// forms "STOP", "CTRL+C", "CTRL+BREAK", and "CTRL+CLOSE" are also supported to parse, but will
|
||||
/// not actually do anything as Watchexec cannot yet deliver nor receive those events.
|
||||
#[arg(long = "map-signal", value_name = "SIGNAL:SIGNAL", value_parser = SignalMappingValueParser)]
|
||||
pub signal_map: Vec<SignalMapping>,
|
||||
|
||||
/// Time to wait for new events before taking action
|
||||
///
|
||||
/// When an event is received, Watchexec will wait for up to this amount of time before handling
|
||||
|
@ -972,6 +991,46 @@ impl<const UNITLESS_NANOS_MULTIPLIER: u64> FromStr for TimeSpan<UNITLESS_NANOS_M
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct SignalMapping {
|
||||
pub from: Signal,
|
||||
pub to: Option<Signal>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SignalMappingValueParser;
|
||||
|
||||
impl TypedValueParser for SignalMappingValueParser {
|
||||
type Value = SignalMapping;
|
||||
|
||||
fn parse_ref(
|
||||
&self,
|
||||
_cmd: &Command,
|
||||
_arg: Option<&Arg>,
|
||||
value: &OsStr,
|
||||
) -> Result<Self::Value, clap::error::Error> {
|
||||
let value = value
|
||||
.to_str()
|
||||
.ok_or_else(|| clap::error::Error::raw(ErrorKind::ValueValidation, "invalid UTF-8"))?;
|
||||
let (from, to) = value
|
||||
.split_once(':')
|
||||
.ok_or_else(|| clap::error::Error::raw(ErrorKind::ValueValidation, "missing ':'"))?;
|
||||
|
||||
let from = from
|
||||
.parse::<Signal>()
|
||||
.map_err(|sigparse| clap::error::Error::raw(ErrorKind::ValueValidation, sigparse))?;
|
||||
let to = if to.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(to.parse::<Signal>().map_err(|sigparse| {
|
||||
clap::error::Error::raw(ErrorKind::ValueValidation, sigparse)
|
||||
})?)
|
||||
};
|
||||
|
||||
Ok(Self::Value { from, to })
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_args() -> Args {
|
||||
use tracing::{debug, warn};
|
||||
|
|
|
@ -27,7 +27,7 @@ use watchexec_events::{Event, Keyboard, ProcessEnd, Tag};
|
|||
use watchexec_signals::Signal;
|
||||
|
||||
use crate::{
|
||||
args::{Args, ClearMode, ColourMode, EmitEvents, OnBusyUpdate},
|
||||
args::{Args, ClearMode, ColourMode, EmitEvents, OnBusyUpdate, SignalMapping},
|
||||
state::RotatingTempFile,
|
||||
};
|
||||
use crate::{emits::events_to_simple_format, state::State};
|
||||
|
@ -184,50 +184,150 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
|
|||
let id = Id::default();
|
||||
let command = interpret_command_args(args)?;
|
||||
|
||||
let signal_map: Arc<HashMap<Signal, Option<Signal>>> = Arc::new(
|
||||
args.signal_map
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|SignalMapping { from, to }| (from, to))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
config.on_action_async(move |mut action| {
|
||||
let add_envs = add_envs.clone();
|
||||
let command = command.clone();
|
||||
let emit_file = emit_file.clone();
|
||||
let signal_map = signal_map.clone();
|
||||
let workdir = workdir.clone();
|
||||
Box::new(
|
||||
async move {
|
||||
trace!(events=?action.events, "handling action");
|
||||
|
||||
let add_envs = add_envs.clone();
|
||||
let command = command.clone();
|
||||
let emit_file = emit_file.clone();
|
||||
let workdir = workdir.clone();
|
||||
let add_envs = add_envs.clone();
|
||||
let command = command.clone();
|
||||
let emit_file = emit_file.clone();
|
||||
let signal_map = signal_map.clone();
|
||||
let workdir = workdir.clone();
|
||||
|
||||
trace!("set spawn hook for workdir and environment variables");
|
||||
let job = action.get_or_create_job(id, move || command.clone());
|
||||
let events = action.events.clone();
|
||||
job.set_spawn_hook(move |command, _| {
|
||||
let add_envs = add_envs.clone();
|
||||
let emit_file = emit_file.clone();
|
||||
let events = events.clone();
|
||||
let job = action.get_or_create_job(id, move || command.clone());
|
||||
let events = action.events.clone();
|
||||
job.set_spawn_hook(move |command, _| {
|
||||
let add_envs = add_envs.clone();
|
||||
let emit_file = emit_file.clone();
|
||||
let events = events.clone();
|
||||
|
||||
if let Some(ref workdir) = workdir.as_ref() {
|
||||
debug!(?workdir, "set command workdir");
|
||||
command.current_dir(workdir);
|
||||
if let Some(ref workdir) = workdir.as_ref() {
|
||||
debug!(?workdir, "set command workdir");
|
||||
command.current_dir(workdir);
|
||||
}
|
||||
|
||||
emit_events_to_command(command, events, emit_file, emit_events_to, add_envs);
|
||||
});
|
||||
|
||||
let show_events = || {
|
||||
if print_events {
|
||||
trace!("print events to stderr");
|
||||
for (n, event) in action.events.iter().enumerate() {
|
||||
eprintln!("[EVENT {n}] {event}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if once {
|
||||
debug!("debug mode: run once and quit");
|
||||
show_events();
|
||||
|
||||
if let Some(delay) = delay_run {
|
||||
job.run_async(move |_| {
|
||||
Box::new(async move {
|
||||
sleep(delay).await;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// this blocks the event loop, but also this is a debug feature so i don't care
|
||||
job.start().await;
|
||||
job.to_wait().await;
|
||||
action.quit();
|
||||
return action;
|
||||
}
|
||||
|
||||
emit_events_to_command(command, events, emit_file, emit_events_to, add_envs);
|
||||
});
|
||||
let is_keyboard_eof = action
|
||||
.events
|
||||
.iter()
|
||||
.any(|e| e.tags.contains(&Tag::Keyboard(Keyboard::Eof)));
|
||||
if is_keyboard_eof {
|
||||
debug!("keyboard EOF, quit");
|
||||
show_events();
|
||||
action.quit();
|
||||
return action;
|
||||
}
|
||||
|
||||
let show_events = || {
|
||||
if print_events {
|
||||
trace!("print events to stderr");
|
||||
for (n, event) in action.events.iter().enumerate() {
|
||||
eprintln!("[EVENT {n}] {event}");
|
||||
let signals: Vec<Signal> = action.signals().collect();
|
||||
trace!(?signals, "received some signals");
|
||||
|
||||
// if we got a terminate or interrupt signal and they're not mapped, quit
|
||||
if (signals.contains(&Signal::Terminate)
|
||||
&& !signal_map.contains_key(&Signal::Terminate))
|
||||
|| (signals.contains(&Signal::Interrupt)
|
||||
&& !signal_map.contains_key(&Signal::Interrupt))
|
||||
{
|
||||
debug!("unmapped terminate or interrupt signal, quit");
|
||||
show_events();
|
||||
action.quit();
|
||||
return action;
|
||||
}
|
||||
|
||||
// pass all other signals on
|
||||
for signal in signals {
|
||||
match signal_map.get(&signal) {
|
||||
Some(Some(mapped)) => {
|
||||
debug!(?signal, ?mapped, "passing mapped signal");
|
||||
job.signal(*mapped);
|
||||
}
|
||||
Some(None) => {
|
||||
debug!(?signal, "discarding signal");
|
||||
}
|
||||
None => {
|
||||
debug!(?signal, "passing signal on");
|
||||
job.signal(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only filesystem events below here (or empty synthetic events)
|
||||
if action.paths().next().is_none() && !action.events.iter().any(|e| e.is_empty()) {
|
||||
debug!("no filesystem or synthetic events, skip without doing more");
|
||||
show_events();
|
||||
return action;
|
||||
}
|
||||
|
||||
// clear the screen before printing events
|
||||
if let Some(mode) = clear {
|
||||
match mode {
|
||||
ClearMode::Clear => {
|
||||
clearscreen::clear().ok();
|
||||
debug!("cleared screen");
|
||||
}
|
||||
ClearMode::Reset => {
|
||||
for cs in [
|
||||
ClearScreen::WindowsCooked,
|
||||
ClearScreen::WindowsVt,
|
||||
ClearScreen::VtLeaveAlt,
|
||||
ClearScreen::VtWellDone,
|
||||
ClearScreen::default(),
|
||||
] {
|
||||
cs.clear().ok();
|
||||
}
|
||||
debug!("hard-reset screen");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if once {
|
||||
debug!("debug mode: run once and quit");
|
||||
show_events();
|
||||
|
||||
if let Some(delay) = delay_run {
|
||||
trace!("delaying run by sleeping inside the job");
|
||||
job.run_async(move |_| {
|
||||
Box::new(async move {
|
||||
sleep(delay).await;
|
||||
|
@ -235,118 +335,27 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
|
|||
});
|
||||
}
|
||||
|
||||
// this blocks the event loop, but also this is a debug feature so i don't care
|
||||
job.start().await;
|
||||
job.to_wait().await;
|
||||
action.quit();
|
||||
return action;
|
||||
}
|
||||
|
||||
let is_keyboard_eof = action
|
||||
.events
|
||||
.iter()
|
||||
.any(|e| e.tags.contains(&Tag::Keyboard(Keyboard::Eof)));
|
||||
if is_keyboard_eof {
|
||||
show_events();
|
||||
action.quit();
|
||||
return action;
|
||||
}
|
||||
|
||||
let signals: Vec<Signal> = action.signals().collect();
|
||||
trace!(?signals, "received some signals");
|
||||
|
||||
// if we got a terminate or interrupt signal, quit
|
||||
if signals.contains(&Signal::Terminate) || signals.contains(&Signal::Interrupt) {
|
||||
show_events();
|
||||
action.quit();
|
||||
return action;
|
||||
}
|
||||
|
||||
// pass all other signals on
|
||||
for signal in signals {
|
||||
job.signal(signal);
|
||||
}
|
||||
|
||||
// clear the screen before printing events
|
||||
if let Some(mode) = clear {
|
||||
match mode {
|
||||
ClearMode::Clear => {
|
||||
clearscreen::clear().ok();
|
||||
debug!("cleared screen");
|
||||
}
|
||||
ClearMode::Reset => {
|
||||
for cs in [
|
||||
ClearScreen::WindowsCooked,
|
||||
ClearScreen::WindowsVt,
|
||||
ClearScreen::VtLeaveAlt,
|
||||
ClearScreen::VtWellDone,
|
||||
ClearScreen::default(),
|
||||
] {
|
||||
cs.clear().ok();
|
||||
}
|
||||
debug!("hard-reset screen");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
show_events();
|
||||
|
||||
if let Some(delay) = delay_run {
|
||||
trace!("delaying run by sleeping inside the job");
|
||||
job.run_async(move |_| {
|
||||
Box::new(async move {
|
||||
sleep(delay).await;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
trace!("querying job state via run_async");
|
||||
job.run_async({
|
||||
let job = job.clone();
|
||||
move |context| {
|
||||
job.run_async({
|
||||
let job = job.clone();
|
||||
let is_running = matches!(context.current, CommandState::Running { .. });
|
||||
Box::new(async move {
|
||||
let innerjob = job.clone();
|
||||
if is_running {
|
||||
move |context| {
|
||||
let job = job.clone();
|
||||
let is_running = matches!(context.current, CommandState::Running { .. });
|
||||
Box::new(async move {
|
||||
let innerjob = job.clone();
|
||||
if is_running {
|
||||
trace!(?on_busy, "job is running, decide what to do");
|
||||
match on_busy {
|
||||
OnBusyUpdate::DoNothing => {}
|
||||
OnBusyUpdate::Signal => {
|
||||
job.signal(if cfg!(windows) {
|
||||
Signal::ForceStop
|
||||
} else {
|
||||
stop_signal.or(signal).unwrap_or(Signal::Terminate)
|
||||
});
|
||||
}
|
||||
OnBusyUpdate::Restart if cfg!(windows) => {
|
||||
job.restart();
|
||||
job.run(move |context| {
|
||||
setup_process(
|
||||
innerjob.clone(),
|
||||
context.command.clone(),
|
||||
outflags,
|
||||
)
|
||||
});
|
||||
}
|
||||
OnBusyUpdate::Restart => {
|
||||
job.restart_with_signal(
|
||||
stop_signal.unwrap_or(Signal::Terminate),
|
||||
stop_timeout,
|
||||
);
|
||||
job.run(move |context| {
|
||||
setup_process(
|
||||
innerjob.clone(),
|
||||
context.command.clone(),
|
||||
outflags,
|
||||
)
|
||||
});
|
||||
}
|
||||
OnBusyUpdate::Queue => {
|
||||
let job = job.clone();
|
||||
tokio::spawn(async move {
|
||||
job.to_wait().await;
|
||||
job.start();
|
||||
match on_busy {
|
||||
OnBusyUpdate::DoNothing => {}
|
||||
OnBusyUpdate::Signal => {
|
||||
job.signal(if cfg!(windows) {
|
||||
Signal::ForceStop
|
||||
} else {
|
||||
stop_signal.or(signal).unwrap_or(Signal::Terminate)
|
||||
});
|
||||
}
|
||||
OnBusyUpdate::Restart if cfg!(windows) => {
|
||||
job.restart();
|
||||
job.run(move |context| {
|
||||
setup_process(
|
||||
innerjob.clone(),
|
||||
|
@ -354,25 +363,51 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
|
|||
outflags,
|
||||
)
|
||||
});
|
||||
});
|
||||
}
|
||||
OnBusyUpdate::Restart => {
|
||||
job.restart_with_signal(
|
||||
stop_signal.unwrap_or(Signal::Terminate),
|
||||
stop_timeout,
|
||||
);
|
||||
job.run(move |context| {
|
||||
setup_process(
|
||||
innerjob.clone(),
|
||||
context.command.clone(),
|
||||
outflags,
|
||||
)
|
||||
});
|
||||
}
|
||||
OnBusyUpdate::Queue => {
|
||||
let job = job.clone();
|
||||
tokio::spawn(async move {
|
||||
job.to_wait().await;
|
||||
job.start();
|
||||
job.run(move |context| {
|
||||
setup_process(
|
||||
innerjob.clone(),
|
||||
context.command.clone(),
|
||||
outflags,
|
||||
)
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
trace!("job is not running, start it");
|
||||
job.start();
|
||||
job.run(move |context| {
|
||||
job.start();
|
||||
job.run(move |context| {
|
||||
setup_process(
|
||||
innerjob.clone(),
|
||||
context.command.clone(),
|
||||
outflags,
|
||||
)
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
action
|
||||
action
|
||||
}
|
||||
.instrument(trace_span!("action handler")),
|
||||
)
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
## Next (YYYY-MM-DD)
|
||||
|
||||
- Derive `Hash` for `Signal`.
|
||||
|
||||
## v2.0.0 (2023-11-29)
|
||||
|
||||
- Deps: upgrade nix to 0.27
|
||||
|
|
|
@ -19,7 +19,7 @@ use nix::sys::signal::Signal as NixSignal;
|
|||
///
|
||||
/// On Unix, there are several "first-class" signals which have their own variants, and a generic
|
||||
/// [`Custom`][Signal::Custom] variant which can be used to send arbitrary signals.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[non_exhaustive]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
.SH NAME
|
||||
watchexec \- Execute commands when watched files change
|
||||
.SH SYNOPSIS
|
||||
\fBwatchexec\fR [\fB\-w\fR|\fB\-\-watch\fR] [\fB\-c\fR|\fB\-\-clear\fR] [\fB\-o\fR|\fB\-\-on\-busy\-update\fR] [\fB\-r\fR|\fB\-\-restart\fR] [\fB\-s\fR|\fB\-\-signal\fR] [\fB\-\-stop\-signal\fR] [\fB\-\-stop\-timeout\fR] [\fB\-d\fR|\fB\-\-debounce\fR] [\fB\-\-stdin\-quit\fR] [\fB\-\-no\-vcs\-ignore\fR] [\fB\-\-no\-project\-ignore\fR] [\fB\-\-no\-global\-ignore\fR] [\fB\-\-no\-default\-ignore\fR] [\fB\-\-no\-discover\-ignore\fR] [\fB\-\-ignore\-nothing\fR] [\fB\-p\fR|\fB\-\-postpone\fR] [\fB\-\-delay\-run\fR] [\fB\-\-poll\fR] [\fB\-\-shell\fR] [\fB\-n \fR] [\fB\-\-no\-environment\fR] [\fB\-\-emit\-events\-to\fR] [\fB\-\-only\-emit\-events\fR] [\fB\-E\fR|\fB\-\-env\fR] [\fB\-\-no\-process\-group\fR] [\fB\-N\fR|\fB\-\-notify\fR] [\fB\-\-color\fR] [\fB\-\-timings\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-bell\fR] [\fB\-\-project\-origin\fR] [\fB\-\-workdir\fR] [\fB\-e\fR|\fB\-\-exts\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-filter\-file\fR] [\fB\-i\fR|\fB\-\-ignore\fR] [\fB\-\-ignore\-file\fR] [\fB\-\-fs\-events\fR] [\fB\-\-no\-meta\fR] [\fB\-\-print\-events\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-\-log\-file\fR] [\fB\-\-manual\fR] [\fB\-\-completions\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICOMMAND\fR]
|
||||
\fBwatchexec\fR [\fB\-w\fR|\fB\-\-watch\fR] [\fB\-c\fR|\fB\-\-clear\fR] [\fB\-o\fR|\fB\-\-on\-busy\-update\fR] [\fB\-r\fR|\fB\-\-restart\fR] [\fB\-s\fR|\fB\-\-signal\fR] [\fB\-\-stop\-signal\fR] [\fB\-\-stop\-timeout\fR] [\fB\-\-map\-signal\fR] [\fB\-d\fR|\fB\-\-debounce\fR] [\fB\-\-stdin\-quit\fR] [\fB\-\-no\-vcs\-ignore\fR] [\fB\-\-no\-project\-ignore\fR] [\fB\-\-no\-global\-ignore\fR] [\fB\-\-no\-default\-ignore\fR] [\fB\-\-no\-discover\-ignore\fR] [\fB\-\-ignore\-nothing\fR] [\fB\-p\fR|\fB\-\-postpone\fR] [\fB\-\-delay\-run\fR] [\fB\-\-poll\fR] [\fB\-\-shell\fR] [\fB\-n \fR] [\fB\-\-no\-environment\fR] [\fB\-\-emit\-events\-to\fR] [\fB\-\-only\-emit\-events\fR] [\fB\-E\fR|\fB\-\-env\fR] [\fB\-\-no\-process\-group\fR] [\fB\-N\fR|\fB\-\-notify\fR] [\fB\-\-color\fR] [\fB\-\-timings\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-bell\fR] [\fB\-\-project\-origin\fR] [\fB\-\-workdir\fR] [\fB\-e\fR|\fB\-\-exts\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-filter\-file\fR] [\fB\-i\fR|\fB\-\-ignore\fR] [\fB\-\-ignore\-file\fR] [\fB\-\-fs\-events\fR] [\fB\-\-no\-meta\fR] [\fB\-\-print\-events\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-\-log\-file\fR] [\fB\-\-manual\fR] [\fB\-\-completions\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICOMMAND\fR]
|
||||
.SH DESCRIPTION
|
||||
Execute commands when watched files change.
|
||||
.PP
|
||||
|
@ -98,6 +98,15 @@ The default is 60 seconds. Set to 0 to immediately force\-kill the command.
|
|||
|
||||
This has no practical effect on Windows as the command is always forcefully terminated; see \*(Aq\-\-stop\-signal\*(Aq for why.
|
||||
.TP
|
||||
\fB\-\-map\-signal\fR=\fISIGNAL:SIGNAL\fR
|
||||
Translate signals from the OS to signals to send to the command
|
||||
|
||||
Takes a pair of signal names, separated by a colon, such as "TERM:INT" to map SIGTERM to SIGINT. The first signal is the one received by watchexec, and the second is the one sent to the command. The second can be omitted to discard the first signal, such as "TERM:" to not do anything on SIGTERM. Note this can make it hard to quit watchexec itself.
|
||||
|
||||
This option can be specified multiple times to map multiple signals.
|
||||
|
||||
Signal syntax is case\-insensitive for short names (like "TERM", "USR2") and long names (like "SIGKILL", "SIGHUP"). Signal numbers are also supported (like "15", "31"). On Windows, the forms "STOP", "CTRL+C", "CTRL+BREAK", and "CTRL+CLOSE" are also supported to parse, but will not actually do anything as Watchexec cannot yet deliver nor receive those events.
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-debounce\fR=\fITIMEOUT\fR
|
||||
Time to wait for new events before taking action
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ watchexec - Execute commands when watched files change
|
|||
**watchexec** \[**-w**\|**\--watch**\] \[**-c**\|**\--clear**\]
|
||||
\[**-o**\|**\--on-busy-update**\] \[**-r**\|**\--restart**\]
|
||||
\[**-s**\|**\--signal**\] \[**\--stop-signal**\] \[**\--stop-timeout**\]
|
||||
\[**-d**\|**\--debounce**\] \[**\--stdin-quit**\]
|
||||
\[**\--map-signal**\] \[**-d**\|**\--debounce**\] \[**\--stdin-quit**\]
|
||||
\[**\--no-vcs-ignore**\] \[**\--no-project-ignore**\]
|
||||
\[**\--no-global-ignore**\] \[**\--no-default-ignore**\]
|
||||
\[**\--no-discover-ignore**\] \[**\--ignore-nothing**\]
|
||||
|
@ -162,6 +162,26 @@ command.
|
|||
This has no practical effect on Windows as the command is always
|
||||
forcefully terminated; see \--stop-signal for why.
|
||||
|
||||
**\--map-signal**=*SIGNAL:SIGNAL*
|
||||
|
||||
: Translate signals from the OS to signals to send to the command
|
||||
|
||||
Takes a pair of signal names, separated by a colon, such as \"TERM:INT\"
|
||||
to map SIGTERM to SIGINT. The first signal is the one received by
|
||||
watchexec, and the second is the one sent to the command. The second can
|
||||
be omitted to discard the first signal, such as \"TERM:\" to not do
|
||||
anything on SIGTERM. Note this can make it hard to quit watchexec
|
||||
itself.
|
||||
|
||||
This option can be specified multiple times to map multiple signals.
|
||||
|
||||
Signal syntax is case-insensitive for short names (like \"TERM\",
|
||||
\"USR2\") and long names (like \"SIGKILL\", \"SIGHUP\"). Signal numbers
|
||||
are also supported (like \"15\", \"31\"). On Windows, the forms
|
||||
\"STOP\", \"CTRL+C\", \"CTRL+BREAK\", and \"CTRL+CLOSE\" are also
|
||||
supported to parse, but will not actually do anything as Watchexec
|
||||
cannot yet deliver nor receive those events.
|
||||
|
||||
**-d**, **\--debounce**=*TIMEOUT*
|
||||
|
||||
: Time to wait for new events before taking action
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue