NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
crates.io · #4706 most downloaded on crates.io
Library to execute commands in response to file modifications
Last release 2 days ago
06 Oct 2026
Release timing varies
gaps range from 2 weeks to 9 months
Some releases are documented
notes for 22 of the last 60 stable releases
7 versions withdrawn
withdrawn after publishing
10 years old
84 releases · first in 2016
chore: Release package watchexec version 8.4.4
chore: Release package watchexec version 8.4.4
chore: Release package watchexec version 8.4.3
chore: Release package watchexec version 8.4.3
chore: Release package watchexec version 8.4.2
chore: Release package watchexec version 8.4.2
One column per quarter.
chore: Release package watchexec version 8.4.1
chore: Release package watchexec version 8.4.1
chore: Release package watchexec version 8.4.0
chore: Release package watchexec version 8.4.0
Filterer::check_dir and Watchexec-owned, source-filtered recursion for Inotify, Windows ReadDirectoryChanges, and Poll backends.fs_ready now reports settled reconciliation and may signal after partial nonfatal failures.FSEvents without rebuilding its shared stream.chore: Release package watchexec version 8.3.0
Feat: add fs_ready signal for watcher readiness
fs_ready signal for watcher readiness (#1024)Nothing published for this version
Fix: bug on macOS where a task in the keyboard events worker would hang after graceful quit
Augments keyboard_events config to emit events for all single keyboard key inputs, in addition to the existing EOF
keyboard_events config to emit events for all single keyboard key inputs, in addition to the existing EOFkeyboard_events now switches to raw mode (and disabling it switches back to cooked)Nothing published for this version
Nothing published for this version
Nothing published for this version
- Deps: nix 0.29
Feature: non-recursive watches with WatchedPath::non_recursive()
WatchedPath::non_recursive()config.pathset() now preserves WatchedPath attributesWatchedPath to the root of the crate (old path remains as re-export for now)Deps: replace command-group with process-wrap (in supervisor, but has flow-on effects)
Deps: watchexec-events and watchexec-signals after major bump and yank
Deprecated items (mostly leftover from splitting out the watchexec_events and watchexec_signals crates) are removed.
Watchexec the core experience rather than providing the kitchensink / components so you could build your own from the pieces; that helps the cohesion of the whole and simplifies many patterns.watchexec_events and watchexec_signals crates) are removed.watchexec-supervisor.WatchexecWatchexec::new() now takes the on_action handler. As this is the most important handler to define and Watchexec will not be functional without one, that enforces providing it first.Watchexec::with_config() lets one provide a config upfront, otherwise the default values are used.Watchexec::default() is mostly used to avoid boilerplate in doc comment examples, and panics on initialisation errors.Watchexec::reconfigure() is removed. Use the public config field instead to access the "live" Arc<Config> (see below).Job#to_wait instead. Of course you can insert them as synthetic events if you want.InitConfig and RuntimeConfig have been unified into a single Config struct.WorkingData structures, all of the config is now flat in the same Config. That makes it easier to work with as all that's needed is to pass an Arc<Config> around, but it does mean the event sources are no longer independent.tokio::sync::watch for some values, and HandlerLock for handlers, and so on, everything is now a new Changeable type, specialised to ChangeableFn for closures and ChangeableFilterer for the Filterer.signal_change() method which must be called after changes to the config; this is taken care of when using the methods on Config. This is required for the few places in Watchexec which need active reconfiguration rather than reading config values just-in-time.Watchexec::reconfigure() and keeping a clone of the config around, an Arc<Config> is now "live" and changes applied to it will affect the Watchexec instance directly.command / commands are removed from config. Instead use the Action handler API for creating new supervised commands.command_grouped is removed from config. That's now an option set on Command.action_throttle is renamed to throttle and now defaults to 50ms, which is the default in Watchexec CLI.keyboard_emit_eof is renamed to keyboard_events.pre_spawn_handler is removed. Use Job#set_spawn_hook instead.post_spawn_handler is removed. Use Job#run instead.The structure has been reworked to be simpler and more extensible. Instead of a Command enum, there's now a Command struct, which holds a single Program and behaviour-altering options. Shell has also been redone, with less special-casing.
If you had:
Command::Exec {
prog: "date".into(),
args: vec!["+%s".into()],
}
You should now write:
Command {
program: Program::Exec {
prog: "date".into(),
args: vec!["+%s".into()],
},
options: Default::default(),
}
The new Program::Shell field args: Vec<String> lets you pass (trailing) arguments to the shell invocation:
Program::Shell {
shell: Shell::new("sh"),
command: "ls".into(),
args: vec!["--".into(), "movies".into()],
}
is equivalent to:
$ sh -c "ls" -- movies
args field of Command::Shell is now the options field of Shell.Shell has a new field program_option: Option<Cow<OsStr>> which is the syntax of the option used to provide the command. Ie for most shells it's -c and for CMD.EXE it's /C; this makes it fully customisable (including its absence!) if you want to use weird shells or non-shell programs as shells.Shell::Powershell is removed.raw_arg instead of arg to avoid quoting issues.Command can no longer take a list of programs. That was always quite a hack; now that multiple supervised commands are possible, that's how multiple programs should be handled.command_grouped option is now Command-level, so you can start both grouped and non-grouped programs.reset_sigmask option to control whether commands should have their signal masks reset on Unix. By default the signal mask is inherited.RuntimeError::NoCommands, RuntimeError::Handler, RuntimeError::HandlerLockHeld, and CriticalError::MissingHandler are removed as the relevant types/structures don't exist anymore.RuntimeError::CommandShellEmptyCommand and RuntimeError::CommandShellEmptyShell are removed; you can construct Shell with empty shell program and Program::Shell with an empty command, these will at best do nothing but they won't error early through Watchexec.RuntimeError::ClearScreen is removed, as clearing the screen is now done by the consumer of Watchexec, not Watchexec itself.RuntimeErrors, and are now CriticalErrors. These being runtime, nominally recoverable errors instead of end-the-world failures is one of the most common pitfalls of using the library, and though recovery is technically possible, it's better approached other ways.on_error handler is now sync only and no longer returns a Result; as such there's no longer the weird logic of "if the on_error handler errors, it will call itself on the error once, then crash".on_error, you should instead use non-async calls (like try_send() for Tokio channels). The error handler is expected to return as fast as possible, and not do blocking work if it can at all avoid it; this was always the case but is now documented more explicitly.The process supervision system is entirely reworked. Instead of "applying Outcomes", there's now a Job type which is a single supervised command, provided by the separate watchexec-supervisor crate. The Action handler itself can only create new jobs and list existing ones, and interaction with commands is done through the Job type.
The controls available on Job are now modeled on "real" supervisors like systemd, and are both more and less powerful than the old Outcome system. This can be seen clearly in how a "restart" is specified. Previously, this was an Outcome combinator:
Outcome::if_running(
Outcome::both(Outcome::stop(), Outcome::start()),
Outcome::start(),
)
Now, it's a discrete method:
job.restart();
Previously, a graceful stop was a mess:
Outcome::if_running(
Outcome::both(
Outcome::both(
Outcome::signal(Signal::Terminate),
Outcome::wait_timeout(Duration::from_secs(30)),
),
Outcome::both(Outcome::stop(), Outcome::start()),
),
Outcome::DoNothing,
)
Now, it's again a discrete method:
job.stop_with_signal(Signal::Terminate, Duration::from_secs(30));
The stop() and start() methods also do nothing if the process is already stopped or started, respectively, so you don't need to check the status of the job before calling them. The try_restart() method is available to do a restart only if the job is running, with the try_restart_with_signal() variant for graceful restarts.
Further, all of these methods are non-blocking sync (and take &self), but they return a Ticket, a future which resolves when the control has been processed. That can be dropped if you don't care about it without affecting the job, or used to perform more advanced flow control. The special to_wait() method returns a detached, cloneable, "wait()" future, which will resolve when the process exits, without needing to hold on to the Job or a reference at all.
See the restart_run_on_successful_build example which starts a cargo build, waits for it to end, and then (re)starts cargo run if the build exited successfully.
Finally: Outcome::Clear and Outcome::Reset are gone, and there's no equivalent on Job: that's because these are screen control actions, not job control. You should use the clearscreen crate directly in your action handler, in conjunction with job control, to achieve the desired effect.
Nothing published for this version
Nothing published for this version
New: Outcome::Race and Outcome::race()
Unify SubSignal and MainSignal into a new Signal type. The former types and paths exist as deprecated aliases/re-exports.
rust-version indication will remain, for the minimum estimated Rust version for the code features used in the crate's own code, but dependencies may have already moved on. From now on, only latest stable is assumed and tested for. (#510)watchexec-events and watchexec-signals crates.SubSignal and MainSignal into a new Signal type. The former types and paths exist as deprecated aliases/re-exports.Nothing published for this version
Deps: drop explicit dependency on libc on Unix.
libc on Unix.dunce, replaced with either Tokio's canonicalize (properly async) or normalize-path (performs no I/O).#[must_use] annotations to a bunch of functions.Send bound to HandlerLock.summarise_events_to_env on Windows to output paths with backslashes.- Deps: upgrade to miette 5.3.0
- Deps: upgrade to Notify 5.0.0
First "stable" release of the library.
First "stable" release of the library.
Change: the library is split into even more crates
project-origins and ignore-files, extract standalone functionalitycommand-group and clearscreenChange: the Action worker now launches a set of Commands
Command replaces and augments Shell, making explicit which style of calling will be usedVec<Command>, so multiple commands to be run as a setPreSpawn and PostSpawn handlers are run per Command, not per command setcmd1 && cmd2Change: the event queue is now a priority queue
on_action to do anythingFilterer trait changes slightly to let filterers use event priorityImprovement: the main subtasks of the runtime are now aborted on error
Improvement: the event queue is explicitly closed when shutting down
Improvement: the action worker will check if the event queue is closed more often, to shutdown early
Improvement: kill_on_drop is set on Commands, which will be a little more eager to terminate processes when we're done with them
Feature: Outcome::Sleep waits for a given duration (#79)
Other miscellaneous:
Deps: add the log feature to tracing so logs can be emitted to log subscribers
Deps: upgrade to Tokio 1.19
Deps: upgrade to Miette 4
Deps: upgrade to Notify 5.0.0-pre.15
Docs: fix the main example in lib.rs (#297)
Docs: describe a tuple argument in the globset filterer interface
Docs: the library crate gains a file-based CHANGELOG.md (and won't go in the Github releases tab anymore)
Docs: the library's readme's code block example is now checked as a doc-test
Meta: PRs are now merged by Bors
Replace git2 dependency by git-config (#267). This makes using the library more pleasant and will also avoid library version mismatch errors when the
Revert backend switch on mac from previous release. We'll do it a different way later
Internal change: kqueue backend is used on mac. This _should_ reduce or eliminate some old persistent bugs on mac, and improve response times, but ple
Watchexec::new() now reports the library's version at debug level=) requirement, to avoid breakage (#266)New error::FsWatcherError enum split off from RuntimeError, and with additional variants to take advantage of targeted help text for known inotify err
error::FsWatcherError enum split off from RuntimeError, and with additional variants to take advantage of targeted help text for known inotify errors on Linuxextensions and filters are now cooperative rather than exclusionary. That is, a filters of ["Gemfile"] and an extensions of ["js", "rb"] will match both Gemfile and index.js rather than matching nothing at all. This restores pre 2.0 behaviour.*/file will match both file and dir/file instead of just dir/file. This is a compatibility fix and is incorrect behaviour which will be removed in the future. Do not rely on it.The on_error handler gets an upgraded parameter which lets it upgrade (runtime) errors to critical.
on_error handler gets an upgraded parameter which lets it upgrade (runtime) errors to critical.summarize_events_to_paths now deduplicates paths within each variable.Action, PreSpawn, and PostSpawn structs passed to handlers now contain an Arc<[Event]> instead of an Arc >
Fix: globset filterer should pass all non-path events
Pre-release Pre-release Compare
Pre-release
Pre-release
Compare
Yanked for critical bug in globset filterer (fixed in pre.8) on 2022-01-26
First version of library v2 that was used in a CLI release.
First version of library v2 that was used in a CLI release.
Update MSRV (to 1.58) and policy (bump incurs minor semver only)
More logging, especially around ignore file discovery and filtering
paths::PATH_SEPARATOR is now public, being : on Unix and ; and Windows.`summarise_events_to_env` used to return COMMON_PATH, it now returns COMMON, in keeping with the other variable names.
summarise_events_to_env used to return COMMON_PATH, it now returns COMMON, in keeping with the other variable names.`summarise_events_to_env` returns a HashMap<&str, OsString> rather than HashMap<&OsStr, OsString>, because the expectation is that the variable names
summarise_events_to_env returns a HashMap<&str, OsString> rather than HashMap<&OsStr, OsString>, because the expectation is that the variable names are processed, e.g. in the CLI: WATCHEXEC_{}_PATH. OsStr makes that painful for no reason (the strings are static anyway).Action struct's events field changes to be an Arc<Vec<Event>> rather than a Vec<Event>: the intent is for the events to be immutable/read-only (and it also made it easier/cheaper to implement the next change below).PreSpawn and PostSpawn structs got a new events: Arc<Vec<Event>> field so these handlers get read-only access to the events that triggered the command.More documentation around tagged filterer:
== and != are case-insensitiveFileTypeProcessEnd (ExitStatus replacement)Placeholder release of v2 library (preview)
Nothing published for this version
Process handling code replaced with the new command-group crate.
use_process_group (default true) allows disabling use of process groups.cargo install watchexec stub removed.Nothing published for this version
`ba26999` Pin globset to 0.4.6 to avoid breakage due to a bugfix in 0.4.7
Initial release as a separate crate.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →