watchexec
Library to execute commands in response to file modifications
8.3.0
5.1M downloads/mo
#4495 most downloaded on crates.io
watchexec/watchexec
What this package is like to depend on
Last release today
22 Aug 2026
Ships fairly regularly
a new release about every 4 months
Some releases are documented
notes for 17 of 58 stable releases
7 versions withdrawn
withdrawn after publishing
10 years old
79 releases · first in 2016
5 releases in the last 12 months
see the full history below
Release timeline
79 releases · Oct 2016 to Aug 2026Releases
latest 60 of 79-
8.3.022 Aug 2026 -
8.2.002 Mar 2026 -
8.1.224 Feb 2026Nothing published for this version
-
8.1.122 Feb 2026Release notes
Open source →- Fix: bug on macOS where a task in the keyboard events worker would hang after graceful quit (#1018)
-
8.1.022 Feb 2026Release notes
Open source →- Augments
keyboard_eventsconfig to emit events for all single keyboard key inputs, in addition to the existing EOF keyboard_eventsnow switches to raw mode (and disabling it switches back to cooked)
- Augments
-
8.0.115 May 2025Nothing published for this version
-
8.0.015 May 2025 withdrawnNothing published for this version
-
6.0.009 Feb 2025Nothing published for this version
-
5.0.014 Oct 2024 -
4.1.028 Apr 2024Release notes
Open source →- Feature: non-recursive watches with
WatchedPath::non_recursive() - Fix:
config.pathset()now preservesWatchedPathattributes - Refactor: move
WatchedPathto the root of the crate (old path remains as re-export for now)
- Feature: non-recursive watches with
-
4.0.020 Apr 2024Release notes
Open source →- Deps: replace command-group with process-wrap (in supervisor, but has flow-on effects)
- Deps: miette 7
- Deps: nix 0.28
-
3.0.129 Nov 2023 -
3.0.026 Nov 2023 withdrawnRelease notes
Open source →General
- Crate is more oriented around
Watchexecthe 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. - Deprecated items (mostly leftover from splitting out the
watchexec_eventsandwatchexec_signalscrates) are removed. - Watchexec can now supervise multiple commands at once. See Action below, the Action docs, and the Supervisor docs for more.
- Because of this new feature, the one where multiple commands could be set under the one supervisor is removed.
- Watchexec's supervisor was split up into its own crate,
watchexec-supervisor. - Tokio requirement is now 1.33.
- Notify was upgraded to 6.0.
- Nix was upgraded to 0.27.
WatchexecWatchexec::new()now takes theon_actionhandler. 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 publicconfigfield instead to access the "live"Arc<Config>(see below).- Completion events aren't emitted anymore. They still exist in the Event enum, but they're not generated by Watchexec itself. Use
Job#to_waitinstead. Of course you can insert them as synthetic events if you want.
Config
InitConfigandRuntimeConfighave been unified into a singleConfigstruct.- Instead of module-specific
WorkingDatastructures, all of the config is now flat in the sameConfig. That makes it easier to work with as all that's needed is to pass anArc<Config>around, but it does mean the event sources are no longer independent. - Instead of using
tokio::sync::watchfor some values, andHandlerLockfor handlers, and so on, everything is now a newChangeabletype, specialised toChangeableFnfor closures andChangeableFiltererfor the Filterer. - There's now a
signal_change()method which must be called after changes to the config; this is taken care of when using the methods onConfig. This is required for the few places in Watchexec which need active reconfiguration rather than reading config values just-in-time. - The above means that instead of using
Watchexec::reconfigure()and keeping a clone of the config around, anArc<Config>is now "live" and changes applied to it will affect the Watchexec instance directly. command/commandsare removed from config. Instead use the Action handler API for creating new supervised commands.command_groupedis removed from config. That's now an option set onCommand.action_throttleis renamed tothrottleand now defaults to50ms, which is the default in Watchexec CLI.keyboard_emit_eofis renamed tokeyboard_events.pre_spawn_handleris removed. UseJob#set_spawn_hookinstead.post_spawn_handleris removed. UseJob#runinstead.
Command
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
Programand behaviour-altering options.Shellhas 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::Shellfieldargs: 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- The old
argsfield ofCommand::Shellis now theoptionsfield ofShell. Shellhas a new fieldprogram_option: Option<Cow<OsStr>>which is the syntax of the option used to provide the command. Ie for most shells it's-cand forCMD.EXEit's/C; this makes it fully customisable (including its absence!) if you want to use weird shells or non-shell programs as shells.- The special-cased
Shell::Powershellis removed. - On Windows, arguments are specified with
raw_arginstead ofargto avoid quoting issues. Commandcan 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.- The top-level Watchexec
command_groupedoption is now Command-level, so you can start both grouped and non-grouped programs. - There's a new
reset_sigmaskoption to control whether commands should have their signal masks reset on Unix. By default the signal mask is inherited.
Errors
RuntimeError::NoCommands,RuntimeError::Handler,RuntimeError::HandlerLockHeld, andCriticalError::MissingHandlerare removed as the relevant types/structures don't exist anymore.RuntimeError::CommandShellEmptyCommandandRuntimeError::CommandShellEmptyShellare removed; you can constructShellwith empty shell program andProgram::Shellwith an empty command, these will at best do nothing but they won't error early through Watchexec.RuntimeError::ClearScreenis removed, as clearing the screen is now done by the consumer of Watchexec, not Watchexec itself.- Watchexec will now panic if locks are poisoned; we can't recover from that.
- The filesystem watcher's "too many files", "too many handles", and other initialisation errors are removed as
RuntimeErrors, and are nowCriticalErrors. 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. - The
on_errorhandler is now sync only and no longer returns aResult; as such there's no longer the weird logic of "if theon_errorhandler errors, it will call itself on the error once, then crash". - If you were doing async work in
on_error, you should instead use non-async calls (liketry_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. - Error diagnostic codes are removed.
Action
The process supervision system is entirely reworked. Instead of "applying
Outcomes", there's now aJobtype which is a single supervised command, provided by the separatewatchexec-supervisorcrate. The Action handler itself can only create new jobs and list existing ones, and interaction with commands is done through theJobtype.The controls available on
Jobare now modeled on "real" supervisors like systemd, and are both more and less powerful than the oldOutcomesystem. This can be seen clearly in how a "restart" is specified. Previously, this was anOutcomecombinator: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()andstart()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. Thetry_restart()method is available to do a restart only if the job is running, with thetry_restart_with_signal()variant for graceful restarts.Further, all of these methods are non-blocking sync (and take
&self), but they return aTicket, 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 specialto_wait()method returns a detached, cloneable, "wait()" future, which will resolve when the process exits, without needing to hold on to theJobor a reference at all.See the
restart_run_on_successful_buildexample which starts acargo build, waits for it to end, and then (re)startscargo runif the build exited successfully.Finally:
Outcome::ClearandOutcome::Resetare gone, and there's no equivalent onJob: 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. - Crate is more oriented around
-
2.3.229 Nov 2023Nothing published for this version
-
2.3.128 Nov 2023 withdrawnNothing published for this version
-
2.3.022 Mar 2023 -
2.2.018 Mar 2023Release notes
Open source →- Ditch MSRV policy. The
rust-versionindication 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) - Split off
watchexec-eventsandwatchexec-signalscrates. - Unify
SubSignalandMainSignalinto a newSignaltype. The former types and paths exist as deprecated aliases/re-exports.
- Ditch MSRV policy. The
-
2.1.114 Feb 2023Nothing published for this version
-
2.1.009 Jan 2023Release notes
Open source →- MSRV: bump to 1.61.0
- Deps: drop explicit dependency on
libcon Unix. - Internal: remove all usage of
dunce, replaced with either Tokio'scanonicalize(properly async) or normalize-path (performs no I/O). - Internal: drop support code for Fuchsia. MIO already didn't support it, so it never compiled there.
- Add
#[must_use]annotations to a bunch of functions. - Add missing
Sendbound toHandlerLock. - Add new keyboard event source; initially supports just detecting EOF on STDIN. (#449)
- Fix
summarise_events_to_envon Windows to output paths with backslashes.
-
2.0.207 Sep 2022 -
2.0.107 Sep 2022 -
2.0.017 Jun 2022Release notes
Open source →First "stable" release of the library.
-
Change: the library is split into even more crates
- Two new low-level crates,
project-originsandignore-files, extract standalone functionality - Filterers are now separate crates, so they can evolve independently (faster) to the main library crate
- These five new crates live in the watchexec monorepo, rather than being completely separate like
command-groupandclearscreen - This makes the main library bit less likely to change as often as it did, so it was finally time to release 2.0.0!
- Two new low-level crates,
-
Change: the Action worker now launches a set of Commands
- A new type
Commandreplaces and augmentsShell, making explicit which style of calling will be used - The action working data now takes a
Vec<Command>, so multiple commands to be run as a set - Commands in the set are run sequentially, with an error interrupting the sequence
- It is thus possible to run both "shelled" and "raw exec" commands in a set
PreSpawnandPostSpawnhandlers are run per Command, not per command set- This new style should be preferred over sending command lines like
cmd1 && cmd2
- A new type
-
Change: the event queue is now a priority queue
- Shutting down the runtime is faster and more predictable. No more hanging after hitting Ctrl-C if there's tonnes of events coming in!
- Signals sent to the main process have higher priority
- Events marked "urgent" skip filtering entirely
- SIGINT, SIGTERM, and Ctrl-C on Windows are marked urgent
- This means it's no longer possible to accidentally filter these events out
- They still require handling in
on_actionto do anything
- The API for the
Filterertrait changes slightly to let filterers use event priority
-
Improvement: 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_dropis set on Commands, which will be a little more eager to terminate processes when we're done with them -
Feature:
Outcome::Sleepwaits for a given duration (#79)
Other miscellaneous:
-
Deps: add the
logfeature to tracing so logs can be emitted tologsubscribers -
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
-
-
2.0.0-pre.1403 Apr 2022 pre-releaseRelease notes
Open source →- Replace git2 dependency by git-config (#267). This makes using the library more pleasant and will also avoid library version mismatch errors when the libgit2 library updates on the system.
-
2.0.0-pre.1318 Mar 2022 pre-releaseRelease notes
Open source →- Revert backend switch on mac from previous release. We'll do it a different way later (#269)
-
2.0.0-pre.1216 Mar 2022 pre-releaseRelease notes
Open source →- Upgraded to Notify pre.14
- Internal change: kqueue backend is used on mac. This should reduce or eliminate some old persistent bugs on mac, and improve response times, but please report any issues you have!
Watchexec::new()now reports the library's version at debug level- Notify version is now specified with an exact (
=) requirement, to avoid breakage (#266)
-
2.0.0-pre.1107 Mar 2022 pre-releaseRelease notes
Open source →- New
error::FsWatcherErrorenum split off fromRuntimeError, and with additional variants to take advantage of targeted help text for known inotify errors on Linux - Help text is now carried through elevated errors properly
- Globset filterer:
extensionsandfiltersare now cooperative rather than exclusionary. That is, a filters of["Gemfile"]and an extensions of["js", "rb"]will match bothGemfileandindex.jsrather than matching nothing at all. This restores pre 2.0 behaviour. - Globset filterer: on unix, a filter of
*/filewill match bothfileanddir/fileinstead of justdir/file. This is a compatibility fix and is incorrect behaviour which will be removed in the future. Do not rely on it.
- New
-
2.0.0-pre.1007 Feb 2022 pre-releaseRelease notes
Open source →- The
on_errorhandler gets an upgraded parameter which lets it upgrade (runtime) errors to critical. summarize_events_to_pathsnow deduplicates paths within each variable.
- The
-
2.0.0-pre.930 Jan 2022 pre-release -
2.0.0-pre.825 Jan 2022 pre-release -
2.0.0-pre.725 Jan 2022 pre-release withdrawnRelease notes
Open source →Yanked for critical bug in globset filterer (fixed in pre.8) on 2022-01-26
-
2.0.0-pre.618 Jan 2022 pre-releaseRelease notes
Open source →First version of library v2 that was used in a CLI release.
- Globset filterer was erroneously passing files with no extension when an extension filter was specified
-
2.0.0-pre.518 Jan 2022 pre-releaseRelease notes
Open source →- Update MSRV (to 1.58) and policy (bump incurs minor semver only)
- Some bugfixes around canonicalisation of paths
- Eliminate context-less IO errors
- Move error types around
- Prep library readme
- Update deps
-
2.0.0-pre.418 Jan 2022 pre-releaseRelease notes
Open source →- More logging, especially around ignore file discovery and filtering
- The const
paths::PATH_SEPARATORis now public, being:on Unix and;and Windows. - Add Subversion to discovered ProjectTypes
- Add common (sub)Filterer for ignore files, so they benefit from a single consistent implementation. This also makes ignore file discovery correct and efficient by being able to interpret ignore files which searching for ignore files, or in other words, not descending into directories which are ignored.
- Integrate this new IgnoreFilterer into the GlobsetFilterer and TaggedFilterer. This does mean that some old v1 behaviour of patterns in gitignores will not behave quite the same now, but that was arguably always a bug. The old "buggy" v1 behaviour around folder filtering remains for manual filters, which are those most likely to be surprising if "fixed".
-
2.0.0-pre.329 Dec 2021 pre-releaseRelease notes
Open source →summarise_events_to_envused to returnCOMMON_PATH, it now returnsCOMMON, in keeping with the other variable names.
-
2.0.0-pre.229 Dec 2021 pre-releaseRelease notes
Open source →summarise_events_to_envreturns aHashMap<&str, OsString>rather thanHashMap<&OsStr, OsString>, because the expectation is that the variable names are processed, e.g. in the CLI:WATCHEXEC_{}_PATH.OsStrmakes that painful for no reason (the strings are static anyway).- The
Actionstruct'seventsfield changes to be anArc<Vec<Event>>rather than aVec<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). - The
PreSpawnandPostSpawnstructs got a newevents: Arc<Vec<Event>>field so these handlers get read-only access to the events that triggered the command.
-
2.0.0-pre.121 Dec 2021 pre-releaseRelease notes
Open source →- MSRV bumped to 1.56
- Rust 2021 edition
- More documentation around tagged filterer:
==and!=are case-insensitive- the mapping of matcher to tags
- the mapping of matcher to auto op
- Finished the tagged filterer:
- Proper path glob matching
- Signal matching
- Process completion matching
- Allowlisting pattern works
- More matcher aliases to the parser
- Negated filters
- Some silly filter parsing bugs
- File event kind matching
- Folder filtering (main confusing behaviour in v1)
- Lots of tests:
- Globset filterer
- Including the "buggy"/confusing behaviour of v1, for parity/compat
- Tagged filterer:
- Paths
- Including verifying that the v1 confusing behaviour is fixed
- Non-path filters
- Filter parsing
- Ignore files
- Filter scopes
- Outcomes
- Change reporting in the environment
- ...Specify behaviour a little more precisely through that process
- Prepare the watchexec event type to be serializable
- A synthetic
FileType - A synthetic
ProcessEnd(ExitStatusreplacement)
- A synthetic
- Some ease-of-use improvements, mainly removing generics when overkill
-
2.0.0-pre.016 Oct 2021 pre-release -
1.17.213 Feb 2023Nothing published for this version
-
1.17.121 Jul 2021Release notes
Open source →- Process handling code replaced with the new command-group crate.
- #158 New option
use_process_group(defaulttrue) allows disabling use of process groups. - #168 Default debounce time further decreased to 100ms.
- Binstall configuration and transitional
cargo install watchexecstub removed.
-
1.17.021 Jul 2021Nothing published for this version
-
1.16.110 Jul 2021 -
1.16.008 May 2021 -
1.15.330 Apr 2021Nothing published for this version
-
1.15.226 Apr 2021Nothing published for this version
-
1.15.117 Apr 2021Nothing published for this version
-
1.15.010 Apr 2021Nothing published for this version
-
1.14.130 Sep 2020Nothing published for this version
-
1.14.003 Jul 2020Nothing published for this version
-
1.13.106 Jun 2020Nothing published for this version
-
1.13.004 Jun 2020Nothing published for this version
-
1.12.019 Nov 2019Nothing published for this version
-
1.11.128 Oct 2019Nothing published for this version
-
1.11.028 Oct 2019Nothing published for this version
-
1.10.330 Jul 2019Nothing published for this version
-
1.10.229 May 2019Nothing published for this version
-
1.10.118 Feb 2019Nothing published for this version
-
1.10.026 Jan 2019Nothing published for this version
-
1.9.209 Sep 2018Nothing published for this version
-
1.9.109 Sep 2018 withdrawnNothing published for this version
-
1.9.019 Aug 2018Nothing published for this version