Compare commits

..

1093 commits
1.9.1 ... main

Author SHA1 Message Date
Félix Saparelli
1c916a01cc
chore: Release 2024-10-14 23:36:34 +13:00
Félix Saparelli
51a70329c4
chore: Release 2024-10-14 23:34:34 +13:00
Félix Saparelli
ffcdc1b536
changelog 2024-10-14 23:34:18 +13:00
Félix Saparelli
d0dcbf0489
chore: Release 2024-10-14 23:33:42 +13:00
Félix Saparelli
a0ca6294c6
chore: Release 2024-10-14 23:29:33 +13:00
Félix Saparelli
cb2a5087ff
doc: fix watchexec lib.rs doc comment links 2024-10-14 23:29:20 +13:00
Félix Saparelli
ecc3256fa4
chore: Release 2024-10-14 23:22:34 +13:00
Félix Saparelli
51c094b8a7
chore: Release 2024-10-14 23:20:00 +13:00
Félix Saparelli
47d3f02ee3
changelog 2024-10-14 23:19:21 +13:00
Félix Saparelli
e36799d9e3
chore: Release 2024-10-14 23:16:59 +13:00
Félix Saparelli
9082f63114
chore: Release 2024-10-14 23:16:21 +13:00
Félix Saparelli
303396e5a6
chore: Release 2024-10-14 23:15:41 +13:00
Félix Saparelli
be3707abc1
changelogs 2024-10-14 23:15:19 +13:00
Félix Saparelli
4c9e72811f
Manpage and completions 2024-10-14 18:06:42 +13:00
Félix Saparelli
cc307828d2
Update bosion example deps 2024-10-14 17:48:20 +13:00
Félix Saparelli
0dafc4f260
fix tests 2024-10-14 15:46:07 +13:00
Félix Saparelli
967d53dfc0
docs: remove mentions of command-group 2024-10-14 12:10:45 +13:00
Félix Saparelli
71aa35a095
deps 2024-10-14 12:02:08 +13:00
Félix Saparelli
c46a06c263
document current release process 2024-10-14 11:47:42 +13:00
Félix Saparelli
792ebf6d04
chore: Release 2024-10-14 11:43:27 +13:00
Félix Saparelli
ace8ea8c63
changelog 2024-10-14 11:42:53 +13:00
Егор Куклин
6c245d3aff
Whitelist files if explicitly watched (#859) 2024-08-21 12:20:09 +12:00
Егор Куклин
08367c6db7
Replace std::fs::canonicalize with dunce::canonicalize (#858)
Closes #830 by going back to non-UNC paths on windows
sample.bat:
```
echo Hello world
```
`cargo run -r -- .\sample.bat` now runs as expected.
Added the same version of dunce as in `ignore-files/Cargo.toml`
2024-08-16 22:39:34 +00:00
Félix Saparelli
2cc7743677
macos-11 was retired 2024-07-03 02:33:01 +12:00
Félix Saparelli
2c1388e678
chore: Release 2024-06-30 21:02:32 +12:00
Félix Saparelli
b0125bc113 Manpage and completions 2024-06-29 15:39:37 +12:00
Félix Saparelli
510dd625d1 feat: add --watch-file
Fixes #849
2024-06-29 15:39:37 +12:00
Félix Saparelli
6e981fd8ab fix(packaging): wrong path in deb/rpm packaging for manpage
Fixes #847
2024-06-29 15:39:37 +12:00
Félix Saparelli
24df7a4f45 repo: fix deprecated cargo config file 2024-06-29 15:39:37 +12:00
Félix Saparelli
9f1f2e9d04
chore: Release 2024-05-16 14:20:58 +12:00
Félix Saparelli
0e393c25cf
update changelog 2024-05-16 14:20:36 +12:00
Luca Barbato
2026c52abd
feat: Add git-describe support (#832) 2024-05-15 13:02:25 +00:00
Félix Saparelli
72f069a847
chore: Release 2024-04-30 20:41:43 +12:00
Adit
4affed6fff
fix(cli): recursive paths provided by user getting treated non-recursively (#828) 2024-04-30 07:10:28 +00:00
Félix Saparelli
e0084e69f8
fix ci again 2024-04-28 19:14:21 +12:00
Félix Saparelli
592b712c95
chore: Release 2024-04-28 18:55:23 +12:00
Félix Saparelli
c9a3b9df00
chore: Release 2024-04-28 18:53:42 +12:00
Félix Saparelli
e63d37f601
chore: Release 2024-04-28 18:52:50 +12:00
Félix Saparelli
14e6294f5a
chore: Release 2024-04-28 18:51:48 +12:00
Félix Saparelli
234d606563
chore: Release 2024-04-28 18:50:18 +12:00
Félix Saparelli
77405c8ce1
chore: Release 2024-04-28 18:48:50 +12:00
Félix Saparelli
6c23afe839
feat: make it possible to watch non-recursively (#827)
Fixes #227
Fixes #174

docs(cli): be more precise in print-events advice to use `-v`
docs(cli): improve jaq error help
feat(cli): add `-W` for non-recursive watches
feat(cli): use non-blocking logging
feat(globset): hide `fmt::Debug` spew from ignore crate
feat(ignore-files): hide `fmt::Debug` spew from ignore crate
feat(lib): make it possible to watch non-recursively
fix(lib): inserting `WatchedPath`s directly should be possible
refactor(lib): move `WatchedPath` out of `fs` mod
2024-04-28 06:33:07 +00:00
dependabot[bot]
ee3795d776
Bump softprops/action-gh-release from 2.0.3 to 2.0.4 (#823)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-28 18:19:34 +12:00
Félix Saparelli
eff96c7324
feat(project-origins): add support for out-of-tree git repos (#826) 2024-04-28 14:26:07 +12:00
Félix Saparelli
a4df258735
doc: fix --on-busy-update help text (#825) 2024-04-23 14:44:59 +12:00
Félix Saparelli
d388a280f0
ci: more build improvements (for next time) 2024-04-21 02:11:37 +12:00
Félix Saparelli
bb97f71c8c
gha: probably the most frustrating syntax in the world 2024-04-21 02:04:56 +12:00
Félix Saparelli
953fa89dd9
even better 2024-04-21 02:02:57 +12:00
Félix Saparelli
0ef87821f2
Run manpage and completions in release when we've already built in releases 2024-04-21 01:57:09 +12:00
Félix Saparelli
62af5dd868
Fix dist manifest 2024-04-21 01:52:11 +12:00
Félix Saparelli
4497aaf515
Fix release builder 2024-04-21 01:38:11 +12:00
Félix Saparelli
a63864c5f2
chore: Release 2024-04-21 01:18:24 +12:00
Félix Saparelli
ee815ba166
chore: Release 2024-04-21 01:06:46 +12:00
Félix Saparelli
d6138b9961
chore: Release 2024-04-21 01:04:18 +12:00
Félix Saparelli
f73d388d18
Changelogs for filterers 2024-04-21 01:03:58 +12:00
Félix Saparelli
86d6c7d448
Remove more PR machinery 2024-04-21 01:02:40 +12:00
Félix Saparelli
d317540fd3
chore: Release 2024-04-21 01:00:28 +12:00
Félix Saparelli
9d91c51651
chore: Release 2024-04-21 00:56:27 +12:00
Félix Saparelli
96480cb588
chore: Release 2024-04-21 00:55:14 +12:00
Félix Saparelli
fd5afb8b3a
Add --wrap-process (#822) 2024-04-20 12:39:28 +00:00
Félix Saparelli
e1cef25d7f
Fix watchexec-events tests 2024-04-21 00:36:59 +12:00
Félix Saparelli
22b58a66ab
Remove tagged filterer 2024-04-21 00:32:01 +12:00
Félix Saparelli
1c47ffbe1a
Update release.toml config 2024-04-21 00:30:56 +12:00
Félix Saparelli
48ff7ec68b
Remove PR machinery 2024-04-21 00:28:06 +12:00
Félix Saparelli
4023bf7124
chore: Release 2024-04-21 00:21:04 +12:00
Félix Saparelli
8864811e79
Fix watchexec-events self-dependency 2024-04-21 00:19:11 +12:00
Félix Saparelli
7535e17661
Fix #809: clear screen before starting process, not on every event (#821) 2024-04-20 12:15:52 +00:00
Félix Saparelli
8ad12b1f65
chore: Release 2024-04-21 00:13:30 +12:00
Félix Saparelli
dca13fed43
chore: Release 2024-04-21 00:12:21 +12:00
Félix Saparelli
f81aed1260
Don't create a tmpfile until one is needed (#820) 2024-04-20 23:52:28 +12:00
Félix Saparelli
ec316a7279
Breaking changes to CLI: various removals (#819) 2024-04-20 11:44:21 +00:00
Félix Saparelli
e505a9ad05
Breaking changes for --on-busy-update (#818) 2024-04-20 11:15:25 +00:00
Félix Saparelli
317221584a
Breaking changes for --shell (#817) 2024-04-20 10:58:29 +00:00
Félix Saparelli
af24252f21
Experimental filter programs (#571) 2024-04-20 10:06:53 +00:00
Félix Saparelli
b72248a38c
Update deps (#816) 2024-04-20 05:45:50 +00:00
Chris West
11b98f776a
feat: under --clear reset, always reset at exit (#797) 2024-04-20 17:03:19 +12:00
dependabot[bot]
8c22d0cac7
Bump actions/download-artifact from 3 to 4 (#732)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-20 04:59:04 +00:00
dependabot[bot]
338999eb65
Bump softprops/action-gh-release from 1 to 2 (#799)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-20 16:58:33 +12:00
dependabot[bot]
538439f045
Bump mathieudutour/github-tag-action from 6.1 to 6.2 (#798)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-20 16:58:27 +12:00
Félix Saparelli
75b2c4b4ae
Adapt supervisor to process-wrap (#815) 2024-04-20 16:58:17 +12:00
dependabot[bot]
a6e0b3f70a
Bump actions/cache from 3 to 4 (#772)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-20 16:42:55 +12:00
dependabot[bot]
f538b74e81
Bump actions/upload-artifact from 3 to 4 (#733)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-20 16:42:08 +12:00
dependabot[bot]
a50ce396cb
Bump mio from 0.8.10 to 0.8.11 (#796)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-06 13:01:52 +13:00
Félix Saparelli
1846c96b86
feat(cli): Add NO_COLOR support (#779) 2024-02-11 05:13:41 +00:00
dependabot[bot]
8b39279423
Bump h2 from 0.3.22 to 0.3.24 (#769)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 04:39:42 +00:00
Félix Saparelli
7f4fba02ef
Remove fish completion from rpm and deb packaging (#767) 2024-01-13 01:29:55 +00:00
github-actions[bot]
d3949cc6e9
release: watchexec-cli v1.25.1 (#764)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2024-01-05 04:41:32 +00:00
github-actions[bot]
465ccfc597
release: watchexec-filterer-ignore v3.0.1 (#763)
Co-authored-by: github-actions <github-actions@github.com>
2024-01-05 15:48:04 +13:00
github-actions[bot]
6a2f637a60
release: ignore-files v2.1.0 (#762)
Co-authored-by: github-actions <github-actions@github.com>
2024-01-04 11:32:58 +00:00
Félix Saparelli
4f757de8df
Canonicalise paths for ignore discovery (#760) 2024-01-04 09:32:47 +00:00
Félix Saparelli
217f57f6a2
Update to async-priority-channel 0.2.0 (#761) 2024-01-04 22:13:08 +13:00
github-actions[bot]
682a9b4d21
release: watchexec-cli v1.25.0 (#754)
Co-authored-by: github-actions <github-actions@github.com>
2024-01-01 10:39:18 +00:00
github-actions[bot]
447b6fa963
release: watchexec-filterer-globset v3.0.0 (#752)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2024-01-01 09:10:43 +00:00
github-actions[bot]
3cbf277b2e
release: watchexec-filterer-tagged v2.0.0 (#753)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2024-01-01 09:09:24 +00:00
github-actions[bot]
48793008eb
release: watchexec-filterer-ignore v3.0.0 (#751)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2024-01-01 08:47:32 +00:00
github-actions[bot]
1ef2fcebf1
release: ignore-files v2.0.0 (#750)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2024-01-01 07:54:19 +00:00
github-actions[bot]
8523bd196c
release: project-origins v1.3.0 (#749)
Co-authored-by: github-actions <github-actions@github.com>
2024-01-01 06:07:03 +00:00
Victor Adossi ("vados")
cb1cfb6bf5
Optimise ignore file gathering (#663)
Co-authored-by: Félix Saparelli <felix@passcod.name>
2024-01-01 05:01:14 +00:00
Félix Saparelli
bf9c85f598
Tweak origin detection lists (#748) 2024-01-01 03:41:14 +00:00
thislooksfun
3ad0e1aa57
Respect applies_in scope when processing nested ignores (#746)
Previously, when importing multiple nested ignore files, some info from
the parent—notably the "root" path—would be inherited. This lead to some
problems with matching of "pseudo-absolute" rules (those with a leading
slash) in nested ignore files (see #745 for more details). To fix this,
we now fully isolate each path of the tree during the import process.
This leads to more accurate, though unfortunately slightly less
performant, rule matching. The only time a builder is reused now is if
two input files have the same `applies_in` value, in which case they are
merged together.

I have added tests to ensure correctness and prevent a regression. I
also was careful to make sure no previous tests broke in any way (all
changes to existing tests were made in isolation, and thus are not
affected by the logic changes). As far as I can tell, the only behavior
change is that now some previously-ignored rules will now be applied,
which could, in very rare configurations, lead to files being
unintentionally ignored. However, due to the aforementioned logic bug,
those files were all ignored by git already, so I suspect the number of
people actually caught off guard by this change to be extremely low,
likely zero.

Fixes #745.
2023-12-30 14:12:59 +13:00
Félix Saparelli
f65a0d21ba
Fix CLI release workflow more (#740) 2023-12-20 01:06:41 +00:00
Félix Saparelli
aa0bf1c24f
Fix CLI release workflow when dispatching (#739) 2023-12-19 23:09:48 +00:00
Félix Saparelli
0a6811f1fb
Update cargo.lock (#738) 2023-12-19 22:34:29 +00:00
github-actions[bot]
e9cce54179
release: watchexec-cli v1.24.2 (#736)
Co-authored-by: github-actions <github-actions@github.com>
2023-12-19 13:10:59 +00:00
github-actions[bot]
6ecc5569e4
release: watchexec-supervisor v1.0.3 (#735)
Co-authored-by: github-actions <github-actions@github.com>
2023-12-19 11:31:34 +00:00
Félix Saparelli
eb4f2ce201
Fix queueing behaviour (#734) 2023-12-19 11:22:59 +00:00
Félix Saparelli
b4a64a096a
Add eyra support as a feature (#728) 2023-12-13 14:08:03 +13:00
github-actions[bot]
a72ff0e142
release: watchexec-cli v1.24.1 (#722)
Co-authored-by: github-actions <github-actions@github.com>
2023-12-11 12:30:05 +00:00
Félix Saparelli
53c9e344eb
Fix argfile regression (#720) 2023-12-11 17:05:38 +13:00
Félix Saparelli
e4e8d39546
Graceful quit on Ctrl-C (#721) 2023-12-11 01:21:57 +00:00
github-actions[bot]
4026938c18
release: watchexec-filterer-tagged v1.0.0 (#719)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-12-10 22:45:04 +00:00
Félix Saparelli
0557d70963
watchexec-filterers: fix bad publish (#715) 2023-12-09 11:24:00 +00:00
github-actions[bot]
477d59d319
release: watchexec-cli v1.24.0 (#699)
Co-authored-by: Félix Saparelli <felix@passcod.name>
Co-authored-by: github-actions <github-actions@github.com>
2023-12-09 10:52:40 +00:00
github-actions[bot]
a166b3bc9f
release: watchexec-supervisor v1.0.2 (#713)
Co-authored-by: github-actions <github-actions@github.com>
2023-12-09 10:43:55 +00:00
github-actions[bot]
deb6072a26
release: watchexec-signals v2.1.0 (#711)
Co-authored-by: github-actions <github-actions@github.com>
2023-12-09 23:25:41 +13:00
Félix Saparelli
709dbe5151
New option: --signal-map (#710) 2023-12-09 09:30:58 +00:00
Félix Saparelli
03460a6181
More logging for CLI and Supervisor (#709) 2023-12-09 09:10:11 +00:00
github-actions[bot]
44d794c921
release: watchexec-supervisor v1.0.1 (#708)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-11-29 06:10:10 +00:00
github-actions[bot]
c75134d255
release: watchexec-events v2.0.1 (#707)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-11-29 05:56:23 +00:00
github-actions[bot]
e90bf3756e
release: watchexec v3.0.1 (#706)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-11-29 05:41:27 +00:00
github-actions[bot]
91b34bc96e
release: watchexec-signals v2.0.0 (#705)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-11-29 05:18:03 +00:00
github-actions[bot]
c4dceb2d88
release: watchexec-events v2.0.0 (#704)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-11-29 05:15:33 +00:00
Félix Saparelli
16be20050b
Amend readmes (#702) 2023-11-28 11:30:33 +00:00
Félix Saparelli
0e94f220e3
Tweaks to help (#700) 2023-11-27 12:57:01 +00:00
Félix Saparelli
9af9189ac4
Add --quiet, --timings, --colo[u]r, --bell (#698) 2023-11-27 12:12:51 +00:00
github-actions[bot]
16e606e944
release: watchexec-filterer-globset v2.0.0 (#696)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-27 10:52:28 +00:00
Félix Saparelli
dc91e69966
Fix bosion readme (#697) 2023-11-27 10:51:51 +00:00
Félix Saparelli
2751caeb39
Add --ignore-nothing (#695) 2023-11-27 10:50:39 +00:00
github-actions[bot]
e7580b0a35
release: watchexec-filterer-ignore v2.0.0 (#694)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-27 10:38:06 +00:00
Félix Saparelli
63562fe64d
Add --only-emit-events (#691) 2023-11-27 23:29:55 +13:00
github-actions[bot]
d9f6d20b6b
release: watchexec v3.0.0 (#692)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-26 04:54:43 +00:00
github-actions[bot]
fb2c5449af
release: watchexec-supervisor v1.0.0 (#690)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-26 04:23:20 +00:00
github-actions[bot]
64bdf7c9d5
release: ignore-files v1.3.2 (#689)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-26 04:22:00 +00:00
github-actions[bot]
65e2db31bc
release: watchexec-events v1.1.0 (#688)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-26 03:33:47 +00:00
github-actions[bot]
f66aa5d808
release: project-origins v1.2.1 (#687)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-26 03:30:39 +00:00
github-actions[bot]
efacb29f86
release: watchexec-signals v1.0.1 (#686)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-26 03:29:53 +00:00
github-actions[bot]
10556dea11
release: bosion v1.0.2 (#685)
Co-authored-by: github-actions <github-actions@github.com>
2023-11-26 03:29:49 +00:00
Félix Saparelli
87ca5d6b20
Dependabot: add supervisor and remove tagged filterer (#684) 2023-11-26 16:21:13 +13:00
Félix Saparelli
318f850e9b
Remove obsolete release order (#683) 2023-11-26 03:15:29 +00:00
Félix Saparelli
b54cd60146
Update all changelogs (#682) 2023-11-26 03:01:13 +00:00
Félix Saparelli
89e3d60ecf
Clippy nursery (#681) 2023-11-26 02:40:57 +00:00
Félix Saparelli
a13bc429eb
Watchexec lib v3 (#601)
Co-authored-by: emilHof <95590295+emilHof@users.noreply.github.com>
2023-11-25 20:33:44 +00:00
Felix Yan
7f23fbd68a
Update Arch Linux package URL in packages.md (#679) 2023-11-21 23:26:59 +00:00
dependabot[bot]
652b8c9b2f
Bump actions/checkout from 3 to 4 (#651)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-05 16:05:40 +12:00
github-actions[bot]
19a3fc9189
release: watchexec-cli v1.23.0 (#647)
Co-authored-by: github-actions <github-actions@github.com>
2023-08-30 06:18:56 +00:00
Félix Saparelli
3639f06745
Add --no-discover-ignore (#645) 2023-08-30 04:21:13 +00:00
Félix Saparelli
1aaff8d319
Re-add -d short flag (#635) 2023-08-30 04:04:53 +00:00
Félix Saparelli
345eda871b
Skip search for ignores when not needed (#644) 2023-08-30 04:01:23 +00:00
Félix Saparelli
4c3b9f0960
Clippy and update lockfile (#646) 2023-08-30 03:43:57 +00:00
github-actions[bot]
96bf3d231e
release: bosion v1.0.1 (#623)
Co-authored-by: github-actions <github-actions@github.com>
2023-07-02 23:47:13 +00:00
dependabot[bot]
afe317d4ee
Update gix-config requirement from 0.24.0 to 0.25.1 in /crates/ignore-files (#622)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-30 10:33:37 +12:00
dependabot[bot]
a91d80141b
Update gix requirement from 0.47 to 0.48 in /crates/bosion (#621)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-30 10:33:26 +12:00
Kian-Meng Ang
402d1ba4f9
Fix some typos (#617) 2023-06-26 22:12:24 +00:00
dependabot[bot]
0f2f5f4ea6
Update gix-config requirement from 0.22.0 to 0.24.0 in /crates/ignore-files (#614)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-26 13:10:47 +12:00
Jiahao XU
f08002aaf6
dep: bump gix from v0.44.1 => v0.47 (#619) 2023-06-25 23:01:16 +00:00
korrat
d72fc38e62
Use raw_arg for CMD on Windows (#616) 2023-06-24 14:10:00 +00:00
dependabot[bot]
2a3a3dee5f
Update clap_complete_nushell requirement from 0.1.10 to 4.3.1 in /crates/cli (#598)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-04 21:00:10 +12:00
dependabot[bot]
6552e3cd7e
Update shadow-rs requirement from 0.21.0 to 0.22.0 in /crates/cli (#597)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-04 20:59:11 +12:00
Félix Saparelli
31b9c2fb91
Fix logfile not creating if it doesn’t exist (#591) 2023-06-04 20:58:58 +12:00
github-actions[bot]
d25ae71787
release: ignore-files v1.3.1 (#600)
Co-authored-by: github-actions <github-actions@github.com>
2023-06-04 20:53:24 +12:00
Jonathan Cammisuli
638cddcf83
handle relative paths when using Ignore programmatically (#599) 2023-06-03 20:24:39 +12:00
github-actions[bot]
eb19f83761
release: watchexec-cli v1.22.3 (#590)
Co-authored-by: github-actions <github-actions@github.com>
2023-05-14 10:51:37 +00:00
github-actions[bot]
1d87097b3e
release: watchexec-filterer-ignore v1.2.1 (#589)
Co-authored-by: github-actions <github-actions@github.com>
2023-05-14 10:04:52 +00:00
github-actions[bot]
7c1c726d85
release: ignore-files v1.3.0 (#588)
Co-authored-by: github-actions <github-actions@github.com>
2023-05-14 09:31:05 +00:00
Félix Saparelli
0b50c91a1c
Fix warnings (#586) 2023-05-14 08:24:57 +00:00
Félix Saparelli
03b3aef9b7
Add recent PRs to changelogs (#587) 2023-05-14 06:30:34 +00:00
Félix Saparelli
0a1e5781cc
Try to fix windows tests (#585) 2023-05-14 18:02:57 +12:00
Félix Saparelli
af2e1562f1
Update completions/manpage (#584) 2023-05-14 17:36:28 +12:00
Félix Saparelli
1028caa389
Update deps (#583) 2023-05-14 05:17:14 +00:00
Jonathan Cammisuli
6a86caacf2
Fix adding globs to the IgnoreFilter (#581) 2023-05-14 16:59:59 +12:00
Jonathan Cammisuli
0afb5f2796
fix bosion tests (#582) 2023-05-11 09:53:42 +12:00
Jonathan Cammisuli
3e79957ad3
Handle nested gitignores (#580) 2023-05-08 22:13:04 +00:00
dependabot[bot]
e6f2dcdf48
Update gix requirement from 0.42.0 to 0.44.1 in /crates/bosion (#577)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-06 23:41:19 +00:00
dependabot[bot]
f9042808ab
Update embed-resource requirement from 1.6.1 to 2.1.1 in /crates/cli (#566)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-06 23:40:20 +00:00
dependabot[bot]
299a01dcf6
Bump enumflags2 from 0.7.5 to 0.7.7 (#573)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-07 11:26:18 +12:00
dependabot[bot]
b9c052d10e
Update gix-config requirement from 0.19.0 to 0.22.0 in /crates/ignore-files (#576)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-07 11:26:02 +12:00
dependabot[bot]
bccc0292a0
Bump h2 from 0.3.16 to 0.3.17 (#572)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-14 06:09:51 +12:00
Caleb Schoepp
8e91d26ef6
Update clearscreen requirement from 2.0.0 to 2.0.1 in /crates/lib (#570) 2023-04-06 00:48:48 +00:00
github-actions[bot]
e8391cd1d0
release: watchexec-cli v1.22.2 (#560)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-22 11:37:49 +00:00
Félix Saparelli
8a2808027f
Normalise paths to unix on windows for filtering only (#558) 2023-03-22 10:55:19 +00:00
Félix Saparelli
38109b8128
Disable signals on windows (#559) 2023-03-22 23:54:53 +13:00
Félix Saparelli
7e06e2d91d
Build manpage just before packaging (#557) 2023-03-22 04:46:19 +00:00
Félix Saparelli
4d081848a7
Fix tests-pass job (#556) 2023-03-22 04:40:38 +00:00
Félix Saparelli
1979d4400d
Update manpage (#555) 2023-03-22 04:28:43 +00:00
Félix Saparelli
7330827db4
Fix dist-manifest gen (#554) 2023-03-22 04:26:15 +00:00
Félix Saparelli
955562062f
Switch away from set-output (#553) 2023-03-22 04:23:48 +00:00
github-actions[bot]
4294bd42da
release: watchexec-cli v1.22.1 (#551)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-03-22 03:54:07 +00:00
github-actions[bot]
f0d115f600
release: watchexec v2.3.0 (#550)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-03-22 02:02:01 +00:00
Félix Saparelli
ce9c5d6b22
Always add changelog section even if it's not used later (#552) 2023-03-22 01:55:16 +00:00
Félix Saparelli
59044a5cba
Add Outcome::Race (#548) 2023-03-22 14:47:26 +13:00
Félix Saparelli
483b29af8e
Kill process on drop when using command-group (#549) 2023-03-22 01:35:46 +00:00
Félix Saparelli
c166823cb6
Log important beats as info (#547) 2023-03-22 01:23:00 +00:00
dependabot[bot]
79b28d0264
Update dirs requirement from 4.0.0 to 5.0.0 in /crates/cli (#543)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-21 13:16:06 +13:00
Félix Saparelli
e06dc0dd16
Fix release final step (#541) 2023-03-18 16:05:14 +00:00
Félix Saparelli
f15ea8a825
Fix release scripts (#540)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-18 15:36:49 +00:00
github-actions[bot]
bfb0a0648a
release: watchexec-filterer-tagged v0.3.0 (#539)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-18 15:06:57 +00:00
github-actions[bot]
c337ebca86
release: watchexec-cli v1.22.0 (#538)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-18 15:06:47 +00:00
github-actions[bot]
a3ebfcceef
release: watchexec-filterer-globset v1.2.0 (#537)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-18 13:38:37 +00:00
github-actions[bot]
cba452edf2
release: watchexec-filterer-ignore v1.2.0 (#536)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-18 12:58:14 +00:00
github-actions[bot]
7444ec133f
release: watchexec v2.2.0 (#535)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-03-19 01:34:55 +13:00
github-actions[bot]
26679721aa
release: ignore-files v1.2.0 (#534)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-03-19 01:08:22 +13:00
github-actions[bot]
d8f997e104
release: watchexec-events v1.0.0 (#533)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-18 11:27:38 +00:00
github-actions[bot]
d6ae98e644
release: watchexec-signals v1.0.0 (#532)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-18 11:09:41 +00:00
Félix Saparelli
9ae16187f0
Set new crates to zero for release (#531) 2023-03-18 10:43:29 +00:00
Félix Saparelli
5c63a99013
Fix and adjust docs (#530) 2023-03-18 10:23:46 +00:00
Félix Saparelli
80e18ea145
Rename --manpage to --manual (#529) 2023-03-18 22:34:33 +13:00
Félix Saparelli
8156864bf3
--emit-events-to (#515) 2023-03-18 21:32:24 +13:00
dependabot[bot]
64ad4e31f5
Update gix-config requirement from 0.18.0 to 0.19.0 in /crates/ignore-files (#526)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-18 21:22:58 +13:00
dependabot[bot]
7383679fbc
Update gix requirement from 0.40.0 to 0.42.0 in /crates/bosion (#528)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-18 21:22:48 +13:00
dependabot[bot]
d866ee615f
Update gix requirement from 0.39.0 to 0.40.0 in /crates/bosion (#523)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-10 11:39:19 +13:00
dependabot[bot]
f83790bce3
Update gix-config requirement from 0.17.0 to 0.18.0 in /crates/ignore-files (#518)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-05 17:17:11 +13:00
dependabot[bot]
d5fb5bfe26
Update gix requirement from 0.38.0 to 0.39.0 in /crates/bosion (#517)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-05 17:16:54 +13:00
Félix Saparelli
0fe17e036a
Bosion test on windows (#521) 2023-03-05 04:06:24 +00:00
github-actions[bot]
639bef9e31
release: bosion v1.0.0 (#520)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-05 03:47:58 +00:00
Félix Saparelli
690b32f030
Add bosion to releaser (#519) 2023-03-05 16:29:54 +13:00
Félix Saparelli
f76b16c4f4
Add bosion crate: build-time info (#516) 2023-03-05 03:26:23 +00:00
Félix Saparelli
14f53a6bf5
Clap 4 and revamp manpage, completions (#513) 2023-03-05 14:57:34 +13:00
Félix Saparelli
1e72cf6866
Revert "Fix #375: urgent events bypass throttle" (#514) 2023-03-03 15:34:15 +00:00
Félix Saparelli
c87a7343b3
Update deps (#511) 2023-03-04 04:21:38 +13:00
Félix Saparelli
e841333c07
Fix #375: urgent events bypass throttle (#509) 2023-03-02 07:18:34 +00:00
David Perez
70beb51ab5
Switch to gix-config because git-config is deprecated (#502)
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-03-02 16:17:19 +13:00
Félix Saparelli
46453a2b35
Ditch MSRV policy (#510) 2023-03-02 15:33:39 +13:00
Rikard Hjort
6232c213a9
update package name (#507) 2023-02-26 07:02:33 +00:00
Félix Saparelli
ca0c300193
Update effective MSRV (#505) 2023-02-22 22:23:00 +00:00
Félix Saparelli
b8a8b2265e
releng: Checksum to individual files, write dist-manifest.json (#500) 2023-02-15 04:34:06 +00:00
github-actions[bot]
d4b86366a2
release: watchexec-cli v1.21.1 (#499)
Co-authored-by: github-actions <github-actions@github.com>
2023-02-14 23:56:59 +00:00
github-actions[bot]
84d3947247
release: watchexec v2.1.1 (#498)
Co-authored-by: github-actions <github-actions@github.com>
2023-02-14 23:04:21 +00:00
Félix Saparelli
5850c284ca
Exit explicitly when we're PID 1 (#497) 2023-02-15 11:29:54 +13:00
Félix Saparelli
90eb9cae03
Reset process sigmask for Rust 1.66 (#494) 2023-02-15 11:11:57 +13:00
dependabot[bot]
2c06f06ee1
Bump tokio from 1.23.1 to 1.24.2 (#491)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-10 15:55:29 +13:00
Félix Saparelli
d6b1d36c6e
Avoid looping in demo (#486) 2023-01-21 08:56:42 +00:00
Félix Saparelli
d92a7eb675
Add #449 to lib changelog (#481) 2023-01-11 15:43:28 +00:00
Félix Saparelli
950f302ebf
Lock CI for msrv checks (#482) 2023-01-12 04:35:34 +13:00
dependabot[bot]
19f5bd0d1e
Update git-config requirement from 0.14.0 to 0.15.0 in /crates/ignore-files (#480)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-10 15:36:43 +13:00
github-actions[bot]
2479aaec4f
release: watchexec-cli v1.21.0 (#478)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-01-09 01:09:21 +00:00
github-actions[bot]
4e3e7a140a
release: watchexec-filterer-tagged v0.2.0 (#477)
Co-authored-by: github-actions <github-actions@github.com>
2023-01-09 00:34:40 +00:00
github-actions[bot]
daa11aad09
release: watchexec-filterer-globset v1.1.0 (#476)
Co-authored-by: github-actions <github-actions@github.com>
2023-01-09 00:32:37 +00:00
Félix Saparelli
7ba37b3964
Update PR advice for auto-merge; add meta comment (#475) 2023-01-09 00:20:59 +00:00
github-actions[bot]
7a8680e937
release: watchexec-filterer-ignore v1.1.0 (#474)
Co-authored-by: github-actions <github-actions@github.com>
2023-01-09 00:15:16 +00:00
github-actions[bot]
ac2a14fd80
release: watchexec v2.1.0 (#472)
Co-authored-by: github-actions <github-actions@github.com>
2023-01-09 00:01:35 +00:00
Félix Saparelli
2523bf5a65
Add PR trigger on auto-merge enable (#473) 2023-01-08 23:59:46 +00:00
github-actions[bot]
33f5ba5ef9
release: ignore-files v1.1.0 (#471)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-01-08 17:12:08 +00:00
github-actions[bot]
f55d669e3c
release: project-origins v1.2.0 (#470)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-01-08 16:05:49 +00:00
Félix Saparelli
a23b924be2
Remove remnants of the old release flow that now break things (#469) 2023-01-08 15:23:31 +00:00
Félix Saparelli
404386e795
Add guide for crate release order (#468) 2023-01-08 15:16:00 +00:00
dependabot[bot]
5d0920c5c9
Bump tokio from 1.21.0 to 1.23.1 (#467) 2023-01-07 00:10:45 +00:00
Felix Uellendall
f3305c5484
Add usecase to README (#460)
Co-authored-by: Félix Saparelli <felix@passcod.name>
2023-01-06 14:20:16 +00:00
dependabot[bot]
2387318ea2
Update command-group requirement from 1.0.8 to 2.0.1 in /crates/lib (#462)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 03:18:16 +13:00
dependabot[bot]
50c1832b35
Update git-config requirement from 0.12.0 to 0.14.0 in /crates/ignore-files (#464)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 03:18:01 +13:00
dependabot[bot]
60a5992e7a
Update clearscreen requirement from 1.0.9 to 2.0.0 in /crates/lib (#463)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 03:17:48 +13:00
dependabot[bot]
97694c05f5
Bump taiki-e/install-action from 1 to 2 (#461)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 03:17:33 +13:00
Félix Saparelli
81d59b62bc
Add to changelogs for libs (#466) 2023-01-06 14:14:42 +00:00
malteneuss
af5742945b
Add complex converter tool wrapper example to docs (#458) 2023-01-06 14:06:40 +00:00
Félix Saparelli
dc98370492
Clippy fixes (#465) 2023-01-07 02:53:49 +13:00
Félix Saparelli
6d65c05e35
Add openSUSE and MINGW packages (#453) 2022-12-02 23:16:56 +00:00
Félix Saparelli
75da1a61ed
Update contrib guide (#452) 2022-12-02 23:07:36 +00:00
Félix Saparelli
04a8ee22bd
Add apt.cli.rs to install methods (#451) 2022-12-02 22:44:04 +00:00
Michael Jones
f613ba1a79
Add option to exit when stdin ends (#449)
Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-12-01 23:19:04 +00:00
dependabot[bot]
db287c49f7
Update git-config requirement from 0.7.1 to 0.12.0 in /crates/ignore-files (#448) 2022-11-24 23:25:38 +00:00
dependabot[bot]
e6bde7a48d
Bump axum-core from 0.2.7 to 0.2.9 (#439) 2022-11-24 23:13:36 +00:00
dependabot[bot]
c30fd84c02
Bump mathieudutour/github-tag-action from 6.0 to 6.1 (#435)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-25 12:04:04 +13:00
dependabot[bot]
b25cf1586c
Bump softprops/action-gh-release from cd28b0f5ee8571b76cfdaa62a30d51d752317477 to 1 (#447)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-24 12:35:27 +13:00
dependabot[bot]
46fba794c1
Bump cargo-bins/release-pr from 1 to 2 (#441)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-14 16:12:24 +00:00
dependabot[bot]
d350f91b12
Bump softprops/action-gh-release from 50195ba7f6f93d1ac97ba8332a178e008ad176aa to cd28b0f5ee8571b76cfdaa62a30d51d752317477 (#438)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-13 21:45:59 +13:00
Aarni Koskela
bb44b3d2dc
Readme: fix CI badge URL (#419)
The previous badge was broken. :)
2022-09-18 21:27:03 +00:00
Robert Laverty
ec1cf13a41
Fix zsh autocomplete after using --shell=bash (#417) 2022-09-15 03:17:35 +00:00
Félix Saparelli
6f4c8604e8
Update release versioning 2022-09-07 18:35:00 +12:00
Félix Saparelli
cf80b8395f
Learn some build optimisations from binstall (#416) 2022-09-07 06:00:08 +00:00
github-actions[bot]
c7c9b30636
release: watchexec-cli v1.20.6 (#415)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-09-07 05:30:28 +00:00
github-actions[bot]
695b36e345
release: watchexec v2.0.2 (#414)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-09-07 05:08:20 +00:00
Félix Saparelli
4397aa5c7e
Default release pr version to patch 2022-09-07 16:54:40 +12:00
Félix Saparelli
dd882109f8
Enable OIDC 2022-09-07 16:53:28 +12:00
Félix Saparelli
014e6246ac
Checkout before setting up signing 2022-09-07 16:52:19 +12:00
Félix Saparelli
8b4e5ef71f
Debug gitsign 2022-09-07 16:48:48 +12:00
Félix Saparelli
236fac4c99
Document the absolute crate release order (#413) 2022-09-07 16:40:47 +12:00
Félix Saparelli
b2ea64d436
Use gitsign for release-pr commits (#412) 2022-09-07 16:40:31 +12:00
github-actions[bot]
4ef180f486
release: watchexec-filterer-globset v1.0.1 (#410)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-09-07 04:31:39 +00:00
github-actions[bot]
6642362025
release: watchexec-filterer-tagged v0.1.1 (#411)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-09-07 04:11:47 +00:00
github-actions[bot]
d168ed9676
release: ignore-files v1.0.1 (#407)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-09-07 03:50:18 +00:00
Félix Saparelli
91a12383fd
Use gnu tar on windows CI (#409) 2022-09-07 03:41:34 +00:00
github-actions[bot]
c46187f92d
release: project-origins v1.1.1 (#408)
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-09-07 03:29:23 +00:00
Félix Saparelli
24a3a3c2f2
Deps: update miette and git-config (#406) 2022-09-07 02:52:53 +00:00
Félix Saparelli
669ad6a7bf
Ditch actions-rs (#395) 2022-09-07 02:15:38 +00:00
Félix Saparelli
f7d38206e3
Configure dependabot (#394) 2022-09-07 01:28:15 +00:00
Félix Saparelli
6c40113e9e
Run cargo update (#392) 2022-09-07 13:15:38 +12:00
Félix Saparelli
1042cba39c
Mention library crate changelog 2022-09-07 13:06:04 +12:00
github-actions[bot]
79bcf6838d
release: watchexec v2.0.1 (#390)
Co-authored-by: github-actions <github-actions@github.com>
2022-09-07 00:57:50 +00:00
Félix Saparelli
b2b3bb8fde
Merge check, bors, and main tests 2022-09-07 12:36:55 +12:00
Félix Saparelli
a49b38c9fd
Only include release notes for cli 2022-09-07 12:31:08 +12:00
Félix Saparelli
e9d24688e1
Re-order readmes to prioritise package managers 2022-09-07 12:24:44 +12:00
Félix Saparelli
b2de641f20
Document known packagings 2022-09-07 12:15:40 +12:00
Félix Saparelli
475063c49f
Mention package managers 2022-09-07 11:30:42 +12:00
Félix Saparelli
05458c3452
Undo escaping 2022-09-07 11:29:26 +12:00
Félix Saparelli
174af8665f
Remove bors, use cargo-bins/release-pr 2022-09-07 11:24:53 +12:00
Félix Saparelli
f684db57ee
Update Notify to stable (#384) 2022-09-02 09:12:47 +00:00
github-actions[bot]
ce68338381 release: project-origins v1.1.0 (#385)
This is a release PR for **project-origins** to version **1.1.0**.

Upon merging, you will still need to manually publish the cargo crate.

```
$ cd crates/project-origins
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|
2022-08-24 09:23:52 +00:00
Félix Saparelli
94deab9452 Update binstall metadata (#383) 2022-08-24 07:19:02 +00:00
Félix Saparelli
34184f925a Add some more project types to project-origins (#370) 2022-08-24 07:19:00 +00:00
github-actions[bot]
f866e6f498 release: cli v1.20.5 (#374)
This is a release PR for **cli** to version **1.20.5**.

Upon merging, this will automatically build the CLI and create a GitHub release. You still need to manually publish the cargo crate.

```
$ cd crates/cli
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|

---

_Edit release notes into the section below:_

<!-- do not change or remove this heading -->
### Release notes

- Fix: mimalloc was set as the global allocator in the watchexec library, instead of leaving it to downstreams. (#369)
- Releng: s390x and ppc64le targets were added to be prebuilt. (#373)
2022-08-03 23:10:41 +00:00
Félix Saparelli
bcba352603 Move mimalloc allocator init to CLI (#369) 2022-08-03 13:11:00 +00:00
Dirk Haubenreisser
90c601deeb Add s390x and ppc64le release-cli build artifacts (#373)
Signed-off-by: Dirk Haubenreisser <haubenr@de.ibm.com>

This PR adds artifact builds for platforms s390x and ppc64le to the GH workflow for target 'release-cli'.
Adding these platforms to the list of pre-built release binaries is instrumental in enabling paketo.io buildpacks for s390x and ppc64le.
2022-08-03 11:27:28 +00:00
github-actions[bot]
bb9021eec7 release: cli v1.20.4 (#366)
This is a release PR for **cli** to version **1.20.4**.

Upon merging, this will automatically build the CLI and create a GitHub release. You still need to manually publish the cargo crate.

```
$ cd crates/cli
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|

---

_Edit release notes into the section below:_

<!-- do not change or remove this heading -->
### Release notes

- Remove CLI help tests. They'll be replaced with brand new integration testing that's not obnoxious. (#362, #363, #365)
- Fix the install filenames for first-party RPM packagings, and possibly for DEBs as well. (#292, #364)
2022-07-01 14:13:39 +00:00
Félix Saparelli
ea9f5ff694 Fix release replacements (#365)
bors r+
2022-07-01 13:57:46 +00:00
Félix Saparelli
c0c9193dff Fix bin filename for deb/rpm packaging (#364)
Unsure if deb ever supported that but rpm sure does not.

Fixes #292
2022-07-01 13:39:43 +00:00
Félix Saparelli
f0d64f07df Remove CLI help tests (#363)
Fixes #362

I've been working to remake the tests better / actually valuable but this is a quick "fix" for that issue... in any case help tests would have been gone anyway.
2022-07-01 13:39:43 +00:00
Félix Saparelli
f548fd1956 Fix release notes using multiline output (#361) 2022-06-29 04:52:45 +00:00
github-actions[bot]
f9ec29a60b release: cli v1.20.3 (#359)
This is a release PR for **cli** to version **1.20.3**.

Upon merging, this will automatically build the CLI and create a GitHub release. You still need to manually publish the cargo crate.

```
$ cd crates/cli
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|

---

_Edit release notes into the section below:_

<!-- do not change or remove this heading -->
### Release notes

- Dependencies are set manually for first-party deb and rpm packages, so they should install without error. A note that these are provided on a best-effort basis and not tested; when available you should prefer your distribution's packaging. (#292, #358)

Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-06-29 03:48:50 +00:00
Félix Saparelli
96825a6f5a Automatic release notes for CLI (#360) 2022-06-29 02:21:57 +00:00
Félix Saparelli
e15fa6cd21 Manually set depends for deb and rpm packages (#358)
Should fix #292
2022-06-29 01:28:29 +00:00
github-actions[bot]
64895132aa release: cli v1.20.2 (#357)
This is a release PR for **cli** to version **1.20.2**.

Upon merging, this will automatically build the CLI and create a GitHub release. You still need to manually publish the cargo crate.

```
$ cd crates/cli
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|
2022-06-28 14:15:48 +00:00
Félix Saparelli
1b70a92162 Fix restart (actually issue a stop outcome) (#353)
Fixes #346
2022-06-28 13:50:56 +00:00
Félix Saparelli
1d2dd7b904 Fix env/shell for windows CLI build (#356)
bors r+
2022-06-28 13:40:21 +00:00
Félix Saparelli
94f4dd26c2 Review logging levels: make info more useful (#354)
Fixes #260
2022-06-28 13:40:20 +00:00
Félix Saparelli
5079f45198 Dispatch CLI release job from commit message, not job output (#355) 2022-06-28 13:25:10 +00:00
github-actions[bot]
50e2f0acbf release: cli v1.20.1 (#352)
This is a release PR for **cli** to version **1.20.1**.

Upon merging, this will automatically build the CLI and create a GitHub release. You still need to manually publish the cargo crate.

```
$ cd crates/cli
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|
2022-06-28 12:43:58 +00:00
Félix Saparelli
4970646f1a Fix logging initialisation (#348)
- Fixes #345 (first crash, related to `RUST_LOG` being set)
- Adds multiple warnings when non-standard logging or build options are set
- Fixes the (internal) crate name of the cli so that the log target `watchexec_cli` works
- Fixes the verbosity config to target the actual crate names (snake vs kebab case)
- Fixes a usage error related to tracing's `log` compatibility
2022-06-28 12:21:19 +00:00
Félix Saparelli
3aa128c4ee Fix new instances of clap being way more strict now (#350) 2022-06-28 12:11:29 +00:00
Félix Saparelli
75784667bc Allow non-UTF8 input for paths and extensions (#349)
- Fixes #345 (second crash)
- Proper fix instead of #347
2022-06-28 01:17:17 +00:00
Félix Saparelli
ede4959102
Fix CLI release workflow (#339, #340, #341, #342, #343) 2022-06-24 14:40:57 +12:00
github-actions[bot]
77d4b03398 release: cli v1.20.0 (#337)
This is a release PR for **cli** to version **1.20.0**.

Upon merging, this will automatically build the CLI and create a GitHub release. You still need to manually publish the cargo crate.

```
$ cd crates/cli
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|
2022-06-23 09:57:55 +00:00
github-actions[bot]
d71412cd12 release: filterer-tagged v0.1.0 (#336)
This is a release PR for **filterer-tagged** to version **0.1.0**.

Upon merging, you will still need to manually publish the cargo crate.

```
$ cd crates/filterer/tagged
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|
2022-06-23 09:24:07 +00:00
github-actions[bot]
9c343a6eb2 release: filterer-globset v1.0.0 (#335)
This is a release PR for **filterer-globset** to version **1.0.0**.

Upon merging, you will still need to manually publish the cargo crate.

```
$ cd crates/filterer/globset
$ cargo publish
```

To merge this release, review the changes then say:

| bors r+ p=10 |
|:-:|
2022-06-23 03:33:20 +00:00
Félix Saparelli
69464d373f Fix release PR instructions again (#334)
See edit history of #333
2022-06-23 00:49:43 +00:00
github-actions[bot]
5cf89eb7b4 release: filterer-ignore v1.0.0 (#333)
This is a release PR for **filterer-ignore** to version **1.0.0**.

Upon merging, you will still need to manually publish the cargo crate.

```
$ cd crates/filterer/ignore
$ cargo publish
```

To merge this release, review the changes then say:
| bors r+ p=10 |
|:-:|
2022-06-23 00:40:59 +00:00
Félix Saparelli
6c3cb476a8 Polish up filterer crates for release (#331) 2022-06-22 23:48:46 +00:00
Félix Saparelli
4f808a1002 Remove prefix on tag (ffs) (#330) 2022-06-17 00:47:06 +00:00
github-actions[bot]
c3df998895 release: lib v2.0.0 (#329)
This is a release PR for **lib** to version **2.0.0**.

Upon merging, you will still need to manually publish the cargo crate.

```
$ cd crates/crates/lib
$ cargo publish
```

To merge this release, review the changes then say:

`bors r+ p=10`

Co-authored-by: Félix Saparelli <felix@passcod.name>
2022-06-17 00:29:33 +00:00
Félix Saparelli
0e13595f08 Roll lib changelog on release (#328) 2022-06-17 00:10:10 +00:00
Félix Saparelli
334c704cc4 Add direct link to latest release in README.md (#327) 2022-06-16 23:58:52 +00:00
Félix Saparelli
d79a080ad1 Check readme codeblock (#325) 2022-06-16 23:18:31 +00:00
Félix Saparelli
5c2c80a6e0 Add --delay-run (#324)
The other side of #79
2022-06-16 22:55:25 +00:00
Félix Saparelli
92d05755c9 Add Outcome::Sleep (#323)
One half of #79
2022-06-16 16:07:27 +00:00
Félix Saparelli
adc4a0a576 Add --log-file so logs are easier to collect (#321) 2022-06-16 15:56:59 +00:00
Félix Saparelli
f0e05f9526 Remove obsolete bin/ scripts (#322) 2022-06-16 15:46:32 +00:00
Félix Saparelli
ba86225ad4 Make it possible to pass multiple Commands (library) (#320) 2022-06-16 15:36:08 +00:00
Félix Saparelli
361e5530c5 Fix EOF error in release PR action (#319) 2022-06-16 11:34:58 +00:00
Félix Saparelli
4562c0f2f9 release: ignore-files v1.0.0 (#318)
Co-authored-by: github-actions <github-actions@github.com>
2022-06-16 11:09:16 +00:00
Félix Saparelli
03ee184d41 Import changelog for the library to a file (#316) 2022-06-16 10:48:29 +00:00
Félix Saparelli
e1d1940261 Handle multi-line commit messages in release-tag workflow (#315) 2022-06-16 10:48:28 +00:00
Félix Saparelli
5ee0356e34 Don’t allow invalid UTF-8 arguments (#317) 2022-06-16 10:19:09 +00:00
github-actions[bot]
b133486c66 release: project-origins v1.0.0 (#314)
This is a release PR for **project-origins** to version **1.0.0**.

  Upon merging, you will still need to manually publish the cargo crate.

```
$ cd crates/project-origins
$ cargo publish
```

To merge this release, review the changes then say:

```
bors r+
```
2022-06-16 09:01:22 +00:00
Félix Saparelli
18dea4b6e6
Fix release merge instructions 2022-06-16 20:54:21 +12:00
Félix Saparelli
e98989f099
Almost there 2022-06-16 20:28:07 +12:00
Félix Saparelli
f21b45e6aa
CI fixes
- #308
- https://github.com/github-community/community/discussions/18697
- #309
- #310
- #311
- #312
- #313
2022-06-16 18:27:28 +12:00
Félix Saparelli
35cf63bc85 Split into more crates (#307) 2022-06-15 03:25:05 +00:00
Félix Saparelli
70aa48c59d Upgrade to clap 3 (#304)
This is just the upgrade, including the new option categories. Move to the derive API or more advanced features is left for later.
2022-06-11 08:05:11 +00:00
Félix Saparelli
ae2718ec4d Upgrade to miette 4 (#303) 2022-06-11 07:55:43 +00:00
Félix Saparelli
0e1a6f2c45 Mark Cargo.lock as binary for merging (#305) 2022-06-11 07:38:31 +00:00
Félix Saparelli
446a8d95a7 Replace event queue with a priority queue (#302)
Solves several issues, generally through delivering signals before filesystem events, preventing situations where an overwhelming amount of events makes it impossible to quit.

Does _not_ solve the problem of a queue full of lower-priority events not accepting an urgent message, but that's a rarer issue that's more complicated to overcome.

Changes the Filterer trait: adds Priority to `check_event()`

Makes some events unfilterable (Urgent priority): SIGINT, SIGTERM, and CTRL_C to the main process. These still need to be handled by `on_action` to do anything, but cannot be stopped before reaching that.
2022-06-11 06:43:11 +00:00
Félix Saparelli
62e79fbf7a Add log feature to tracing (#300) 2022-06-07 12:15:17 +00:00
Félix Saparelli
557efab1c4 Document the format of the globset tuple arguments (#298) 2022-06-07 11:46:07 +00:00
Félix Saparelli
e215e2c09f Add folder/file ignore and filter tests to globset (#299) 2022-06-07 11:25:50 +00:00
Félix Saparelli
ec26a99b7d Fix watchexec example in docs found in #295 (#297) 2022-06-07 09:19:16 +00:00
Félix Saparelli
f3e0f0cbda Update readme badge for bors (#289) 2022-05-30 03:19:42 +00:00
Félix Saparelli
84eb92b82e Separate check on main (#288) 2022-05-30 03:07:21 +00:00
Félix Saparelli
3d19ce04e7 Specify full name for bors builds (#287) 2022-05-30 02:59:14 +00:00
Félix Saparelli
d395faeefa Test with MSRV on all platforms (#286) 2022-05-30 02:44:50 +00:00
Félix Saparelli
4be340a4bc Configure bors (#285) 2022-05-30 02:34:22 +00:00
Félix Saparelli
edf023c009 cli: v1.19.0 2022-04-15 15:49:58 +12:00
Félix Saparelli
c58d66bec5 Link manpage from readme 2022-04-15 15:47:44 +12:00
Félix Saparelli
396424112e Add new options to help 2022-04-15 15:45:17 +12:00
Félix Saparelli
3523925c1e Reorganise manpage options into categories 2022-04-15 15:45:17 +12:00
Félix Saparelli
f60a623a44 Add --env and --workdir options 2022-04-15 15:45:17 +12:00
Félix Saparelli
6f3cdac9fd Document --project-origin in man 2022-04-15 15:45:17 +12:00
Félix Saparelli
515d5568b2 Document feature 2022-04-15 15:45:17 +12:00
Félix Saparelli
84aac2243c Add --project-origin override 2022-04-15 15:45:17 +12:00
Félix Saparelli
4a6d4350c5 Always install release tools 2022-04-15 15:45:17 +12:00
Félix Saparelli
00e9b17043 Final change to --debounce default: down to 50ms
Closes #168
2022-04-15 15:45:17 +12:00
Félix Saparelli
d4acb9d719 Add notice for change to $SHELL-as-default in 2.0
Ref #210
2022-04-15 15:45:17 +12:00
Félix Saparelli
009ef2356f Deny 2018 idioms 2022-04-13 22:59:43 +12:00
Félix Saparelli
b374de6097 cli: v1.18.12 2022-04-13 09:22:53 +12:00
Félix Saparelli
6b0f626441 Document project origin behaviour in man 2022-04-13 09:09:13 +12:00
Félix Saparelli
036d9a27e8 Ignore homedir if discovered as origin unless explicitly requested
Fixes #276
2022-04-12 21:59:05 +12:00
Félix Saparelli
98348be8b5 lib: v2.0.0-pre.14 2022-04-04 11:47:36 +12:00
David Calavera
20597802ad
Remove git2 as a dependency (#267)
This dependency cannot be sent between threads.
Instead use git-config which is a pure rust implementation to read git config files.

Uses the builtin values::Path struct to extract ignore paths.
It takes care of all the corner cases with how git interprets paths.
2022-04-04 11:43:30 +12:00
Félix Saparelli
e8d3bb964c cli: v1.18.11 2022-03-28 10:29:14 +13:00
Félix Saparelli
386e01b06f The thing I said should never fail does in fact fail
Fixes #271
2022-03-28 10:14:54 +13:00
Félix Saparelli
57e2ddf9fa cli: v1.18.10 2022-03-27 18:23:06 +13:00
Félix Saparelli
d38428f297 Look up project origin on implicit pwd
This may cause some more issues to crop up while waiting for --project-origin,
but it is the more correct behaviour.

Fixes #270
2022-03-27 16:31:58 +13:00
Félix Saparelli
7baf79e929 cli: v1.18.9 2022-03-18 15:18:58 +13:00
Félix Saparelli
3969264e91 lib: v2.0.0-pre.13 2022-03-18 15:00:02 +13:00
Félix Saparelli
71f2e38ecb Use cargo-install gh task 2022-03-18 14:12:23 +13:00
Félix Saparelli
07bf2523b6 Revert mac to fsevents 2022-03-18 14:08:59 +13:00
Félix Saparelli
572fc53793 cli: v1.18.8 2022-03-16 16:24:19 +13:00
Félix Saparelli
bd3d7a8411 Update man 2022-03-16 16:23:57 +13:00
Félix Saparelli
29dcd418ba lib: v2.0.0-pre.12 2022-03-16 16:10:33 +13:00
Félix Saparelli
e5731abde0 Use exact version reqs for prereleases
I did not realise that Cargo considers -pre.1 and -pre.2 to be
compatible, so the latest Notify prerelease broke all existing
releases, and whenever I release a new lib it may break all CLI
versions if the API changes.
2022-03-16 16:02:29 +13:00
Félix Saparelli
aeb699b658 Show CLI version in debug logs 2022-03-16 00:01:32 +13:00
Félix Saparelli
da22d34274 Fix #262: document env var ordering and other specifics 2022-03-15 23:26:37 +13:00
Félix Saparelli
17edfe663b Add library version to debug log 2022-03-15 23:25:54 +13:00
Félix Saparelli
223d29fa4a Update lockfile 2022-03-14 12:58:02 +13:00
Félix Saparelli
465895ddec Upgrade notify 2022-03-14 12:57:49 +13:00
Félix Saparelli
5e6adabf8d Use kqueue on mac 2022-03-14 12:50:46 +13:00
Félix Saparelli
a3c03b9fac cli: v1.18.7 2022-03-07 21:41:30 +13:00
Félix Saparelli
ce449d85e2 Require test snapshots to change for bin release 2022-03-07 21:39:54 +13:00
Félix Saparelli
9ff5875327 lib: v2.0.0-pre.11 2022-03-07 20:59:47 +13:00
Félix Saparelli
72100217e8 Allow */foo filters to match foo (compat)
This is buggy behaviour that exists in <=1.17, and this patch restores it*

Per gitignore rules, this glob:

    */somefile

Will match:

    foo/somefile
    bar/somefile

But not:

    somefile

However, so far we’ve accepted this. As this is a breaking change, this patch
introduces a hack which lets us accept this behaviour, but clearly marks it as
a compatibility exception. In 2.0, this will be gone.

Fixes #258

* It only restores it on unices, because the behaviour cannot be restored in
  any reasonable manner on Windows. Ah well. It's not for long anyway.
2022-03-07 20:33:29 +13:00
Félix Saparelli
03aef187bd Let --exts and --filter work together
These two options are meant to work together: they are a union of filters
rather than competing. Thus:

    --exts toml,py --filter Gemfile

will match all of:

    foo.toml
    bar.py
    Gemfile

rather than matching no files at all because Gemfile doesn’t have an extension.

Fixes #259
2022-02-22 02:26:46 +13:00
Félix Saparelli
4af6865ef0 Add broken tests for regressions
See #258 and #259
2022-02-12 22:39:40 +13:00
Félix Saparelli
93da1ce780 Carry help through elevated errors properly
This might show the help text twice actually. Let's see...
2022-02-12 20:05:19 +13:00
Félix Saparelli
b77da446d8
Split known create errors to get proper help text 2022-02-12 19:49:02 +13:00
Félix Saparelli
3b64a41d80
Split fs watcher errors into their own enum 2022-02-12 19:37:24 +13:00
Félix Saparelli
56380154d3
Clippy 2022-02-11 00:52:35 +13:00
Félix Saparelli
797a7fc708 Setup cargo-release 2022-02-10 01:40:32 +13:00
Félix Saparelli
e5bb99a60b
Add shout-outs to friends of watchexec 2022-02-08 14:59:41 +13:00
Félix Saparelli
bd7a933906 cli: v1.18.6 2022-02-07 17:34:26 +13:00
Félix Saparelli
bdcbe5ca3c lib: v2.0.0-pre.10 2022-02-07 17:18:21 +13:00
Félix Saparelli
55c02567e0 Bump lib version in cli if in pathed mode 2022-02-07 17:18:09 +13:00
Félix Saparelli
96a6a6e9d9 Revert ignoring .git only when Git is discovered
And so on from other VCS. This causes confusion (ref #255) when watching
from outside a git repo, where the VCS type isn’t detected so the .git
folder is included in the watchset. While slightly incorrect, it’s more
expected that these folders be ignored regardless, and there’s always
--no-default-ignore to avoid those ignores if needed.
2022-02-07 17:15:27 +13:00
Félix Saparelli
94b21c2da3 Load global ignores even when there’s no discovered VCS
Fixes #255
2022-02-07 17:15:27 +13:00
Félix Saparelli
965030b47b Add help text to FsWatcherCreate on Linux
See #251
2022-02-07 17:15:27 +13:00
Félix Saparelli
e40744f033 Elevate FsWatcherCreate errors to critical
See #251
2022-02-07 17:15:27 +13:00
Félix Saparelli
c6f230a168 Pretty-print runtime errors 2022-02-07 17:15:27 +13:00
Félix Saparelli
427e4a0d08 Change on_error to let the handler raise a CriticalError 2022-02-07 17:15:27 +13:00
Félix Saparelli
8ebcf083b8 Start work on 1.18.6 2022-01-31 19:15:30 +13:00
Félix Saparelli
a890b0c916 Deduplicate paths in summarise_events_to_env
Fixes #253
2022-01-31 02:14:28 +13:00
Félix Saparelli
ba64fb3c38 cli: v1.18.5 2022-01-31 00:49:45 +13:00
Félix Saparelli
ada9888fdf Use lib pre.9 2022-01-31 00:45:47 +13:00
Félix Saparelli
bc50c198b3 lib: v2.0.0-pre.9 2022-01-31 00:35:27 +13:00
Félix Saparelli
d7a305fc4c
Only run one outcome worker at a time 2022-01-31 00:05:43 +13:00
Félix Saparelli
2a9ee4de0b
Add detailed logging to process holder 2022-01-31 00:05:10 +13:00
Félix Saparelli
9a736c5eb9
Use newly &self Supervisor.wait to drop wait loop 2022-01-31 00:04:56 +13:00
Félix Saparelli
995d38078e
Use a tokio::watch in the supervisor to avoid races 2022-01-31 00:00:51 +13:00
Félix Saparelli
3e942c4d19
Double-check the ongoing atomic 2022-01-30 22:29:33 +13:00
Félix Saparelli
fdf4dcd13e
Provide opportunities for process holder to be queried concurrently to a wait() 2022-01-29 04:14:51 +13:00
Félix Saparelli
12ef0411f8
Don't log an error from outcome worker on graceful exit 2022-01-29 04:13:28 +13:00
Félix Saparelli
328700dd72
Stop holding supervisor handle 2022-01-29 04:13:08 +13:00
Félix Saparelli
c74eab5e91 Split up action in mods 2022-01-29 02:39:06 +13:00
Félix Saparelli
cc0c0be45a
Save a pointer by passing around an Arc<[T]> instead of Arc<Vec<T>> 2022-01-29 02:17:23 +13:00
Félix Saparelli
22f081a47b Spawn outcome apply separately from action loop 2022-01-29 02:08:34 +13:00
Félix Saparelli
10b31a0269 Wrap Supervisor in a RwLock 2022-01-29 02:08:34 +13:00
Félix Saparelli
a651d149b0 Replace AtomicTakes with HandleLocks in action 2022-01-29 02:08:34 +13:00
Félix Saparelli
b8ffa0c38a Formatting 2022-01-29 02:08:34 +13:00
Félix Saparelli
9845d48464 Add HandlerLock wrapper
This lets a Handler be shared (via a Tokio mutex)
2022-01-29 02:08:34 +13:00
Félix Saparelli
d9006a240d Remove keep_* config methods 2022-01-29 02:08:25 +13:00
Félix Saparelli
c444895593
Move swaplock to the root 2022-01-28 23:32:54 +13:00
Félix Saparelli
18a2b204e2 Hide io::errors when "waiting on process group" 2022-01-27 10:48:24 +13:00
Félix Saparelli
c79d2726ba Disable Windows ARM64 builds
They're consistently failing, needs debugging.
2022-01-26 10:25:33 +13:00
Félix Saparelli
7ccd3c92e0 cli: v1.18.4 2022-01-26 10:04:09 +13:00
Félix Saparelli
e6d80558f8 Use lib pre.8 2022-01-26 10:03:53 +13:00
Félix Saparelli
fc40336db7 lib: v2.0.0-pre.8 2022-01-26 10:01:15 +13:00
Félix Saparelli
a6c997f470
globset: Always pass non-path events (#248) 2022-01-26 09:59:53 +13:00
Félix Saparelli
366277c6c1 Add issue templates 2022-01-26 08:10:14 +13:00
Félix Saparelli
96a41e1b4c
Remove unused Result<> return 2022-01-26 03:24:15 +13:00
Félix Saparelli
77ee59a9e3 cli: v1.18.3 2022-01-26 02:36:27 +13:00
Félix Saparelli
d067b5a5de Use lib pre.7 2022-01-26 02:36:01 +13:00
Félix Saparelli
f6a3b76d92 lib: v2.0.0-pre.7 2022-01-26 02:33:28 +13:00
Félix Saparelli
a5e756df7c Remove unused check_glob utility 2022-01-26 01:58:01 +13:00
Félix Saparelli
59d8388d7e globset: allow multipath events through if any path passes the filter 2022-01-26 01:51:48 +13:00
Félix Saparelli
42d64d0a6d
Fail all folders on extension check 2022-01-26 01:18:32 +13:00
Félix Saparelli
6cebaf204f Restore shell default to sh on unix (#240) 2022-01-26 01:06:18 +13:00
Félix Saparelli
94b4dffc02
Merge pull request #243 from abitrolly/patch-1
cli/README.md: Need glob pattern to ignore subdirs
2022-01-25 23:23:51 +13:00
Anatoli Babenia
8c8cdf8e82
cli/README.md: Need glob pattern to ignore subdirs 2022-01-25 13:21:41 +03:00
Félix Saparelli
5bdd87dbf6
Merge pull request #242 from aeolyus/main
Fix spawing -> spawning typo
2022-01-25 22:40:48 +13:00
Richard Huang
67452152c2
Fix spawing -> spawning typo 2022-01-24 21:52:16 -08:00
Félix Saparelli
a51979b54e cli: v1.18.2 2022-01-24 20:53:20 +13:00
Félix Saparelli
9b866ad9e8
Remove default signal from clap, leave it for parse
Fixes #239
2022-01-24 20:52:29 +13:00
Félix Saparelli
b8b2c2ca70 Fix citation versioning 2022-01-23 21:55:09 +13:00
Félix Saparelli
b5c16f291e cli: v1.18.1 2022-01-23 21:53:29 +13:00
Félix Saparelli
4e7e75e1d5 Strip leading period if present from -e exts (#236) 2022-01-23 21:53:12 +13:00
Félix Saparelli
c79a3bd12c Upgraded fmt 2022-01-22 14:00:42 +13:00
Félix Saparelli
187c696d8f Fix release workflow 2022-01-19 00:25:45 +13:00
Félix Saparelli
7effd1a61e cli: v1.18.0 2022-01-19 00:05:03 +13:00
Félix Saparelli
7d60b1689e Use lib pre.6 2022-01-19 00:04:41 +13:00
Félix Saparelli
a3fd743786 lib: v2.0.0-pre.6 2022-01-19 00:00:46 +13:00
Félix Saparelli
631c328a55 Use version for lib dependency 2022-01-18 23:56:10 +13:00
Félix Saparelli
c58333496e Fail files with no extension on extension check
Instead of passing them, I don't know what I was thinking
2022-01-18 23:56:10 +13:00
Félix Saparelli
c367288e38
Add desktop notification to cli feature list 2022-01-18 23:29:28 +13:00
Félix Saparelli
c91ab99ac3 lib: v2.0.0-pre.5 2022-01-18 23:27:30 +13:00
Félix Saparelli
9f67ec35b1 More OsSplit windows fixing 2022-01-18 23:14:08 +13:00
Félix Saparelli
881e8f4047 More OsSplit testing 2022-01-18 22:53:24 +13:00
Félix Saparelli
0334b9c9fa Bump msrv in ghaction too 2022-01-18 22:42:19 +13:00
Félix Saparelli
7f092ca4b7 Further windows fix to OsSplit 2022-01-18 22:39:55 +13:00
Félix Saparelli
783da395bb Fix lib readme 2022-01-18 22:38:45 +13:00
Félix Saparelli
8748c062c4 Update deps 2022-01-18 22:21:21 +13:00
Félix Saparelli
fd474c7e38
Fix and add tests for OsSplit 2022-01-18 22:10:25 +13:00
Félix Saparelli
044a8244f9
Almost forgot windows... 2022-01-16 20:17:32 +13:00
Félix Saparelli
1d64cac11f Formattting 2022-01-16 20:08:21 +13:00
Félix Saparelli
8bd48c6290
Don't fail if we can't canonicalize an event path
e.g. for deletes
2022-01-16 20:06:44 +13:00
Félix Saparelli
4737937350
Remove generic tagged io error enum variant 2022-01-16 20:01:14 +13:00
Félix Saparelli
e2f27a1b01
Add context to last runtime io error
and remove generic runtime io error enum variant
2022-01-16 20:00:07 +13:00
Félix Saparelli
2ea62aec6a
Add context to last critical io error
and remove generic critical io error enum variant
2022-01-16 19:55:57 +13:00
Félix Saparelli
bc0fe6be70
Add context to io errors 2022-01-16 19:46:52 +13:00
Félix Saparelli
93a961abf6
Clippy 2022-01-16 19:16:53 +13:00
Félix Saparelli
4085fc522c
Add SpecificIoError trait support for tagged error 2022-01-16 19:16:07 +13:00
Félix Saparelli
44a44a50d1
Move TaggedFiltererError into main error mod 2022-01-16 19:14:31 +13:00
Félix Saparelli
b29b3bf1e0
Add helper trait to add context to io errors 2022-01-16 19:09:50 +13:00
Félix Saparelli
c821faf383
Split error mod and split generic/specific io errors 2022-01-16 18:57:40 +13:00
Félix Saparelli
6a925fc392
Canonicalise origin and workdir 2022-01-16 17:42:49 +13:00
Félix Saparelli
2a1a386208
Rename (with compat alias) --no-ignore to --no-project-ignore 2022-01-16 17:34:06 +13:00
Félix Saparelli
26e0b60b7b lib: v2.0.0-pre.4 2022-01-16 16:55:55 +13:00
Félix Saparelli
10b71847da
Add support for Pijul and Subversion 2022-01-16 16:13:05 +13:00
Félix Saparelli
9327f8c293
Only ignore .git if we're in a git project, and so on 2022-01-16 16:12:50 +13:00
Félix Saparelli
2682dde41d
Handle errors from ignore file discovery 2022-01-16 16:11:17 +13:00
Félix Saparelli
3d32f2d24b
Hook up new globset signature 2022-01-16 16:09:53 +13:00
Félix Saparelli
561a34d90e
Drop globset::list_from_ignore_file 2022-01-16 15:21:10 +13:00
Félix Saparelli
817f4c2caf
Drop ignorefile tests from globset and tagged now that they're done via common IgnoreFilterer 2022-01-16 15:20:14 +13:00
Félix Saparelli
b8d9ad728e
Integrate IgnoreFilterer into GlobsetFilterer 2022-01-16 15:18:15 +13:00
Félix Saparelli
d6dfb87063
Integrate IgnoreFilterer into TaggedFilterer 2022-01-16 14:51:35 +13:00
Félix Saparelli
8219e577d0
Reduce swaplock hold for tagged compile globs 2022-01-16 14:51:11 +13:00
Félix Saparelli
f073eeb344
Add ::empty() and Clone to IgnoreFilterer 2022-01-16 14:49:14 +13:00
Félix Saparelli
0618eb45fc
Hold swaplock for less in change() 2022-01-16 14:48:29 +13:00
Félix Saparelli
ed6df57aa0
Ignore .git, .hg, etc while looking for ignore files 2022-01-16 02:36:22 +13:00
Félix Saparelli
1c388d6b2d
Logic inversione 2022-01-16 02:03:02 +13:00
Félix Saparelli
0c2b4848be
Be more efficient, and more importantly, correct, when ignoring ignores 2022-01-16 01:44:53 +13:00
Félix Saparelli
e40b426930 Clippy 2022-01-16 01:44:11 +13:00
Félix Saparelli
63f9eedfec Clippy 2022-01-16 00:58:30 +13:00
Félix Saparelli
e9bc4fcf1b
Add ignore support to ignore finder 2022-01-16 00:57:29 +13:00
Félix Saparelli
5bdc1b491f
Add tests for ignore filterer 2022-01-15 23:46:41 +13:00
Félix Saparelli
58d47f78e1
Add more filtering logging and spans 2022-01-15 23:46:06 +13:00
Félix Saparelli
1ce238949e
Reduce verbosity of tracing span output 2022-01-15 16:40:01 +13:00
Félix Saparelli
f0435eeaf3
Document envvars read/written and files read 2022-01-15 15:58:30 +13:00
Félix Saparelli
431ef710ee
Expose paths::PATH_SEPARATOR 2022-01-15 15:58:11 +13:00
Félix Saparelli
97070c815c
Add --no-global-ignore to completion and man 2022-01-15 15:57:40 +13:00
Félix Saparelli
d70abeac6f
Add --notify to man 2022-01-15 15:26:57 +13:00
Félix Saparelli
78b8acf6cc
Add --print-events to man 2022-01-15 15:26:35 +13:00
Félix Saparelli
afb61712a6
Remove --no-shell from completion 2022-01-15 15:26:12 +13:00
Félix Saparelli
be5782432a
Add --notify and --print-events to completion 2022-01-15 15:25:47 +13:00
Félix Saparelli
da6f9cfc55
Attempt to amend zsh completions for multi-v verbose 2022-01-15 15:20:27 +13:00
Félix Saparelli
775b0ac81c
Add -vv+ usage to manpage 2022-01-15 15:19:16 +13:00
Félix Saparelli
5e285074bd
Add ignore test helper 2022-01-15 15:15:40 +13:00
Félix Saparelli
00def9e19a
Use pretty logging at high -vvvv 2022-01-15 15:14:25 +13:00
Félix Saparelli
2c840f4a2d
Reorganise the ignore mod for brevity 2022-01-15 15:12:32 +13:00
Félix Saparelli
5f43a6eae8
Move profile to where it's effective 2022-01-15 14:34:37 +13:00
Félix Saparelli
0e12030e3b
Perform fat LTO in release 2022-01-13 01:02:37 +13:00
Félix Saparelli
23a3482361
Include some debug info in release, but compress it for prebuilds 2022-01-13 01:02:22 +13:00
Félix Saparelli
0936a68355
Split ignore mod further 2022-01-12 00:59:39 +13:00
Félix Saparelli
ca4d4900a9
Add filterer and parser for ignore files 2022-01-12 00:54:21 +13:00
Félix Saparelli
31cbe69051
Configure miette docsurl globally per error type 2022-01-12 00:53:41 +13:00
Félix Saparelli
9527202c51
Add RuntimeError::Set of related errors 2022-01-12 00:52:39 +13:00
Félix Saparelli
6a541e5f27
Move ignore_files one level deeper
in preparation for new ignore functionality
2022-01-10 20:47:06 +13:00
Félix Saparelli
1c99546071
Add lots of logging to ignore file discovery 2022-01-01 01:42:39 +13:00
Félix Saparelli
163bcc3022
Adjust display of errors and exit notify 2022-01-01 01:33:52 +13:00
Félix Saparelli
7fa657fc48
Update cli readme 2021-12-29 22:06:50 +13:00
Félix Saparelli
7fa80f8715 lib: v2.0.0-pre.3 2021-12-29 21:49:07 +13:00
Félix Saparelli
d0bb14f39c
Maintain the naming of the env vars 2021-12-29 21:36:42 +13:00
Félix Saparelli
d39ffed51e lib: v2.0.0-pre.2 2021-12-29 21:29:30 +13:00
Félix Saparelli
185676cc1e
Fix doctests 2021-12-29 21:21:07 +13:00
Félix Saparelli
9ed3645088
Add environment vars support 2021-12-29 20:55:09 +13:00
Félix Saparelli
53d9a65e5f
Change env summarise to return &str as keys for ease of use 2021-12-29 20:52:17 +13:00
Félix Saparelli
ed72c4998e
Add access to events for {Pre,Post}Spawn
Also make access to events read-only (via Arc) for Action
2021-12-29 20:40:12 +13:00
Félix Saparelli
4dbb924977
Add default ignores 2021-12-29 20:14:29 +13:00
Félix Saparelli
b5da4e31ba
Add -h testing 2021-12-29 19:56:02 +13:00
Félix Saparelli
736f7f1270
Reorder items in help output
Apparently clap goes by the internal option name, not the display :(
2021-12-29 19:36:40 +13:00
Félix Saparelli
a00912f17f
Implement --no-meta for default filterer 2021-12-29 19:29:45 +13:00
Félix Saparelli
e2f6fe147a
Support no-{global,project,vcs}-ignore 2021-12-29 19:19:43 +13:00
Félix Saparelli
be4b184cd0
Revise --no-ignore and --no-vcs-ignore help text for clarity and conciseness 2021-12-29 19:10:17 +13:00
Félix Saparelli
b300be081f
Fix help test files 2021-12-29 19:00:54 +13:00
Félix Saparelli
ea3f09408f
Fix new option name 2021-12-29 18:53:47 +13:00
Félix Saparelli
ffcdf17914
Implement tagged's --no-meta 2021-12-29 18:51:51 +13:00
Félix Saparelli
c45840b657
Load global and -F filter files 2021-12-29 18:39:31 +13:00
Félix Saparelli
db322f1228 Fix --help test 2021-12-24 19:14:57 +13:00
Félix Saparelli
75f14ba3d6
Fix OsSplit in windows 2021-12-24 19:07:01 +13:00
Félix Saparelli
08a1e7cc67
Hook up --notify 2021-12-24 19:06:01 +13:00
Félix Saparelli
6f8049dd93
Hook up --ignore, --filter, --exts properly 2021-12-24 18:51:35 +13:00
Félix Saparelli
6cd2252b6c
Modify options available if tagged filterer enabled 2021-12-24 03:56:03 +13:00
Félix Saparelli
bb212b413f
Process --no-process-group 2021-12-24 03:47:15 +13:00
Félix Saparelli
ec49185488
Split tagged and globset filterer in cli 2021-12-24 03:37:51 +13:00
Félix Saparelli
3bfbcaaa2f
Add test logging and dev-console debugging hints 2021-12-24 02:48:21 +13:00
Félix Saparelli
3f94c3c088
Update project status in Contributing 2021-12-24 02:47:55 +13:00
Félix Saparelli
d64135c2c0
Upgrade tracing-subscriber to 0.3 2021-12-24 02:35:26 +13:00
Félix Saparelli
c3094eaff8
Upgrade to tokio-console 0.1 2021-12-24 02:28:21 +13:00
Félix Saparelli
0f51a6e794
Test filter files 2021-12-24 02:21:45 +13:00
Félix Saparelli
26254f7022
Add support for "tagged filter files" 2021-12-24 02:20:56 +13:00
Félix Saparelli
8f9492a7bc
Fix filtering when there's both a Glob and a NotGlob match 2021-12-24 02:19:58 +13:00
Félix Saparelli
f19dbf945d
Express process end exceptions as hex in filters 2021-12-23 00:32:56 +13:00
Félix Saparelli
9d04143202 lib: v2.0.0-pre.1 2021-12-21 18:19:16 +13:00
Félix Saparelli
401b84db8a Clippy 2021-12-21 18:15:47 +13:00
Félix Saparelli
d14e7ff41b
Implement process completion signal matching 2021-12-21 18:05:52 +13:00
Félix Saparelli
8a7699cf2f
Add process completion signal tests 2021-12-21 18:01:51 +13:00
Félix Saparelli
e4d669e230
Implement most of process completion matching
(except ExitSignal)
2021-12-21 17:56:14 +13:00
Félix Saparelli
e7de00edfe
Change process completion auto op to glob 2021-12-21 17:49:24 +13:00
Félix Saparelli
5856f976db
Add tests for process completion 2021-12-21 17:49:02 +13:00
Félix Saparelli
4323a28852
Implement signal matching 2021-12-21 16:38:57 +13:00
Félix Saparelli
ff8b019245
Add tests for signal matchers 2021-12-21 16:29:36 +13:00
Félix Saparelli
8a25ea95af
Add sig alias for signal parsed tag 2021-12-21 16:19:35 +13:00
Félix Saparelli
962c7cd6d6
Test pids 2021-12-21 16:14:58 +13:00
Félix Saparelli
02534defd6
Test feks 2021-12-18 12:53:33 +13:00
Félix Saparelli
946c52b513
Document op and matcher mappings 2021-12-18 12:44:59 +13:00
Félix Saparelli
159a8a4b2e
Change auto op for FEKs to glob 2021-12-18 12:44:40 +13:00
Félix Saparelli
ba9d9939d9
Add more aliases for matcher 2021-12-18 12:44:18 +13:00
Félix Saparelli
5ebceddc3e
Start testing nonpaths 2021-12-17 23:29:02 +13:00
Félix Saparelli
d38a78631f
Test negation in tagged parser 2021-12-17 23:19:36 +13:00
Félix Saparelli
debded9c0e
Add stability note to tagged filterer 2021-12-17 23:19:16 +13:00
Félix Saparelli
d6b5dd5ae0
Test parser 2021-12-17 03:57:25 +13:00
Félix Saparelli
b59acaa9cf
Start testing parser and nonpaths 2021-12-15 07:24:13 +13:00
Félix Saparelli
e4cd1d7379
Test ignorefiles on globset, bugs and all 2021-12-15 06:39:54 +13:00
Félix Saparelli
1af3a1c849
Extract common test helpers 2021-12-15 06:33:55 +13:00
Félix Saparelli
23925db51e
Test scopes 2021-12-15 06:01:40 +13:00
Félix Saparelli
015447e433
Fix whitelisting in pathed globs 2021-12-15 04:57:17 +13:00
Félix Saparelli
a4c863baf8
Test ignore files with folders and globs 2021-12-14 01:47:34 +13:00
Félix Saparelli
f8250f47de
Suppress supervisor.handle warning 2021-12-10 00:53:18 +13:00
Félix Saparelli
289f76ae6c
Test outcome resolving 2021-12-08 22:07:58 +13:00
Félix Saparelli
f229456f88
Specify and test sorting behaviour 2021-12-08 21:43:10 +13:00
Félix Saparelli
89fd99a683
Add multipaths tests 2021-12-08 21:25:03 +13:00
Félix Saparelli
e9275702d1
Fix common_path stemming and start testing summarise_events_to_env 2021-12-07 01:14:19 +13:00
Félix Saparelli
f304774a4f
Mention windows 11 in manifest 2021-12-07 00:50:56 +13:00
Félix Saparelli
a3751519e8
Re-export notify event types 2021-12-07 00:50:33 +13:00
Félix Saparelli
41869af688
Simplify summarise_events_to_env signature (and usage) 2021-12-07 00:49:14 +13:00
Félix Saparelli
0826d531af
Test scoped path glob filtering 2021-12-02 21:33:48 +13:00
Félix Saparelli
ce3f2a3cbd
Fix tests/document remaining differences from globset 2021-12-01 01:45:19 +13:00
Félix Saparelli
2d633d9177
Use proper ignore API so path globs match correctly
This notably fixes the v1 "confusing" behaviour when matching folders,
where the expectation is for any of:

folder
folder/
/folder

to match the folder and all paths below it, but v1 would only do this
when *both* of these were added:

**/folder
**/folder/**

Which is very verbose and has caught literally everyone who's ever tried
to do this kinda thing.

The old behaviour is preserved in the globset filterer, for
compatibility, as there are other small behavioural changes that this
affects, even though the new behaviour in the tagged filterer is
arguably the most correct and the old is a bug.
2021-12-01 01:41:07 +13:00
Félix Saparelli
1ff3cbf455
Add lots more logging to add_filter path 2021-11-30 02:11:50 +13:00
Félix Saparelli
f3bc5fa6d3
Test globset properly
All paths are canonicalised at entrance, so testing should reflect that
2021-11-30 01:42:00 +13:00
Félix Saparelli
4367380a7c
Ignore failing tests 2021-11-28 02:28:14 +13:00
Félix Saparelli
269894e1b0
Add tests for tagged (paths only)
- v1 buggy tests don't pass
2021-11-28 02:22:07 +13:00
Félix Saparelli
0efdf8ea16
Pull globset tests from v1 2021-11-22 22:22:39 +13:00
Félix Saparelli
a46cfba1a2
Pre-add support for logical grouping of options in help
Will work once we switch to clap3
2021-11-22 21:24:59 +13:00
Félix Saparelli
a12a83cea2
Fix mistaken assumption in ProcessEnd::from(unix) 2021-10-28 01:03:54 +13:00
Félix Saparelli
745e3baa71
Span tracing in globset filterer check 2021-10-28 01:03:24 +13:00
Félix Saparelli
631b492064
Span tracing in tagged filterer check 2021-10-28 01:01:35 +13:00
Félix Saparelli
afd9677441 Add manual trigger to audit and check 2021-10-26 21:14:47 +13:00
Félix Saparelli
b13c23c576 Use latest stable toolchain in audit 2021-10-26 21:13:57 +13:00
Félix Saparelli
6d23339dea
Yes but why rely on bits 2021-10-22 06:04:11 +13:00
Félix Saparelli
5c012c5b0c
Fix nonzeros not being bit-opable (windows) 2021-10-22 05:55:28 +13:00
Félix Saparelli
963cd68cc4 Fix cli for ProcessEnd 2021-10-22 05:48:37 +13:00
Félix Saparelli
470cdd698b
Replace ExitStatus with our own type in Event 2021-10-22 05:38:48 +13:00
Félix Saparelli
6671863f2f
Replace std FileType with our own (serde-able) enum 2021-10-20 01:18:43 +13:00
Félix Saparelli
401437784d
Opt in to 2021 ed 2021-10-20 01:00:21 +13:00
Félix Saparelli
be37349b90
Bump msrv to 1.56.0 (2021 ed) 2021-10-20 00:48:47 +13:00
Félix Saparelli
c84e14825d
Opt into resolver=2 2021-10-20 00:47:41 +13:00
Félix Saparelli
c758675728
Prep for folder tests 2021-10-20 00:47:22 +13:00
Félix Saparelli
110e1d4c96
Start a test harness for globset 2021-10-17 17:11:50 +13:00
Félix Saparelli
34d7c5ee9c
Make globset easier to create (less generics) 2021-10-17 17:11:29 +13:00
Félix Saparelli
f9cbb11258
Just reset the MSRV to latest stable (1.55), might as well 2021-10-17 04:12:04 +13:00
Félix Saparelli
9d14ad7166
Document that == and != are case-insensitive 2021-10-17 04:03:18 +13:00
Félix Saparelli
96de715cf7
Fix cli test on windows?
This won't matter for long
2021-10-17 03:59:22 +13:00
Félix Saparelli
beb87bdbb0
Update MSRV to 1.45
we can do it, it's a breaking...
also, Tokio requirement
2021-10-17 03:58:22 +13:00
Félix Saparelli
489fb612c7
Tagged docs typo 2021-10-17 03:54:48 +13:00
Félix Saparelli
1eb10074ef
Markdown typo 2021-10-17 03:53:05 +13:00
Félix Saparelli
43353fecb2
Fix for windows (unmarked unix-only import) 2021-10-17 03:48:32 +13:00
Félix Saparelli
0b60f5edb5 lib: v2.0.0-pre.0 2021-10-17 03:41:13 +13:00
Félix Saparelli
a79de88c58 Merge branch 'main' into tokio 2021-10-17 03:39:19 +13:00
Félix Saparelli
5d57621512
Write readme for 2.0.0-pre.0 2021-10-17 03:37:12 +13:00
Félix Saparelli
70b1a3cd7b
Fix doc links 2021-10-17 03:06:08 +13:00
Félix Saparelli
60fa09182d
Docs: watchexec 2021-10-17 03:01:55 +13:00
Félix Saparelli
fae4fbf9a0
Docs: error 2021-10-17 02:32:43 +13:00
Félix Saparelli
17b83fda08
Docs: command 2021-10-17 02:24:36 +13:00
Félix Saparelli
fcf6a2154a
Add notes to refer to more precise docs on the RuntimeConfig 2021-10-17 01:22:55 +13:00
Félix Saparelli
88dfc0d664
Add note about logging and error handling to main lib doc 2021-10-17 01:16:41 +13:00
Félix Saparelli
1522aaf409
Docs: action 2021-10-17 01:12:04 +13:00
Félix Saparelli
d43165494f
Docs: event 2021-10-16 23:47:00 +13:00
Félix Saparelli
47fc2f30a8
Docs: SubSignal 2021-10-16 23:31:00 +13:00
Félix Saparelli
0ad0845018
Docs: filter 2021-10-16 23:14:57 +13:00
Félix Saparelli
8b8b9674aa
Docs: fs 2021-10-16 20:02:17 +13:00
Félix Saparelli
423caaef2a
Docs: ProjectType 2021-10-16 19:56:38 +13:00
Félix Saparelli
e62f313533
Eliminate eyre from doctests too 2021-10-16 19:08:35 +13:00
Félix Saparelli
2225b6d097
Reduce deps featureset 2021-10-16 17:27:21 +13:00
Félix Saparelli
17310ee4b5
Docs: modules 2021-10-16 17:13:32 +13:00
Félix Saparelli
e06f615531
Implement basic check_glob() 2021-10-16 17:09:21 +13:00
Félix Saparelli
f58e97a62f
Fix globset ignorefile support (wrong field) 2021-10-16 17:01:27 +13:00
Félix Saparelli
5d2f2fcf62
Support the COMMON_PATH variant of the event summariser 2021-10-16 16:54:48 +13:00
Félix Saparelli
30abed3fb2
Add function to import an ignore file in globset filterer format 2021-10-16 16:45:03 +13:00
Félix Saparelli
ebabef9eed
Support extensions in globset filterer 2021-10-16 16:37:29 +13:00
Félix Saparelli
19b27959ed
Add globset filterer 2021-10-16 16:26:29 +13:00
Félix Saparelli
b2f4d0f244 Port method to summarise events from old source 2021-10-16 15:18:42 +13:00
Félix Saparelli
083c1e2f52 Move common_prefix to its own mod 2021-10-16 13:55:20 +13:00
Félix Saparelli
e577b040b9
Handle signalling to sub process on non-unix 2021-10-16 01:21:52 +13:00
Félix Saparelli
92513a4dc3
Add SubSignal type for sending signals to subprocesses 2021-10-16 01:14:17 +13:00
Félix Saparelli
86882e8d27
Add From<&str> for WatchedPath 2021-10-16 01:13:39 +13:00
Félix Saparelli
75243bfdad
Rename Signal to MainSignal
in preparation for another signal type
2021-10-16 01:13:16 +13:00
Félix Saparelli
55e4e1dc58 Review TODOs in the code 2021-10-15 23:00:50 +13:00
Félix Saparelli
b780345e8b Use mimalloc for musl builds 2021-10-15 14:27:32 +13:00
Félix Saparelli
948388b019 Reduce featureset of dependencies (tokio,git2) 2021-10-15 14:27:17 +13:00
Félix Saparelli
8f61ac31da
Prep fs pathset for future 2021-10-15 01:38:21 +13:00
Félix Saparelli
14b0364135
Get filetype filters actually working 2021-10-14 01:26:15 +13:00
Félix Saparelli
ae6af17aea
Find and load all ignores for watchexec cli 2021-10-14 00:38:56 +13:00
Félix Saparelli
87b6729ab7
Add utility ProjectType::{is_vcs,is_soft} and project::common_prefix fns 2021-10-14 00:14:31 +13:00
Félix Saparelli
f24e95504b
Fix panic in tagged filtering 2021-10-14 00:13:48 +13:00
Félix Saparelli
c6336cdf3c
Add filetype matcher 2021-10-13 04:06:55 +13:00
Félix Saparelli
fb4f136c0d
Match path globs 2021-10-13 04:06:39 +13:00
Félix Saparelli
758ac2dc89
Change panic into a todo 2021-10-13 01:51:09 +13:00
Félix Saparelli
fd2edbf11c
Compile gitignore patterns 2021-10-13 01:49:38 +13:00
Félix Saparelli
c9da2c133a
Harmonise root/origin 2021-10-13 01:49:11 +13:00
Félix Saparelli
f16ba2dff1
Redo and sketch new filter adding process 2021-10-13 00:48:42 +13:00
Félix Saparelli
cd7d5f1fcb
Draft and plan ignore loading 2021-10-11 23:34:14 +13:00
Félix Saparelli
3c65aee839
Find project types 2021-10-10 23:35:27 +13:00
Félix Saparelli
1dff1f5644
Find project origins more efficiently 2021-10-10 23:35:11 +13:00
Félix Saparelli
59fe74656a
Stop using eyre even in examples 2021-10-10 21:04:40 +13:00
Félix Saparelli
7af0339871 Detect project origins (if any) 2021-10-10 17:56:49 +13:00
Félix Saparelli
3219be53f5
Add support for bazaar global ignores 2021-10-10 16:06:56 +13:00
Félix Saparelli
65b042ec8f
Discover ignore files for path and for user/env 2021-10-10 16:03:05 +13:00
Félix Saparelli
8bc58ba6b5
Start sketching gitignore support 2021-10-09 18:45:32 +13:00
Félix Saparelli
a1fce1b06e
Replace eyre with miette
and upgrade it to 3.2.0
2021-10-09 18:43:51 +13:00
Félix Saparelli
323f2d29ee
Split off filter errors into their own type(s) 2021-10-09 18:41:45 +13:00
Félix Saparelli
3dff065f4b
Add error variants for external custom errors 2021-10-09 18:38:37 +13:00
Félix Saparelli
81bee9513d
Remove derive-builder
* the InitConfigBuilder non-miette error goes away
* creating an InitConfig is no longer faillible for no reason
* the "builder" style is consistent between the two config structs
2021-10-09 18:37:59 +13:00
Félix Saparelli
07878f8357
Implement path filtering 2021-09-30 04:03:46 +13:00
Félix Saparelli
288ce9d2f4
== and != perform case-insensitive comparisons 2021-09-30 02:34:27 +13:00
Félix Saparelli
d6b7175bb1 Formattting 2021-09-29 23:43:39 +13:00
Félix Saparelli
f93ba29982 Update deps 2021-09-29 23:38:02 +13:00
Félix Saparelli
dfb5525c7e Add tokio-console 2021-09-29 01:47:18 +13:00
Félix Saparelli
e916ac8050
Add repology badge 2021-09-29 01:43:36 +13:00
Félix Saparelli
45a7ce6aa0
Fix logic error and upgrade warn to panic in debug 2021-09-28 23:44:28 +13:00
Félix Saparelli
6b306a15ab
Add event saturation notes 2021-09-28 22:52:59 +13:00
Félix Saparelli
30dae61a02
Add filtering to demo CLI 2021-09-28 22:25:56 +13:00
Félix Saparelli
7cdb6ac5ad
Bypass filters for empty events 2021-09-28 22:23:48 +13:00
Félix Saparelli
f492bca8c3
Add filter add/del error to runtime 2021-09-28 22:23:23 +13:00
Félix Saparelli
9bb6e1356a
Add is_empty and is_internal to Event 2021-09-28 22:22:33 +13:00
Félix Saparelli
f673d00e9c
Add logging to filter parser 2021-09-28 22:22:14 +13:00
Félix Saparelli
f3c74bd151
Add methods to configure tagger filter 2021-09-28 22:21:51 +13:00
Félix Saparelli
4fda3c477b
Add logging to tagged filter 2021-09-28 22:21:13 +13:00
Félix Saparelli
6e414d1de4
Change the default of Shell 2021-09-28 01:44:20 +13:00
Félix Saparelli
2c894266a8
Add negation filters, and filter application 2021-09-28 00:54:33 +13:00
Félix Saparelli
b57fa8b236 Start implementing two Filterers (v1 and v2) 2021-09-23 21:59:35 +12:00
Félix Saparelli
7875b4db67
Provide filterer as trait
which will allow basically anything user-provided as filter, though of
course we'll have (soon) our own fairly comprehensive implementation
2021-09-22 23:39:41 +12:00
Félix Saparelli
f1685821a3
Split types out of action 2021-09-18 17:20:05 +12:00
Félix Saparelli
29d0b66ba9
Move filter types out 2021-09-18 17:09:00 +12:00
Félix Saparelli
2be21b6bac
Move filter parser out 2021-09-18 17:07:32 +12:00
Félix Saparelli
84dc77f787 Add parser for filters 2021-09-14 20:11:29 +12:00
Félix Saparelli
6a55f5cc6d
Start on filter types 2021-09-13 19:51:07 +12:00
Félix Saparelli
9e3c8c1f32
Rename particle/culars to tags 2021-09-13 19:34:40 +12:00
Félix Saparelli
59647e64a6
Merge pull request #213 from ethanhs/patch-1
Add arm64 musl build
2021-09-06 08:46:49 +12:00
Ethan Smith
c2d57fcf48
Add arm64 musl build 2021-09-05 13:42:20 -07:00
Félix Saparelli
e5fcc6553e
Also leave alt buffer on reset 2021-09-04 02:00:57 +12:00
Félix Saparelli
4043ed34ae
Add Reset outcome (#186, #211) 2021-09-04 01:55:25 +12:00
Félix Saparelli
645ab74c62
Implement Outcome::Wait, and CLI on-busy=queue 2021-09-03 09:25:23 +12:00
Félix Saparelli
b923638cbd
Correctly watch for process completion 2021-09-03 09:25:06 +12:00
Félix Saparelli
9c8d4c1a1b
Add fs event metadata to event 2021-09-03 08:14:04 +12:00
Félix Saparelli
07f5e445f6
Use Event Display impl in cli 2021-09-03 07:58:20 +12:00
Félix Saparelli
5cbbb7b67f
Add Display impl for events 2021-09-03 07:57:59 +12:00
Félix Saparelli
608aa516b1
Add source to internal event 2021-09-03 07:57:45 +12:00
Félix Saparelli
29e7780fdc
Add process completion handling to cli demo 2021-09-03 05:43:53 +12:00
Félix Saparelli
ec6b508894
Ignoring supervisor kill/signal internal errors
As the errors only occur when the process is dead anyway
2021-09-03 05:43:26 +12:00
Félix Saparelli
b728bfecfc
Differentiate trace logs when ending supervisor 2021-09-03 05:42:24 +12:00
Félix Saparelli
f880b0b38a
Always apply Both outcomes, even when the first fails 2021-09-03 05:41:50 +12:00
Félix Saparelli
8e4994abca
Add process supervisor to watch command to completion
Also change the concept of a completion handler to instead sending a
synthetic "process completed" event down the same path as usual.

That makes handling completion the job of the action handler, but also
means it's immediately possible to launch a process or do an action in
response to the process completing. Win win!
2021-09-03 05:22:15 +12:00
Félix Saparelli
0f247e9e5c
Fix demo for poll option gaining a timeout 2021-09-03 05:18:30 +12:00
Félix Saparelli
ef453193af
Add pre-spawn and post-spawn hooks 2021-08-25 04:41:14 +12:00
Félix Saparelli
1fd5c85317 Good idea but let's keep compat for now 2021-08-25 02:40:22 +12:00
Félix Saparelli
6df6d6fd5a Handle signals in cli 2021-08-24 23:19:44 +12:00
Félix Saparelli
7d492fa677
Hide --watch-when-idle from help 2021-08-24 22:56:15 +12:00
Félix Saparelli
70e8a4dff2
Fix cli tests 2021-08-24 22:53:44 +12:00
Félix Saparelli
58b37940b8 Implement most existing options and mark the rest 2021-08-24 22:46:16 +12:00
Félix Saparelli
6767948daa
Specify delay for fs Poll mode 2021-08-24 22:28:29 +12:00
Félix Saparelli
33fb691d29
Add method to insert events into watchexec manually 2021-08-24 22:20:44 +12:00
Félix Saparelli
9cb1f5bf79
Re-export Outcome's Signal type 2021-08-24 21:31:46 +12:00
Félix Saparelli
e939f97c90
Move config creation out of arg parsing, and start on using libv2 2021-08-24 20:23:37 +12:00
Félix Saparelli
23d794ed7e
Apply tabs project-wide 2021-08-24 20:22:25 +12:00
Félix Saparelli
33f8b60e46
Take IntoIterator<AsRef<str>> to be more flexible on input 2021-08-24 20:14:01 +12:00
Félix Saparelli
05afb141b6
Add InitConfig::builder() to be a little more idiomatic
Neat side effect: keeps rust-analyzer from complaining about unknown types
(because it doesn't expand the builder macro)!
2021-08-24 19:59:11 +12:00
Félix Saparelli
b4ead7f5fb
Report several runtime errors if notify gives us several paths in an error 2021-08-23 06:08:25 +12:00
Félix Saparelli
3588cb4d62
Log fs watch/unwatch errors harder (to reveal paths) 2021-08-23 05:15:55 +12:00
Félix Saparelli
6cb3fc1c9a
Demo switching file watcher backends at runtime 2021-08-23 03:59:02 +12:00
Félix Saparelli
4a9168f5e6
Log on graceful exit 2021-08-23 03:36:10 +12:00
Félix Saparelli
9b94f18890
Fix double-actioning in throttle 2021-08-23 03:12:23 +12:00
Félix Saparelli
b42336cb74
Actually watch files in example 2021-08-23 03:11:58 +12:00
Félix Saparelli
b2effda341
Update to miette 1.0 beta 2021-08-23 02:37:13 +12:00
Félix Saparelli
5314d201a4
Expand print_out example into watchexec test case 2021-08-23 02:36:58 +12:00
Félix Saparelli
74d8e73817
Fix tokio panic for async handlers 2021-08-23 02:35:28 +12:00
Félix Saparelli
931648a955
Add signals() convenience iter on Event 2021-08-23 02:35:03 +12:00
Félix Saparelli
cc5b1c988e
Add some logging to Process 2021-08-23 02:34:44 +12:00
Félix Saparelli
60ed6bba25
Fix infinite loop in action recv code 2021-08-23 02:34:29 +12:00
Félix Saparelli
05117e69fe
Prep for more handlers in action 2021-08-23 02:33:23 +12:00
Félix Saparelli
4843920a36
Protect again starting command without anything in command 2021-08-23 02:32:48 +12:00
Félix Saparelli
17b09d8798
Add graceful exit support 2021-08-23 02:32:08 +12:00
Félix Saparelli
f4a8a9fc6a
Print PID in initialisation 2021-08-23 02:30:56 +12:00
Félix Saparelli
18d2487ec3
Rename reconfig to reconfigure 2021-08-23 00:31:39 +12:00
Félix Saparelli
3066ee5913
Implement most of process handling 2021-08-23 00:28:20 +12:00
Félix Saparelli
f150c26b22
Add action.grouped config 2021-08-23 00:27:45 +12:00
Félix Saparelli
d2d0bb7be2
Resolve compound outcomes 2021-08-22 23:23:01 +12:00
Félix Saparelli
7a709b9b4d
Prefer generic Both combinator than specific ClearAnd 2021-08-22 23:22:27 +12:00
Félix Saparelli
c85c164c09
Make the example in lib.rs compile 2021-08-22 22:06:50 +12:00
Félix Saparelli
227c2a0e0d
Add command and shell options to action 2021-08-22 22:06:31 +12:00
Félix Saparelli
613fe24c64
Add paths() convenience iter on Event 2021-08-22 22:06:12 +12:00
Félix Saparelli
8998c40746
Add convenience methods for Outcome's combinators 2021-08-22 22:05:45 +12:00
Félix Saparelli
d7d549a4c8
Add convenience modifiers on RuntimeConfig ourselves
It's not exactly a builder, and this lets us flatten all the options at the
top level instead of requiring the user to dig deeper into the
action, fs, etc modules' WorkingData structs.
2021-08-22 22:05:09 +12:00
Félix Saparelli
6c3c06e39c
Stop using Builder for RuntimeConfig 2021-08-22 20:49:24 +12:00
Félix Saparelli
53854d93d4
Change Outcome::OrStart to the more flexible ::IfRunning 2021-08-22 20:47:47 +12:00
Félix Saparelli
656c0d8fd2
Fix action throttling
Specifically, on loop start the timeout would be for the full throttle duration,
which is not correct if some time has passed or the loop goes on recycle
2021-08-22 20:29:57 +12:00
Félix Saparelli
40f6f7397f
Fill out Outcome 2021-08-22 20:27:51 +12:00
Félix Saparelli
5b8611a8c0
Make notes on needed docs 2021-08-22 20:26:48 +12:00
Félix Saparelli
350b85e0c7
Allow an outcome to be determined by the action handler without &mut! 2021-08-22 18:56:57 +12:00
Félix Saparelli
2a0661b122
Call action handler on action 2021-08-22 05:58:03 +12:00
Félix Saparelli
2812a723ff
Write action throttling code 2021-08-22 02:54:02 +12:00
Félix Saparelli
ce60be2ec9
Add print_out example 2021-08-22 02:53:31 +12:00
Félix Saparelli
f2f138ce9f
Add logging to Watchexec 2021-08-22 02:53:20 +12:00
Félix Saparelli
019018c93e
Add docs for error_handler as it's a bit tricky 2021-08-21 22:30:19 +12:00
Félix Saparelli
816313303a
Finish handlers by implementing the error hook 2021-08-21 20:46:44 +12:00
Félix Saparelli
0f37e42243
Add action worker 2021-08-21 04:43:55 +12:00
Félix Saparelli
dcde429787
Improve handlers to not need GATs and provide various impls 2021-08-21 04:43:15 +12:00
Félix Saparelli
9f34492c79
Upgrade to notify pre.12 2021-08-20 02:59:39 +12:00
Félix Saparelli
95ad3e91ff
Add example / aspirational usage to lib doc 2021-08-20 02:56:13 +12:00
Félix Saparelli
6a46c2bff3
Refactor watchexec to only have &self methods and default to wrap it in Arc 2021-08-20 02:55:34 +12:00
Félix Saparelli
249c581dc9
Make channel buffers configurable 2021-08-19 21:28:56 +12:00
Félix Saparelli
5d11ccaa71
Add a bit more structure via documentation 2021-08-19 20:44:02 +12:00
Félix Saparelli
319729582f
Split signal and fs examples 2021-08-19 20:31:29 +12:00
Félix Saparelli
6f3abdeaea
Add RuntimeError::from_handler utility 2021-08-19 20:30:35 +12:00
Félix Saparelli
4e4a8e6853
Add Handler trait 2021-08-19 20:30:01 +12:00
Félix Saparelli
0bb38f40a5
Start off on main interface 2021-08-19 01:12:50 +12:00
Félix Saparelli
826dbd8cda
Make fs::worker Send 2021-08-19 00:40:35 +12:00
Félix Saparelli
cae00070fb
Remove chrono dep 2021-08-18 18:54:05 +12:00
Félix Saparelli
0237a568df
Handle signals into events 2021-08-17 21:41:13 +12:00
Félix Saparelli
f5e19a6e5f
Adapt Shell command builder 2021-08-17 03:09:22 +12:00
Félix Saparelli
7053360187
Clarify fs worker usage 2021-08-17 01:37:01 +12:00
Félix Saparelli
822148da03
Canonicalise paths + add example 2021-08-17 01:15:17 +12:00
Félix Saparelli
f08bdad8ee
Use local datetime on events instead of naive 2021-08-16 21:52:13 +12:00
Félix Saparelli
61fec2cf27
Use lib v1 for cli 2021-08-16 21:49:22 +12:00
Félix Saparelli
b15615bbaa
Start on watchexec v2 2021-08-16 21:49:12 +12:00
Félix Saparelli
e21a3a99f6 cli: v1.17.1 2021-07-31 06:37:29 +12:00
Félix Saparelli
13140c88c4 Temporarily disable rpm releases 2021-07-31 06:36:57 +12:00
Félix Saparelli
42c34a66a2
Install release tools with --locked 2021-07-31 06:18:47 +12:00
Félix Saparelli
c6ec9015f7
Add --notify to test 2021-07-31 05:26:26 +12:00
Félix Saparelli
d54d74854e
Add experimental --notify option
See #139
2021-07-31 05:11:42 +12:00
Félix Saparelli
a3173194a1
Implement cli's own wrapper handler
That will let us hook in watchexec-cli specific stuff
2021-07-31 04:24:05 +12:00
Félix Saparelli
3208652ed8
Add citation.cff 2021-07-29 00:45:13 +12:00
Félix Saparelli
aa99fa1674 Add bin/sign tool 2021-07-22 03:56:31 +12:00
Félix Saparelli
bc07ad486d cli: v1.17.0 2021-07-22 03:25:51 +12:00
Félix Saparelli
3948e4e9a0 Cache cargo bin in CI 2021-07-22 03:18:27 +12:00
Félix Saparelli
d7b7dcd5f0 Only install deb+rpm tooling on linux 2021-07-22 03:13:00 +12:00
Félix Saparelli
288eba37e0 Use lib 1.17.1 2021-07-22 01:36:56 +12:00
Félix Saparelli
bd96803d1e lib: v1.17.1 2021-07-22 01:36:33 +12:00
Félix Saparelli
0ab1405186
Ignore errors when killing command on pre-spawn 2021-07-22 01:36:19 +12:00
Félix Saparelli
7bd99f30df Use lib 1.17.0 2021-07-22 01:18:00 +12:00
Félix Saparelli
bf922e36ac lib: v1.17.0 2021-07-22 01:15:46 +12:00
Félix Saparelli
d8fb70c454
Cease with the 2015-style #[macro_use]s 2021-07-22 01:01:11 +12:00
Félix Saparelli
66caedf978
Add --no-process-group flag 2021-07-22 00:56:35 +12:00
Félix Saparelli
ef36d3429d
Fix expectation in windows path test 2021-07-22 00:47:52 +12:00
Félix Saparelli
73eef67206
Don't rely on bash being installed for windows tests 2021-07-22 00:47:02 +12:00
Félix Saparelli
3485bd7e16
Add windows tests for path/env handling 2021-07-22 00:40:05 +12:00
Félix Saparelli
f88c30b854
Another windows test fix 2021-07-22 00:22:39 +12:00
Félix Saparelli
18fdbbcfea Merge branch 'docs/website' 2021-07-22 00:18:13 +12:00
Félix Saparelli
85e2c12c34
Review readmes, add website links 2021-07-22 00:13:59 +12:00
Félix Saparelli
146d49ee8e
Update deps 2021-07-21 23:57:31 +12:00
Félix Saparelli
665f5ef6f0
Remove binstall metadata from the lib 2021-07-21 23:55:10 +12:00
Félix Saparelli
d59a76182a
Remove cargo install stub 2021-07-21 23:54:42 +12:00
Félix Saparelli
392547ffa4
Fix typo 2021-07-21 23:47:16 +12:00
Félix Saparelli
f5a92220c9
Drop debounce to 100ms (#168) 2021-07-21 23:44:24 +12:00
Félix Saparelli
eb59e92b8f
Add support for disabling process groups (#158) 2021-07-21 23:38:42 +12:00
Félix Saparelli
bb6a5ae891
Split process.rs into shell and paths
(now that the bulk of process code is gone)
2021-07-21 23:37:24 +12:00
Félix Saparelli
72cda2b0b0
Make sure to kill the old command group before starting a new one 2021-07-21 21:42:55 +12:00
Félix Saparelli
9c20c8c8b5 Replace process code with command-group 2021-07-21 20:22:40 +12:00
Félix Saparelli
4d6ad2cc1f
Use website as source for docs logo 2021-07-20 20:32:38 +12:00
Félix Saparelli
07974e0d14
Implement @argfile argument parsing (#145) 2021-07-17 02:10:03 +12:00
Félix Saparelli
847520357c
Rename ignore test fns 2021-07-17 01:33:39 +12:00
Félix Saparelli
54ac2e9ae0
Use color-eyre for application errors rather than the library's error type 2021-07-17 01:33:04 +12:00
Félix Saparelli
0c25ea0269 cli: v1.16.2 2021-07-10 20:08:45 +12:00
Félix Saparelli
6457df5a0a Use 1.16.1 lib in cli 2021-07-10 20:08:23 +12:00
Félix Saparelli
8368846444 Update deps 2021-07-10 20:07:59 +12:00
Félix Saparelli
7b16a226b2 lib: v1.16.1 2021-07-10 20:04:31 +12:00
Félix Saparelli
ba26999028 Pin globset version to avoid breakage
Breakage caused by this fix: https://github.com/BurntSushi/ripgrep/pull/1756

The fix is correct, but it does break a lot of stuff :/
2021-07-10 20:02:37 +12:00
Félix Saparelli
604c24ec38 Link to website for downloads 2021-07-10 03:48:12 +12:00
Félix Saparelli
e6c7da3635 Add rpm support with cargo-generate-rpm 2021-07-10 03:48:12 +12:00
Félix Saparelli
1eaaf7e844 Use cargo-deb to replace existing deb kludge 2021-07-10 03:48:12 +12:00
Félix Saparelli
4526ba2cae Merge in cargo watch's release improvements 2021-07-10 03:48:12 +12:00
Félix Saparelli
654a324fdd Set website as website 2021-07-10 03:48:12 +12:00
Félix Saparelli
08d795f841 Remove stub main.rs from library 2021-07-10 03:48:12 +12:00
Félix Saparelli
badb27a19c Add release public key 2021-07-10 03:48:12 +12:00
Félix Saparelli
bdc2e384fc
Merge pull request #202 from watchexec/on-update/bug-200 2021-07-10 03:47:37 +12:00
Félix Saparelli
585f73adde Poll process completion harder on windows 2021-07-10 03:43:21 +12:00
Félix Saparelli
71a178d4c2 Actually check process when querying for completion
With --on-update=do-nothing, we need to know when the process is done
before we can spawn a new one, but we never actually used to truly check
the process, only the presence or absence of a spawned process. That
process may have already completed, but because we don't wait on it when
in do-nothing mode, there is no opportunity to notice this.

So now we either actually check the completion status of the process (on
Windows), or we expose the `done` mutex value on demand (Unix).
Essentially this adds a way to check the completion status of the
process without blocking (modulo a mutex lock on unix).

Fixes #200
2021-07-10 03:43:21 +12:00
Félix Saparelli
960bbbabbc cli: v1.16.1 2021-05-22 20:05:57 +12:00
Félix Saparelli
695c2e0f1a Update lockfile 2021-05-22 20:00:06 +12:00
Félix Saparelli
f0862b9f1e Update to clearscreen 1.0.4 for bugfix 2021-05-22 19:59:32 +12:00
Félix Saparelli
d8540d76b8
Fomat and clippy 2021-05-10 23:37:03 +12:00
Félix Saparelli
319727b0a6
It was in the wrong place the whole time! 🤬 2021-05-10 23:27:53 +12:00
Félix Saparelli
7e4ff78bc4
🤦 2021-05-10 23:21:30 +12:00
Félix Saparelli
169de99ee5
Whoops, wrong file 2021-05-10 23:15:14 +12:00
Félix Saparelli
1015c1979a
Update help snapshot version when releasing 2021-05-10 23:10:55 +12:00
Félix Saparelli
cb684fc7a8
Fix help snapshot for windows (.exe!) 2021-05-10 23:10:30 +12:00
Félix Saparelli
0d2f28c91a
Add test for cli help 2021-05-10 23:02:31 +12:00
Félix Saparelli
eafbc9a77b
Clean up due to removing pubs 2021-05-10 22:44:35 +12:00
Félix Saparelli
324fbe9055
Remove unused cli mod 2021-05-10 22:27:41 +12:00
Félix Saparelli
668d931eac
Remove public interface now that cli is a different crate 2021-05-10 22:26:38 +12:00
Félix Saparelli
6e844cc29a
Turn off panic=abort
This may help in some rare crash cases where system resources could be in an inconsistent state.
2021-05-09 16:29:50 +12:00
Félix Saparelli
dc4a31252f
Only check MSRV for library 2021-05-09 04:20:13 +12:00
Félix Saparelli
be16d2e6a8
Use published lib in cli for cli publish 2021-05-09 03:54:06 +12:00
Félix Saparelli
9a002be54c
Ignore lib version tags for CI to avoid work duplication 2021-05-09 03:49:57 +12:00
Félix Saparelli
ad9f51a24b lib: v1.16.0 2021-05-09 03:46:23 +12:00
Félix Saparelli
c517b98782 Split readmes 2021-05-09 03:44:11 +12:00
Félix Saparelli
6a0ac73547 cli: v1.16.0 2021-05-09 02:21:07 +12:00
Félix Saparelli
c5c69731c2 Use grep -E for macOS compat 2021-05-09 02:19:53 +12:00
Félix Saparelli
8ebbce5516
Add nix to install section 2021-05-09 01:50:44 +12:00
Félix Saparelli
d4e0e93278
Only build cli crate during release 2021-05-09 01:23:06 +12:00
Félix Saparelli
148be31f65
Add a stub to the watchexec (lib) crate to point to the watchexec-cli crate 2021-05-09 01:19:50 +12:00
Félix Saparelli
8295ac7a1c Don't fail CI on clippy warnings 2021-05-08 19:22:35 +12:00
Félix Saparelli
6aebd9b3e7 Formatting 2021-05-08 19:12:13 +12:00
Félix Saparelli
07f56ac7d5
Refactor inotify max watches warning to be less nested and satisfy clippy better 2021-05-08 19:07:56 +12:00
Félix Saparelli
d23261673b
Update clearscreen 2021-05-08 18:43:12 +12:00
Félix Saparelli
bdc3550a0d
Restrict CI to changes that will cause recompiles 2021-05-01 04:32:42 +12:00
Félix Saparelli
149b9b9821
Split the versioning of the lib and cli (in theory) 2021-05-01 04:22:33 +12:00
Félix Saparelli
aeab4159d4
Change semver policy for the library 2021-05-01 03:56:16 +12:00
Félix Saparelli
b761118c19
Leave current name on readme 2021-05-01 03:50:33 +12:00
Félix Saparelli
181ca7b5c5
Change crate description for library 2021-05-01 03:47:22 +12:00
Félix Saparelli
aff9f2d8f2
Leave binstall config on the lib crate for now 2021-05-01 03:46:36 +12:00
Félix Saparelli
1f3f4ada35 Split into two crates: lib and cli
That has a number of advantages:

- #193 the build.rs is only run for the CLI, so the Windows manifest is
  not embedded in the library anymore, opening it up for downstreams.

- it sets the stage for decoupling the version numbers of the CLI and
  library, to have the library increase its major more often, while the
  CLI retains compatibility further… that is, to have both follow semver

- it removes the CLI-only dependencies from the library

- it makes compilation a bit faster as compiling the library and the
  CLI's other dependencies can happen in parallel

One major disadvantage:

- installing via cargo changes from watchexec to watchexec-cli. Most
  installs are from prebuilt and from packages, but that's still a
  potential stumble.

And of course, the CLI APIs in the library are gone (they were already
deprecated, though).

We also take this opportunity to get rid of the clear_screen code and
use our new clearscreen library. #99 #171 #185
2021-05-01 03:27:17 +12:00
Félix Saparelli
898f83e29c
Add experimental builds for WinARM 2021-05-01 02:26:33 +12:00
Félix Saparelli
5da59b122d
Finish apple m1 prep config 2021-05-01 02:25:10 +12:00
Félix Saparelli
74934f304f 1.15.3 2021-05-01 00:27:44 +12:00
Félix Saparelli
db5b662581
Vom
Fixes #192 again
2021-04-30 14:19:19 +12:00
Félix Saparelli
74cec51700
Disable activeCodePage again
Fixes #192
2021-04-30 13:53:25 +12:00
Félix Saparelli
6e2835b8aa
Fix #191: timing suffix is now provided by Duration's Debug impl, so remove it from the message 2021-04-28 14:12:16 +12:00
Félix Saparelli
12184136f7 1.15.2 2021-04-27 02:04:52 +12:00
Félix Saparelli
bed43d9e4c
Add compatiblity entries again 2021-04-27 01:59:44 +12:00
Félix Saparelli
97207710cd
Attempt to fix format of metadata section 2021-04-27 01:49:39 +12:00
Félix Saparelli
1fab379cbd
Add back metadata 2021-04-27 01:39:17 +12:00
Félix Saparelli
0db6c97f2d
Add heapType again 2021-04-27 01:33:41 +12:00
Félix Saparelli
f30e2fc49e
Add activeCodePage again 2021-04-27 01:28:34 +12:00
Félix Saparelli
73ac954a40
Update ws ns to 2020 2021-04-27 01:23:51 +12:00
Félix Saparelli
1a8d1cb142
Back to only longPath, but in new format? 2021-04-27 01:12:09 +12:00
Félix Saparelli
fe4410640d
Only do the manifest settings? 2021-04-27 01:06:37 +12:00
Félix Saparelli
226ef97ad9
Also opt-in to the UTF-8 codepage 2021-04-27 00:53:42 +12:00
Félix Saparelli
150c474b9c
Opt into SegmentHeap to maybe reduce memory use on Windows 2021-04-27 00:34:15 +12:00
Félix Saparelli
fa081d97bb
Augment the windows manifest with metadata and OS version compatibility 2021-04-27 00:29:04 +12:00
Félix Saparelli
8decbd63f6 Update lockfile 2021-04-27 00:10:21 +12:00
Félix Saparelli
1b889b3063
Limit version up script to the first version ie ours 2021-04-27 00:09:11 +12:00
Félix Saparelli
34989e6fc2
Add logo 2021-04-26 23:59:43 +12:00
Félix Saparelli
a510ecd936 Enable experimental Apple M1 cross builds and downgrade i686 musl to experimental 2021-04-21 10:03:24 +12:00
Félix Saparelli
cdcb4e2af4 Show multi command and --shell uses 2021-04-19 09:42:27 +12:00
Félix Saparelli
2603f13fc7 1.15.1 2021-04-17 12:48:21 +12:00
Félix Saparelli
25f02c5777
Add test for Shell::Unix with shopts 2021-04-17 02:19:59 +12:00
Félix Saparelli
1c338adb9d
Generate html manpage with toc style 2021-04-17 02:11:16 +12:00
Félix Saparelli
81b3340f6c
Update lockfile to latest notify patch release 2021-04-17 02:05:04 +12:00
Félix Saparelli
0e26934bbd
Add manpage in HTML format to release packages 2021-04-17 02:04:43 +12:00
Félix Saparelli
591b4a11c2 Remove obsolete brew formula 2021-04-17 02:01:26 +12:00
Félix Saparelli
749bf89b6c Document the argument processing in the manpage
Fixes #82
2021-04-17 02:01:26 +12:00
Félix Saparelli
f1c7b555e1 Document the --shell semantics in the manpage 2021-04-17 02:01:26 +12:00
Félix Saparelli
6635635a9c Fix #181 by splitting shell program in Shell::Unix() by ascii space to handle additional args 2021-04-17 02:01:26 +12:00
Félix Saparelli
1b44e7aa87 Extract Shell handling into one method common to both windows and unix 2021-04-17 02:01:26 +12:00
Félix Saparelli
a2078e3703 Document the semantics of the Shell variants 2021-04-17 02:01:26 +12:00
Félix Saparelli
3c26e3987c
Merge pull request #189 from mwu-tow/git-root-lookup
Fix lookup of the root git directory
2021-04-13 02:10:32 +12:00
Michał W. Urbańczyk
10429777ce Fixed lookup of the root git directory. 2021-04-12 13:32:57 +02:00
Félix Saparelli
940f4b98d0 Update lockfile 2021-04-11 06:09:48 +12:00
Félix Saparelli
139b6b43c7
Drop debounce to 150ms (#168) 2021-04-11 06:05:28 +12:00
Félix Saparelli
03aad1caaa
Don't run checks on tags 2021-04-11 05:59:42 +12:00
Félix Saparelli
579e53678e 1.15.0 2021-04-11 05:58:32 +12:00
Félix Saparelli
d124fdb587
Adjust bin/version to deal with this project's Cargo.toml 2021-04-11 05:53:04 +12:00
Félix Saparelli
6b1ba7aad7
Update dependencies 2021-04-11 05:51:33 +12:00
Félix Saparelli
05e66784ce
Apply clippy recommendations 2021-04-11 05:44:24 +12:00
Félix Saparelli
6ec02d38ab Regenerate manpage 2021-04-11 05:36:43 +12:00
Félix Saparelli
b5f56e831d
Formatting 2021-04-11 05:33:30 +12:00
Félix Saparelli
f4637892ab
Fix --shell option 2021-04-11 05:29:55 +12:00
Félix Saparelli
b0d2a6c875
Bump MSRV to 1.43 2021-04-11 05:17:11 +12:00
Félix Saparelli
15cdd7c754
Use Durations for durations in Config 2021-04-11 05:13:44 +12:00
Félix Saparelli
75ef0095ff
Cosmetic fixes to help 2021-04-11 05:06:32 +12:00
Félix Saparelli
5da11d2fd7 Add more unix shell testing 2021-04-11 05:01:21 +12:00
Félix Saparelli
52284ae866
If you say so, windows 2021-04-11 04:58:38 +12:00
Félix Saparelli
52ca1e98fb Formatting again 2021-04-11 04:55:00 +12:00
Félix Saparelli
d37261529c
Moar windows fixes 2021-04-11 04:50:31 +12:00
Félix Saparelli
15806019af
Fix windows compile mistake 2021-04-11 04:44:49 +12:00
Félix Saparelli
0ade9dfc3a
Implement Display for Signal on windows 2021-04-11 04:44:13 +12:00
Félix Saparelli
2e85658a12
Update MSRV to 1.40 for non_exhaustive 2021-04-11 04:38:56 +12:00
Félix Saparelli
5753e7773f
Formatting 2021-04-11 04:32:58 +12:00
Félix Saparelli
cdd8a7b91a
Fix process test and add more for windows 2021-04-11 04:32:48 +12:00
Félix Saparelli
ced37d7617 Expose on-busy-update in cli args 2021-04-11 04:21:47 +12:00
Félix Saparelli
e08f1934ec
Simplify on_update code further 2021-04-11 04:02:14 +12:00
Félix Saparelli
d25c374d7f
Clarify the behaviour when receiving updates while running in the code 2021-04-11 03:59:30 +12:00
Félix Saparelli
f32943bfd2
Document --watch-when-idle much more thoroughly 2021-04-11 03:31:44 +12:00
Félix Saparelli
1b5bdee08e
Make terminology more consistent and avoid some "kill child" usages 2021-04-11 03:21:38 +12:00
Félix Saparelli
12d130c3f1
Refactor run on_update to make the behaviour clearer 2021-04-11 03:15:17 +12:00
Félix Saparelli
6cfe649228
Document --shell 2021-04-11 02:51:20 +12:00
Félix Saparelli
e5cdd51633
Remove/deprecate Config.no_shell 2021-04-11 02:36:38 +12:00
Félix Saparelli
4c29c7a09d
Add --shell option 2021-04-11 02:36:10 +12:00
Félix Saparelli
92060e5655
Expose shell via builder 2021-04-11 02:28:29 +12:00
Félix Saparelli
1280a15ca2
Support powershell and custom shells in the backend 2021-04-11 02:08:39 +12:00
Félix Saparelli
2a382a9486
Rename spawn args positively 2021-04-11 01:44:44 +12:00
Félix Saparelli
7965ccb605
Discard code from attempt at wrapping quotes 2021-04-11 01:40:30 +12:00
Félix Saparelli
bbaaff8a1e
Split args from cli in preparation to remove it from lib entirely 2021-04-11 01:33:40 +12:00
Félix Saparelli
cd3b8c6cba
Move Args and builder to Config, and deprecate old names 2021-04-11 01:23:24 +12:00
Félix Saparelli
31fd818247
Emit log level separately from Args 2021-04-11 01:09:28 +12:00
Félix Saparelli
757de0d92e
Use ArgsBuilder ourselves 2021-04-11 01:09:06 +12:00
Félix Saparelli
0c6b1bf2e4
Undocument --kill 2021-04-11 01:06:32 +12:00
Félix Saparelli
988acaed65
Hide --kill option 2021-04-11 01:04:47 +12:00
Félix Saparelli
9c1b65e712
Deprecate get_args and get_args_from 2021-04-11 01:04:25 +12:00
Félix Saparelli
7d523dfcf0
Document clear_screen functions 2021-04-11 01:02:44 +12:00
Félix Saparelli
c42fe66985
Remove new Args.changes field 2021-04-11 01:02:13 +12:00
Félix Saparelli
426b6a8318
Deprecate Args.debug harder 2021-04-11 01:01:20 +12:00
Félix Saparelli
9968a66042
Hide Args.once harder 2021-04-11 00:58:24 +12:00
Félix Saparelli
268c0cee38
Harmonise defaults between cli and builder 2021-04-11 00:57:46 +12:00
Félix Saparelli
0b79e93c84
Mark cli::Args non-exhaustive so ArgsBuilder usage is mandatory 2021-04-11 00:56:47 +12:00
Félix Saparelli
fca038b23c
Reduce lints in favour of stability rather than strictness 2021-04-11 00:55:50 +12:00
Félix Saparelli
1da3a8d26c Add --changes-only to manpage and completions 2021-04-11 00:01:02 +12:00
Félix Saparelli
ecd7d1c7e3 Switch to gh actions and clean up metadata and build stuff 2021-04-10 23:53:12 +12:00
Félix Saparelli
3a3ae89046 Add version script 2021-04-10 22:44:11 +12:00
Félix Saparelli
5e4af976de
Merge pull request #180 from nindoja/add_changes_option
Add --changes-only option to only print path change information.
2021-04-09 19:30:53 +12:00
Shane Fry
949fbcf7ae Add --changes-only option to only print path change information. 2021-04-08 12:52:44 -05:00
Félix Saparelli
35e39afb95
Merge pull request #169 from coolaj86/patch-1 2020-10-03 21:45:27 +13:00
Félix Saparelli
f99a1a7a38
Normalise install section 2020-10-03 21:44:43 +13:00
AJ ONeal
6414f60b52 add Webi install instructions 2020-10-01 15:23:07 -06:00
Félix Saparelli
c8619e34e0 1.14.1 2020-09-30 17:16:54 +13:00
Félix Saparelli
838103fcf2 Embed long paths manifest on windows (#163) 2020-09-30 17:06:41 +13:00
Félix Saparelli
166f0b8fc2
Drop default debounce to 300ms (cf #168) 2020-09-30 16:48:24 +13:00
Félix Saparelli
f2da804d3b
Correct --watch help text as per #165 2020-09-15 13:21:02 +12:00
Félix Saparelli
55b03e82c3
Fix #165 by adding example of running against a single file 2020-09-07 02:12:38 +12:00
Félix Saparelli
570ed3afdf
Add windows manifest to enable long path support (#163)
- `requestedPrivileges`: explicitly set privilege level to be the same as parent
- `longPathAware`: opt into transparent long path support on Win10 and above
2020-07-19 16:14:23 +12:00
Félix Saparelli
fdb0cfb759 Add soft deprecation on Args.debug 2020-07-04 17:42:13 +12:00
Félix Saparelli
502081203a 1.14.0 2020-07-04 00:57:29 +12:00
Félix Saparelli
2926e956ad Wump winapi to .9 2020-07-04 00:51:51 +12:00
Félix Saparelli
0b5120430c Revise clippy lints to avoid breakage 2020-07-04 00:38:50 +12:00
Félix Saparelli
f38e7ab969 Cargo fmt 2020-07-04 00:20:02 +12:00
Félix Saparelli
f045fd45ac Mention new --no-meta/--no-env in readme 2020-07-03 23:55:54 +12:00
Félix Saparelli
66555c9b18 Add CONTRIBUTING.md 2020-07-03 23:51:53 +12:00
Félix Saparelli
0b13c39056
Merge pull request #160 from PhilipDaniels/main 2020-07-03 23:32:49 +12:00
Félix Saparelli
0716830374
Merge pull request #157 from qguv/fix-child-env-overflow 2020-07-03 23:19:29 +12:00
Philip Daniels
03142f5ec1 Call init_logger after parsing the args. 2020-06-25 11:16:22 +01:00
Philip Daniels
ebd5ec084f Do not call init_logger from inside watch.
And move init_logger to main.rs for future use.
2020-06-24 15:17:59 +01:00
Quint Guvernator
2615a1de7c wrap ioerror when it represents oserror E2BIG 2020-06-24 13:40:11 +02:00
Quint Guvernator
cca7b19283 fix some clippy lints 2020-06-24 10:58:15 +02:00
Quint Guvernator
7770de8f7c test: add new arguments to spawn(...) 2020-06-23 15:02:10 +02:00
Quint Guvernator
d246114678 add --no-meta switch to avoid some redundant events for #78 2020-06-23 14:41:53 +02:00
Quint Guvernator
524812028d add --no-environment switch to fix #78 2020-06-23 13:43:55 +02:00
Félix Saparelli
6f6fb0ceb2
Merge pull request #156 from thomasetter/main
add Kate (KDE default editor) swap file to default ignores
2020-06-15 15:39:22 +12:00
Thomas Etter
c50abc324c add Kate (KDE default editor) swap file to default ignores
.filename.kate-swp format https://unix.stackexchange.com/a/112400
2020-06-14 18:17:13 +02:00
Félix Saparelli
b9473c6c86 1.13.1 2020-06-06 15:21:08 +12:00
Félix Saparelli
92029f7365 Fix faulty .git lookup code 2020-06-06 15:16:02 +12:00
Félix Saparelli
80bbea7d21 1.13.0 2020-06-04 19:39:07 +12:00
Félix Saparelli
6a3af92d25 Update deps 2020-06-04 19:38:49 +12:00
Félix Saparelli
be6c29c821
Merge pull request #147 from Chris-Nicholls/gitignore 2020-06-04 19:29:53 +12:00
Martin Sehnoutka
f8f6c0ac5a Mention time units explicitly in CLI interface
The force-poll flag does not mention time units as opposed to the
debounce flag and to make it even more confusing, the comment in the
code says something else than the output:
*** Polling for changes every 2000 ms

This patch makes it clear what units are used.
2020-04-18 17:15:46 +12:00
Chris Nicholls
2d29a7d7c4 Look for .ignore files in subfolders of watched paths 2020-03-09 10:59:02 +00:00
Chris Nicholls
0175e071e1 Include .gitignores in subdirectories 2020-03-06 10:28:29 +00:00
Félix Saparelli
5843deb573
Allow clippy::too_many_lines 2019-11-19 19:42:20 +13:00
Félix Saparelli
97232cfd09
1.12.0 2019-11-19 19:06:43 +13:00
daubaris
50fa771c41 Adding missed details 2019-11-19 01:15:07 +13:00
daubaris
9a204241f7 Satisfying requested changes 2019-11-19 01:15:07 +13:00
daubaris
7ad8e78a34 Rename the command from 'watch-idle' to 'watch-when-idle' 2019-11-19 01:15:07 +13:00
daubaris
de5ed1c2bd #123 --watch-idle flag implementation 2019-11-19 01:15:07 +13:00
Félix Saparelli
90b9a85c0c
Swap out deprecated mem::uninitialized() 2019-11-19 00:38:58 +13:00
Thomas Etter
6e64a2c152 update the test to make sure that files matching part of the ignore are not excluded 2019-11-12 23:36:49 +13:00
Thomas Etter
1c6f5fab89 fix the ignore path handling to match the README by ignoring everything
below a directory
2019-11-12 23:36:49 +13:00
Félix Saparelli
798fd3c0bd
Fix verb tense on help message for --force-poll 2019-11-11 09:51:06 +13:00
Félix Saparelli
211bb681f5
1.11.1 2019-10-28 22:32:55 +13:00
Félix Saparelli
e33fd8f5b5
Remove borrowing that was introduced in previous release 2019-10-28 22:32:08 +13:00
Félix Saparelli
05c0d4aaea
Allow clippy failures 2019-10-28 20:00:47 +13:00
Félix Saparelli
a0053ccda4
1.11.0 2019-10-28 19:20:10 +13:00
Félix Saparelli
97fd7c6a17
Update nix, glob, env_logger 2019-10-28 19:02:57 +13:00
Félix Saparelli
bb02d84661
Re-export useful things 2019-10-28 18:56:06 +13:00
Félix Saparelli
6c653e8e52
Return owned Args from Handler instead of borrowed 2019-10-28 18:55:39 +13:00
Wayne Warren
720ff44b71 #133: leave Handler initialization to watch caller 2019-10-28 18:17:25 +13:00
Félix Saparelli
31b5cb0ab6
Add chocolatey package 2019-10-28 14:51:04 +13:00
Félix Saparelli
a1fbad0180
Fix last clippy lints 2019-10-28 13:48:57 +13:00
Félix Saparelli
7ef9d13b51
Fix travis clippy check 2019-10-28 13:19:12 +13:00
Félix Saparelli
2d4e74ef33
Fix travis msrv windows check 2019-10-28 13:18:09 +13:00
Félix Saparelli
544d133520
Adjust badges 2019-10-28 13:17:15 +13:00
Félix Saparelli
e0a67e3e7e
Lint travis config 2019-10-28 13:13:09 +13:00
Félix Saparelli
e54b7f2427
Remove appveyor 2019-10-28 13:11:33 +13:00
Félix Saparelli
24204e7b71
Add windows travis builds 2019-10-28 13:01:55 +13:00
Félix Saparelli
f7e1639ed1
Upgrade to winapi 0.3 2019-10-28 12:17:00 +13:00
Félix Saparelli
e563ae8fc1
Enable anti-unwrap lints 2019-10-28 01:12:10 +13:00
Félix Saparelli
5f7123ecbe
Lint tests too 2019-10-28 00:37:24 +13:00
Félix Saparelli
a3c5bd7201
Run formatter 2019-10-28 00:31:52 +13:00
Félix Saparelli
10fac30c7b
Add editorconfig 2019-10-28 00:31:41 +13:00
Félix Saparelli
d47419e385
Forbid a whole lot more clippy stuff 2019-10-28 00:31:26 +13:00
Félix Saparelli
b842c149b6
Use Rust 2018 2019-10-27 23:58:00 +13:00
Félix Saparelli
0eb83a6387
Bump msrv to 1.38 2019-10-27 23:49:52 +13:00
Félix Saparelli
f56e838fb9
Lints and clippys 2019-10-27 23:47:35 +13:00
Félix Saparelli
6b028cb649
Add builder for Args 2019-10-27 23:29:58 +13:00
Félix Saparelli
8e84eb6b04
Add semver policy statement 2019-10-27 22:54:44 +13:00
Sven-Hendrik Haase
3766114f40 Add zsh completions to release files 2019-10-23 17:29:44 +13:00
Sven-Hendrik Haase
63c7895407 Add --no-ignore to zsh completion 2019-10-23 17:29:44 +13:00
Sven-Hendrik Haase
43aa0947dc Add zsh completions for #118 (credit @Mange) 2019-10-23 17:29:44 +13:00
Sven-Hendrik Haase
7eafb48aa8 Prefer using --locked
This is to ensure that binaries built in this way are built reproducibly.
2019-10-23 17:28:54 +13:00
Josh Soref
0d4a2270ec spelling: supporting 2019-10-14 11:09:51 +13:00
Ryan James Spencer
e90bbcb9bd Support a dedicated ignore file
ref. https://github.com/passcod/cargo-watch/issues/127

This adds support for a dedicated ignore file by the name of `.ignore` a
la `fd`, `ripgrep`, et. al.

This purely just mimics what `Gitignore` is doing except it doesn't
ignore `.git` directories. There might be more I need to tweak and the
interface might be too obtuse, but this is a first pass.

I've also added a `--no-ignore` flag which ignores both `.gitignore` and
the dedicated `.ignore`. It might make sense to add a specific flag that
ignores `.ignore` but respects `.gitignore` to support the old
behaviour, but I wasn't sure what to name it.
2019-10-12 14:13:57 +13:00
Jon Grythe Stødle
c4ac0c0cbb Fix typo in -e description 2019-10-11 11:41:11 +13:00
Félix Saparelli
1738f26161 1.10.3 2019-07-30 11:47:37 +12:00
Leon Barrett
841d72b669 Update options in man page
The options were out of date (e.g. `-d` was debug). This updates them to
match the help text.

This also adds some documentation about separating the command with --
so that clap doesn't eat the flags intended for the command.
2019-07-30 11:44:58 +12:00
Félix Saparelli
64c7b5112e
Update Arch install instructions (close #107) 2019-06-20 15:46:39 +12:00
Félix Saparelli
0a2bc01c2f 1.10.2 2019-05-29 18:38:35 +12:00
Félix Saparelli
98b57ac64e [libs] Update notify to 4.0.12 2019-05-29 18:27:11 +12:00
Félix Saparelli
8e8410e35d [docs] Add zsh completion wiki page 2019-04-17 22:16:35 +12:00
Igor Gnatenko
e59ca38c4f [meta] Exclude unneeded files from crates.io (#117) 2019-03-23 17:01:19 +13:00
Igor Gnatenko
f91e23aa18 [libs] Update nix to 0.13 (#116) 2019-03-22 23:02:39 +13:00
Igor Gnatenko
22f5408bc0 Drop mktemp dependency (#115)
It is not used and depends on very very old version of uuid.

Signed-off-by: Igor Gnatenko <i.gnatenko.brain@gmail.com>
2019-03-12 00:49:17 +13:00
SpiralP
dbc52e012e [run] Always canonicalize input paths (#113)
A client may pass non-canonicalized paths into `watch`, and these are therefore not matched against the filters correctly. Thus, some events could call a Handler's `on_update` method even when an ignored file was changed.

See repro: https://github.com/SpiralP/rust-cargo-watch-test

This bug was introduced in aae5a216b0.
2019-03-04 11:40:35 +13:00
Félix Saparelli
867ef7bc3c 1.10.1 2019-02-18 21:27:57 +13:00
Félix Saparelli
4918b123d9 [run] Restore clearing of the screen on initial run 2019-02-18 21:26:21 +13:00
Josh Gao
6342f86c10 [errors] Fix infinite recursion in fmt::Display for Error (#111) 2019-02-15 17:02:07 +13:00
Félix Saparelli
8eb90738de [run] Move "once" logic to be entirely within handler 2019-01-27 15:51:49 +13:00
Félix Saparelli
4ceb70f6bf [docs] Fix logic inversion in on_manual doc comment 2019-01-27 10:24:24 +13:00
Félix Saparelli
b5afcd58ae 1.10.0 2019-01-26 18:21:52 +13:00
Félix Saparelli
8ffe20d545 [libs] Update other deps 2019-01-26 18:19:57 +13:00
Félix Saparelli
63afc013c9 [libs] Update Notify 2019-01-26 18:02:27 +13:00
Félix Saparelli
578ffa1deb [run] Fix outdated comment in ExecHandler 2019-01-26 17:42:36 +13:00
Félix Saparelli
5846b64020 [run] Further dry the ExecHandler 2019-01-26 17:32:48 +13:00
Félix Saparelli
3dd2f797ab [process] Remove some more unwrap 2019-01-26 17:26:33 +13:00
Félix Saparelli
6a23f77687 [run] Pass ops by reference when possible 2019-01-26 17:20:29 +13:00
Félix Saparelli
ede5505a6b [run] Dry spawning the child process 2019-01-26 17:16:07 +13:00
Félix Saparelli
5e44cafb94 [windows] Attempt to use tput to clear the screen
In some situations, this may clear the screen better than a call to
`cls`, and in all other cases `cls` is called anyway.

May help #99
2019-01-26 15:22:09 +13:00
Félix Saparelli
6f473bcd87 [meta] Run formatter 2019-01-26 15:15:27 +13:00
Félix Saparelli
3a6c6b36b2
Merge pull request #105 from watchexec/c-bindings
Library changes for #103, building on #104
2019-01-26 15:09:39 +13:00
Félix Saparelli
aae5a216b0 [api] Make watchexec take a Handler rather than a callback
Instead of special-casing the callback, which is the path least-taken,
switch the internals to a Handler model, where the default behaviour is
an implementation of a Handler, and external callers can implement their
own Handlers and pass them in.

While doing so, change all unwraps in run::run to returning Errs, and
expand the watchexec Error enum to accommodate. That should make it
easier to use as a library.

Also, differentiate between "manual" and "on update" runs. For now the
only manual run is the initial run, but this paves the way for e.g.
keyboard- or signal- triggered runs.
2019-01-26 14:45:13 +13:00
Félix Saparelli
ac3a4f0717 [pathop] Revert changes adding a time field 2019-01-26 14:40:30 +13:00
Félix Saparelli
b49bf74dd6 [meta] Revert .gitignore change 2019-01-26 09:16:43 +13:00
Félix Saparelli
65db0dfae1 [meta] Revert version bump 2019-01-26 09:16:02 +13:00
David Ziegler
7b3daeef9c Major changes for c bindings integration as third party crate (see project: github/InfinityMod/watchexec_c).
Changed version to 1.9.3 for third party crate compatibility detection.
2019-01-23 18:56:09 +01:00
Félix Saparelli
62d24168d6
Merge pull request #102 from Mange/fix-tty-output
Support running commands that allocate their own TTYs (*nix)
2019-01-19 18:48:43 +13:00
Magnus Bergmark
9fd25dd667
Use setsid() instead of setpgid()
This makes it possible to run commands that allocates TTYs and still
capture their output and reap the process on completion.

> setpgid — set process group ID for job control
> setsid — create session and set process group ID

With this in place, the `waitpid` call detects that the process has
exited even if the process allocated a TTY, and is able to unblock the
runner and continue.

Further, the output of the command is also shown in the controlling
terminal.
2019-01-17 13:21:27 +01:00
Félix Saparelli
e0f649d79a Update clap version out of lock file, fixes #76 2018-12-15 14:24:43 +13:00
Félix Saparelli
f6b16813aa Cfg off remaining items for a clean win/mac build
Closes #98
2018-10-05 20:56:06 +13:00
Félix Saparelli
cee1712c4f Link up glob syntax documentation 2018-09-29 13:40:06 +12:00
Félix Saparelli
ba23ed09f6 Fix tests 2018-09-09 16:16:40 +12:00
Félix Saparelli
dc712197c0 1.9.2: revert whitespace changes 2018-09-09 16:03:01 +12:00
242 changed files with 29585 additions and 3120 deletions

11
.cargo/config.toml Normal file
View file

@ -0,0 +1,11 @@
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
[target.armv7-unknown-linux-musleabihf]
linker = "arm-linux-musleabihf-gcc"
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[target.aarch64-unknown-linux-musl]
linker = "aarch64-linux-musl-gcc"

12
.clippy-lints Normal file
View file

@ -0,0 +1,12 @@
-W clippy::nursery
-W clippy::pedantic
-A clippy::module-name-repetitions
-A clippy::similar-names
-A clippy::cognitive-complexity
-A clippy::too-many-lines
-A clippy::missing-errors-doc
-A clippy::missing-panics-doc
-A clippy::default-trait-access
-A clippy::enum-glob-use
-A clippy::option-if-let-else
-A clippy::blocks-in-conditions

21
.editorconfig Normal file
View file

@ -0,0 +1,21 @@
root = true
[*]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[cli/tests/snapshots/*]
indent_style = space
trim_trailing_whitespace = false
[*.{md,ronn}]
indent_style = space
indent_size = 4
[*.{cff,yml}]
indent_size = 2
indent_style = space

3
.gitattributes vendored Normal file
View file

@ -0,0 +1,3 @@
Cargo.lock merge=binary
doc/watchexec.* merge=binary
completions/* merge=binary

17
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,17 @@
---
name: Bug report
about: Something is wrong
title: ''
labels: bug, need-info
assignees: ''
---
Please delete this template text before filing, but you _need_ to include the following:
- Watchexec's version
- The OS you're using
- A log with `-vvv --log-file` (if it has sensitive info you can email it at felix@passcod.name — do that _after_ filing so you can reference the issue ID)
- A sample command that you've run that has the issue
Thank you

View file

@ -0,0 +1,24 @@
---
name: Feature request
about: Something is missing
title: ''
labels: feature
assignees: ''
---
<!-- Please note that this project has a high threshold for changing default behaviour or breaking compatibility. If your feature or change can be done without breaking, present it that way. -->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
If proposing a new CLI option, option names you think would fit.
**Additional context**
Add any other context about the feature request here.

23
.github/ISSUE_TEMPLATE/regression.md vendored Normal file
View file

@ -0,0 +1,23 @@
---
name: Regression
about: Something changed unexpectedly
title: ''
labels: ''
assignees: ''
---
**What used to happen**
**What happens now**
**Details**
- Latest version that worked:
- Earliest version that doesn't: (don't sweat testing earlier versions if you don't remember or have time, your current version will do)
- OS:
- A debug log with `-vvv --log-file`:
```
```
<!-- You may truncate the log to just the part supporting your report if you're confident the rest is irrelevant. If it contains sensitive information (if you can't reduce/reproduce outside of work you'd rather remain private, you can either redact it or send it by email.) -->

50
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,50 @@
# Dependabot dependency version checks / updates
version: 2
updates:
- package-ecosystem: "github-actions"
# Workflow files stored in the
# default location of `.github/workflows`
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/cli"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/lib"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/events"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/signals"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/supervisor"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/filterer/ignore"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/filterer/globset"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/bosion"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/ignore-files"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/crates/project-origins"
schedule:
interval: "weekly"

62
.github/workflows/clippy.yml vendored Normal file
View file

@ -0,0 +1,62 @@
name: Clippy
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
tags-ignore:
- "*"
env:
CARGO_TERM_COLOR: always
CARGO_UNSTABLE_SPARSE_REGISTRY: "true"
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
jobs:
clippy:
strategy:
fail-fast: false
matrix:
platform:
- ubuntu
- windows
- macos
name: Clippy on ${{ matrix.platform }}
runs-on: "${{ matrix.platform }}-latest"
steps:
- uses: actions/checkout@v4
- name: Configure toolchain
run: |
rustup toolchain install stable --profile minimal --no-self-update --component clippy
rustup default stable
# https://github.com/actions/cache/issues/752
- if: ${{ runner.os == 'Windows' }}
name: Use GNU tar
shell: cmd
run: |
echo "Adding GNU tar to PATH"
echo C:\Program Files\Git\usr\bin>>"%GITHUB_PATH%"
- name: Configure caching
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Clippy
run: cargo clippy -- $(cat .clippy-lints | tr -d '\r' | xargs)
shell: bash

26
.github/workflows/dist-manifest.jq vendored Normal file
View file

@ -0,0 +1,26 @@
{
dist_version: "0.0.2",
releases: [{
app_name: "watchexec",
app_version: $version,
changelog_title: "CLI \($version)",
artifacts: [ $files | split("\n") | .[] | {
name: .,
kind: (if (. | test("[.](deb|rpm)$")) then "installer" else "executable-zip" end),
target_triples: (. | [capture("watchexec-[^-]+-(?<target>[^.]+)[.].+").target]),
assets: ([[
{
kind: "executable",
name: (if (. | test("windows")) then "watchexec.exe" else "watchexec" end),
path: "\(
capture("(?<dir>watchexec-[^-]+-[^.]+)[.].+").dir
)\(
if (. | test("windows")) then "\\watchexec.exe" else "/watchexec" end
)",
},
(if (. | test("[.](deb|rpm)$")) then null else {kind: "readme", name: "README.md"} end),
(if (. | test("[.](deb|rpm)$")) then null else {kind: "license", name: "LICENSE"} end)
][] | select(. != null)])
} ]
}]
}

325
.github/workflows/release-cli.yml vendored Normal file
View file

@ -0,0 +1,325 @@
name: CLI Release
on:
workflow_dispatch:
push:
tags:
- "v*.*.*"
env:
CARGO_TERM_COLOR: always
CARGO_UNSTABLE_SPARSE_REGISTRY: "true"
jobs:
info:
name: Gather info
runs-on: ubuntu-latest
outputs:
cli_version: ${{ steps.version.outputs.cli_version }}
steps:
- uses: actions/checkout@v4
- name: Extract version
id: version
shell: bash
run: |
set -euxo pipefail
version=$(grep -m1 -F 'version =' crates/cli/Cargo.toml | cut -d\" -f2)
if [[ -z "$version" ]]; then
echo "Error: no version :("
exit 1
fi
echo "cli_version=$version" >> $GITHUB_OUTPUT
build:
strategy:
matrix:
name:
- linux-amd64-gnu
- linux-amd64-musl
- linux-i686-musl
- linux-armhf-gnu
- linux-arm64-gnu
- linux-arm64-musl
- linux-s390x-gnu
- linux-ppc64le-gnu
- mac-x86-64
- mac-arm64
- windows-x86-64
include:
- name: linux-amd64-gnu
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
cross: false
experimental: false
- name: linux-amd64-musl
os: ubuntu-latest
target: x86_64-unknown-linux-musl
cross: true
experimental: false
- name: linux-i686-musl
os: ubuntu-latest
target: i686-unknown-linux-musl
cross: true
experimental: true
- name: linux-armhf-gnu
os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
cross: true
experimental: false
- name: linux-arm64-gnu
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
cross: true
experimental: false
- name: linux-arm64-musl
os: ubuntu-latest
target: aarch64-unknown-linux-musl
cross: true
experimental: true
- name: linux-s390x-gnu
os: ubuntu-latest
target: s390x-unknown-linux-gnu
cross: true
experimental: false
- name: linux-ppc64le-gnu
os: ubuntu-latest
target: powerpc64le-unknown-linux-gnu
cross: true
experimental: false
- name: mac-x86-64
os: macos-latest
target: x86_64-apple-darwin
cross: false
experimental: false
- name: mac-arm64
os: macos-latest
target: aarch64-apple-darwin
cross: true
experimental: false
- name: windows-x86-64
os: windows-latest
target: x86_64-pc-windows-msvc
cross: false
experimental: false
#- name: windows-arm64
# os: windows-latest
# target: aarch64-pc-windows-msvc
# cross: true
# experimental: true
name: Binaries for ${{ matrix.name }}
needs: info
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
env:
version: ${{ needs.info.outputs.cli_version }}
dst: watchexec-${{ needs.info.outputs.cli_version }}-${{ matrix.target }}
steps:
- uses: actions/checkout@v4
# https://github.com/actions/cache/issues/752
- if: ${{ runner.os == 'Windows' }}
name: Use GNU tar
shell: cmd
run: |
echo "Adding GNU tar to PATH"
echo C:\Program Files\Git\usr\bin>>"%GITHUB_PATH%"
- name: Configure caching
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-${{ matrix.target }}-
${{ runner.os }}-cargo-
- run: sudo apt update
if: startsWith(matrix.os, 'ubuntu-')
- name: Add musl tools
run: sudo apt install -y musl musl-dev musl-tools
if: endsWith(matrix.target, '-musl')
- name: Add aarch-gnu tools
run: sudo apt install -y gcc-aarch64-linux-gnu
if: startsWith(matrix.target, 'aarch64-unknown-linux')
- name: Add arm7hf-gnu tools
run: sudo apt install -y gcc-arm-linux-gnueabihf
if: startsWith(matrix.target, 'armv7-unknown-linux-gnueabihf')
- name: Add s390x-gnu tools
run: sudo apt install -y gcc-s390x-linux-gnu
if: startsWith(matrix.target, 's390x-unknown-linux-gnu')
- name: Add ppc64le-gnu tools
run: sudo apt install -y gcc-powerpc64le-linux-gnu
if: startsWith(matrix.target, 'powerpc64le-unknown-linux-gnu')
- name: Install cargo-deb
if: startsWith(matrix.name, 'linux-')
uses: taiki-e/install-action@v2
with:
tool: cargo-deb
- name: Install cargo-generate-rpm
if: startsWith(matrix.name, 'linux-')
uses: taiki-e/install-action@v2
with:
tool: cargo-generate-rpm
- name: Configure toolchain
run: |
rustup toolchain install --profile minimal --no-self-update stable
rustup default stable
rustup target add ${{ matrix.target }}
- name: Install cross
if: matrix.cross
uses: taiki-e/install-action@v2
with:
tool: cross
- name: Build
shell: bash
run: |
${{ matrix.cross && 'cross' || 'cargo' }} build \
-p watchexec-cli \
--release --locked \
--target ${{ matrix.target }}
- name: Make manpage
shell: bash
run: |
cargo run -p watchexec-cli \
${{ (!matrix.cross) && '--release --target' || '' }} \
${{ (!matrix.cross) && matrix.target || '' }} \
--locked -- --manual > doc/watchexec.1
- name: Make completions
shell: bash
run: |
bin/completions \
${{ (!matrix.cross) && '--release --target' || '' }} \
${{ (!matrix.cross) && matrix.target || '' }} \
--locked
- name: Package
shell: bash
run: |
set -euxo pipefail
ext=""
[[ "${{ matrix.name }}" == windows-* ]] && ext=".exe"
bin="target/${{ matrix.target }}/release/watchexec${ext}"
objcopy --compress-debug-sections "$bin" || true
mkdir "$dst"
mkdir -p "target/release"
cp "$bin" "target/release/" # workaround for cargo-deb silliness with targets
cp "$bin" "$dst/"
cp -r crates/cli/README.md LICENSE completions doc/{logo.svg,watchexec.1{,.*}} "$dst/"
- name: Archive (tar)
if: '! startsWith(matrix.name, ''windows-'')'
run: tar cavf "$dst.tar.xz" "$dst"
- name: Archive (deb)
if: startsWith(matrix.name, 'linux-')
run: cargo deb -p watchexec-cli --no-build --no-strip --target ${{ matrix.target }} --output "$dst.deb"
- name: Archive (rpm)
if: startsWith(matrix.name, 'linux-')
shell: bash
run: |
set -euxo pipefail
shopt -s globstar
cargo generate-rpm -p crates/cli --target "${{ matrix.target }}" --target-dir "target/${{ matrix.target }}"
mv target/**/*.rpm "$dst.rpm"
- name: Archive (zip)
if: startsWith(matrix.name, 'windows-')
shell: bash
run: 7z a "$dst.zip" "$dst"
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.name }}
retention-days: 1
path: |
watchexec-*.tar.xz
watchexec-*.tar.zst
watchexec-*.deb
watchexec-*.rpm
watchexec-*.zip
upload:
needs: [build, info]
name: Checksum and publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install b3sum
uses: taiki-e/install-action@v2
with:
tool: b3sum
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Dist manifest
run: |
jq -ncf .github/workflows/dist-manifest.jq \
--arg version "${{ needs.info.outputs.cli_version }}" \
--arg files "$(ls watchexec-*)" \
> dist-manifest.json
- name: Bulk checksums
run: |
b3sum watchexec-* | tee B3SUMS
sha512sum watchexec-* | tee SHA512SUMS
sha256sum watchexec-* | tee SHA256SUMS
- name: File checksums
run: |
for file in watchexec-*; do
b3sum --no-names $file > "$file.b3"
sha256sum $file | cut -d ' ' -f1 > "$file.sha256"
sha512sum $file | cut -d ' ' -f1 > "$file.sha512"
done
- uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564
with:
tag_name: v${{ needs.info.outputs.cli_version }}
name: CLI v${{ needs.info.outputs.cli_version }}
append_body: true
files: |
dist-manifest.json
watchexec-*.tar.xz
watchexec-*.tar.zst
watchexec-*.deb
watchexec-*.rpm
watchexec-*.zip
*SUMS
*.b3
*.sha*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

148
.github/workflows/tests.yml vendored Normal file
View file

@ -0,0 +1,148 @@
name: Test suite
on:
workflow_dispatch:
pull_request:
types:
- opened
- reopened
- synchronize
push:
branches:
- main
tags-ignore:
- "*"
env:
CARGO_TERM_COLOR: always
CARGO_UNSTABLE_SPARSE_REGISTRY: "true"
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
jobs:
test:
strategy:
fail-fast: false
matrix:
platform:
- macos
- ubuntu
- windows
name: Test ${{ matrix.platform }}
runs-on: "${{ matrix.platform }}-latest"
steps:
- uses: actions/checkout@v4
- name: Configure toolchain
run: |
rustup toolchain install --profile minimal --no-self-update stable
rustup default stable
# https://github.com/actions/cache/issues/752
- if: ${{ runner.os == 'Windows' }}
name: Use GNU tar
shell: cmd
run: |
echo "Adding GNU tar to PATH"
echo C:\Program Files\Git\usr\bin>>"%GITHUB_PATH%"
- name: Cargo caching
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-cargo-stable-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-stable-
${{ runner.os }}-cargo-
- name: Compilation caching
uses: actions/cache@v4
with:
path: target/
key: ${{ runner.os }}-target-stable-${{ hashFiles('**/Cargo.lock') }}
- name: Run test suite
run: cargo test
- name: Run watchexec-events integration tests
run: cargo test -p watchexec-events -F serde
- name: Check that CLI runs
run: cargo run -p watchexec-cli -- -1 echo
- name: Install coreutils on mac
if: ${{ matrix.platform == 'macos' }}
run: brew install coreutils
- name: Run watchexec integration tests (unix)
if: ${{ matrix.platform != 'windows' }}
run: crates/cli/run-tests.sh
shell: bash
env:
WATCHEXEC_BIN: target/debug/watchexec
- name: Run bosion integration tests
run: ./run-tests.sh
working-directory: crates/bosion
shell: bash
- name: Generate manpage
run: cargo run -p watchexec-cli -- --manual > doc/watchexec.1
- name: Check that manpage is up to date
run: git diff --exit-code -- doc/
- name: Generate completions
run: bin/completions
- name: Check that completions are up to date
run: git diff --exit-code -- completions/
cross-checks:
name: Checks only against select targets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure toolchain
run: |
rustup toolchain install --profile minimal --no-self-update stable
rustup default stable
sudo apt-get install -y musl-tools
rustup target add x86_64-unknown-linux-musl
- name: Install cross
uses: taiki-e/install-action@v2
with:
tool: cross
- name: Cargo caching
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-cargo-stable-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-stable-
${{ runner.os }}-cargo-
- run: cargo check --target x86_64-unknown-linux-musl
- run: cross check --target x86_64-unknown-freebsd
- run: cross check --target x86_64-unknown-netbsd
tests-pass:
if: always()
name: Tests pass
needs:
- test
- cross-checks
runs-on: ubuntu-latest
steps:
- uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

4
.gitignore vendored
View file

@ -1,3 +1,3 @@
target
*.tar.gz
*.zip
/watchexec-*
watchexec.*.log

1
.rustfmt.toml Normal file
View file

@ -0,0 +1 @@
hard_tabs = true

View file

@ -1,71 +0,0 @@
language: rust
cache: cargo
dist: trusty
sudo: false
addons:
apt:
packages:
- fakeroot
env:
global:
- PROJECT_NAME=watchexec
matrix:
include:
# Default test+release version
- os: osx
rust: stable
env: TARGET=x86_64-apple-darwin
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-gnu
# Extra targets for linux only
- os: linux
rust: stable
env: TARGET=i686-unknown-linux-musl
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-musl
# Minimum version advertised in readme
- os: linux
rust: 1.26.0
env: TARGET=x86_64-unknown-linux-gnu
- os: osx
rust: 1.26.0
env: TARGET=x86_64-apple-darwin
# Non-critical but interesting for upcoming Rust changes
- os: linux
rust: nightly
env: TARGET=x86_64-unknown-linux-gnu
- os: osx
rust: nightly
env: TARGET=x86_64-apple-darwin
allow_failures:
- rust: nightly
fast_finish: true
before_script: ci/before_script.sh
script: ci/script.sh
before_deploy: ci/before_deploy.sh
deploy:
provider: releases
api_key:
secure: sbV2K4G2SA78U6d8SNZKExenWnuv1MsJ/9ovDDH4ucnzkpJEWDV2iwPklcu5oaRj3Sz4jYuYXToqcbJmSjL5eXjlk8rh2sG2LWT+8Up3X1vxte2XFXko15I/613rD8E/qWfS9FqzmuhMX+gb4P7OwWvVUwtw0IIuSGfBW/TEgUTFUZnUmgdm5ra8VnV3CvmTbn8botxbkdAUvk4C0g7yqHjlV7v9xU+DEXz2Y820cAH8ulu1ZU3JBm+XfVzZ09kByeQ7wnvyRuE4RhVtKK8nKUy+2JF7HX5N+0Du8z9ZHosV6+uoUz9i2OecYzAvL8xKiSkeHBqTxIDTeM4lnnDmnm5LsJ4aEU6pBSuWhglmflTbtAN7rBfYgZGJ6je6Gem5bOcCDtGI7+2qjf00Jo7vbmyK6D6Y6yxwf3W0QnOZcXrn9BWZLMMgochIBlVTTM1zFodcprpdHo8iHNVms3A++WqLnp1O0L/55id59VITGJNafy2vmXU/nlQi2MO03s3SF3jdHT7rchYjJRAcGR79QtCLiL3CbYnaQJsDNviyMm1VC6hkst0tXB8t12v2ht5NU7NEN8E31jnnRLRnwr7LUFRgOzFVF0M5jSqs3eCLnYyI7gCMKL2qOZ2yxJuD9bKsVZDpVvUqnaj5ifE+TMYoONPrc9W1hTyfcND9MhCsM+g=
file_glob: true
file: ${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.*
skip_cleanup: true
on:
repo: watchexec/watchexec
tags: true
rust: stable
notifications:
email:
on_success: never

17
CITATION.cff Normal file
View file

@ -0,0 +1,17 @@
cff-version: 1.2.0
message: |
If you use this software, please cite it using these metadata.
title: "Watchexec: a tool to react to filesystem changes, and a crate ecosystem to power it"
version: "2.2.0"
date-released: 2024-10-14
repository-code: https://github.com/watchexec/watchexec
license: Apache-2.0
authors:
- family-names: Green
given-names: Matt
- family-names: Saparelli
given-names: Félix
orcid: https://orcid.org/0000-0002-2010-630X

122
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,122 @@
# Contribution guidebook
This is a fairly free-form project, with low contribution traffic.
Maintainers:
- Félix Saparelli (@passcod) (active)
- Matt Green (@mattgreen) (original author, mostly checked out)
There are a few anti goals:
- Calling watchexec is to be a **simple** exercise that remains intuitive. As a specific point, it
should not involve any piping or require xargs.
- Watchexec will not be tied to any particular ecosystem or language. Projects that themselves use
watchexec (the library) can be focused on a particular domain (for example Cargo Watch for Rust),
but watchexec itself will remain generic, usable for any purpose.
## Debugging
To enable verbose logging in tests, run with:
```console
$ env RUST_LOG=watchexec=trace,info RUST_TEST_THREADS=1 RUST_NOCAPTURE=1 cargo test --test testfile -- testname
```
To use [Tokio Console](https://github.com/tokio-rs/console):
1. Add `--cfg tokio_unstable` to your `RUSTFLAGS`.
2. Run the CLI with the `dev-console` feature.
## PR etiquette
- Maintainers are busy or may not have the bandwidth, be patient.
- Do _not_ change the version number in the PR.
- Do _not_ change Cargo.toml or other project metadata, unless specifically asked for, or if that's
the point of the PR (like adding a crates.io category).
Apart from that, welcome and thank you for your time!
## Releasing
```
cargo release -p crate-name --execute patch # or minor, major
```
When a CLI release is done, the [release notes](https://github.com/watchexec/watchexec/releases) should be edited with the changelog.
### Release order
Use this command to see the tree of workspace dependencies:
```console
$ cargo tree -p watchexec-cli | rg -F '(/' --color=never | sed 's/ v[0-9].*//'
```
## Overview
The architecture of watchexec is roughly:
- sources gather events
- events are debounced and filtered
- event(s) make it through the debounce/filters and trigger an "action"
- `on_action` handler is called, returning an `Outcome`
- outcome is processed into managing the command that watchexec is running
- outcome can also be to exit
- when a command is started, the `on_pre_spawn` and `on_post_spawn` handlers are called
- commands are also a source of events, so e.g. "command has finished" is handled by `on_action`
And this is the startup sequence:
- init config sets basic immutable facts about the runtime
- runtime starts:
- source workers start, and are passed their runtime config
- action worker starts, and is passed its runtime config
- (unless `--postpone` is given) a synthetic event is injected to kickstart things
## Guides
These are generic guides for implementing specific bits of functionality.
### Adding an event source
- add a worker for "sourcing" events. Looking at the [signal source
worker](https://github.com/watchexec/watchexec/blob/main/crates/lib/src/signal/source.rs) is
probably easiest to get started here.
- because we may not always want to enable this event source, and just to be flexible, add [runtime
config](https://github.com/watchexec/watchexec/blob/main/crates/lib/src/config.rs) for the source.
- for convenience, probably add [a method on the runtime
config](https://github.com/watchexec/watchexec/blob/main/crates/lib/src/config.rs) which
configures the most common usecase.
- because watchexec is reconfigurable, in the worker you'll need to react to config changes. Look at
how the [fs worker does it](https://github.com/watchexec/watchexec/blob/main/crates/lib/src/fs.rs)
for reference.
- you may need to [add to the event tag
enum](https://github.com/watchexec/watchexec/blob/main/crates/lib/src/event.rs).
- if you do, you should [add support to the "tagged
filterer"](https://github.com/watchexec/watchexec/blob/main/crates/filterer/tagged/src/parse.rs),
but this can be done in follow-up work.
### Process a new event in the CLI
- add an option to the
[args](https://github.com/watchexec/watchexec/blob/main/crates/cli/src/args.rs) if necessary
- add to the [runtime
config](https://github.com/watchexec/watchexec/blob/main/crates/cli/src/config/runtime.rs) when
the option is present
- process relevant events [in the action
handler](https://github.com/watchexec/watchexec/blob/main/crates/cli/src/config/runtime.rs)
---
vim: tw=100

4673
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,54 +1,43 @@
[package]
name = "watchexec"
version = "1.9.1"
authors = ["Matt Green <mattgreenrocks@gmail.com>"]
description = "Executes commands in response to file modifications"
documentation = "https://github.com/watchexec/watchexec"
homepage = "https://github.com/watchexec/watchexec"
repository = "https://github.com/watchexec/watchexec"
readme = "README.md"
keywords = ["watcher", "inotify", "fsevents", "kqueue"]
categories = ["command-line-utilities"]
license = "Apache-2.0"
[workspace]
resolver = "2"
members = [
"crates/lib",
"crates/cli",
"crates/events",
"crates/signals",
"crates/supervisor",
"crates/filterer/globset",
"crates/filterer/ignore",
"crates/bosion",
"crates/ignore-files",
"crates/project-origins",
]
[badges]
appveyor = { repository = "watchexec/watchexec" }
travis-ci = { repository = "watchexec/watchexec" }
[profile.dev]
panic = "abort"
[workspace.dependencies]
miette = "5.10.0"
tempfile = "3.8.0"
tracing-test = "0.2.4"
rand = "0.8"
uuid = "1.5.0"
[profile.release]
lto = true
panic = "abort"
debug = 1 # for stack traces
codegen-units = 1
strip = "symbols"
[dependencies]
glob = "0.2.11"
globset = "0.4.1"
lazy_static = "1.1.0"
log = "0.4.4"
notify = "4.0"
[profile.dev.build-override]
opt-level = 0
codegen-units = 1024
debug = false
debug-assertions = false
overflow-checks = false
incremental = false
[dev-dependencies]
mktemp = "0.3.1"
[dependencies.clap]
version = "2.26"
default-features = false
features = ["wrap_help"]
[dependencies.env_logger]
version = "0.5.12"
default-features = false
features = []
[target.'cfg(unix)'.dependencies]
nix = "0.11.0"
[target.'cfg(windows)'.dependencies]
winapi = "0.2.8"
kernel32-sys = "0.2.2"
[[bin]]
name = "watchexec"
doc = false
[profile.release.build-override]
opt-level = 0
codegen-units = 1024
debug = false
debug-assertions = false
overflow-checks = false
incremental = false

View file

@ -1,30 +0,0 @@
LATEST_TAG=$(shell git tag | tail -n1)
.PHONY: doc test
debug: src/* Cargo.toml
@cargo build
release: src/* Cargo.toml
@cargo build --release
clean:
@cargo clean
test:
@cargo test
doc: doc/watchexec.1.ronn
@ronn doc/watchexec.1.ronn
cargo-release:
@cargo publish
homebrew-release:
@brew bump-formula-pr --strict --url="https://github.com/mattgreen/watchexec/archive/$(LATEST_TAG).tar.gz" watchexec
install: release
@cp target/release/watchexec /usr/bin
uninstall:
@rm /usr/bin/watchexec

134
README.md
View file

@ -1,120 +1,84 @@
# watchexec
[![CI status on main branch](https://github.com/watchexec/watchexec/actions/workflows/tests.yml/badge.svg)](https://github.com/watchexec/watchexec/actions/workflows/tests.yml)
[![Build Status](https://badgen.net/travis/watchexec/watchexec/master)](https://travis-ci.org/watchexec/watchexec)
[![Build status](https://badgen.net/appveyor/ci/watchexec/watchexec/master)](https://ci.appveyor.com/project/watchexec/watchexec)
[![Crates.io status](https://badgen.net/crates/v/watchexec)](https://crates.io/crates/watchexec)
# Watchexec
Software development often involves running the same commands over and over. Boring!
`watchexec` is a **simple**, standalone tool that watches a path and runs a command whenever it detects modifications.
`watchexec` is a simple, standalone tool that watches a path and runs a command whenever it detects modifications.
Example use cases:
* Automatically run unit tests
* Run linters/syntax checkers
* Rebuild artifacts
## Features
* Simple invocation and use
* Runs on OS X, Linux and Windows
* Simple invocation and use, does not require a cryptic command line involving `xargs`
* Runs on OS X, Linux, and Windows
* Monitors current directory and all subdirectories for changes
* Uses most efficient event polling mechanism for your platform (except for [BSD](https://github.com/passcod/notify#todo))
* Coalesces multiple filesystem events into one, for editors that use swap/backup files during saving
* By default, uses `.gitignore` to determine which files to ignore notifications for
* Support for watching files with a specific extension
* Support for filtering/ignoring events based on glob patterns
* Launches child processes in a new process group
* Sets the following environment variables in the child process:
* If a single file changed (depending on the event type):
* `$WATCHEXEC_CREATED_PATH`, the path of the file that was created
* `$WATCHEXEC_REMOVED_PATH`, the path of the file that was removed
* `$WATCHEXEC_RENAMED_PATH`, the path of the file that was renamed
* `$WATCHEXEC_WRITTEN_PATH`, the path of the file that was modified
* `$WATCHEXEC_META_CHANGED_PATH`, the path of the file whose metadata changed
* If multiple files changed:
* `$WATCHEXEC_COMMON_PATH`, the longest common path of all of the files that triggered a change
* Optionally clears screen between executions
* Optionally restarts the command with every modification (good for servers)
* Does not require a language runtime
* Loads `.gitignore` and `.ignore` files
* Uses process groups to keep hold of forking programs
* Provides the paths that changed in environment variables or STDIN
* Does not require a language runtime, not tied to any particular language or ecosystem
* [And more!](./crates/cli/#features)
## Anti-Features
* Not tied to any particular language or ecosystem
* Does not require a cryptic command line involving `xargs`
## Quick start
## Usage Examples
Watch all JavaScript, CSS and HTML files in the current directory and all subdirectories for changes, running `npm run build` when a change is detected:
Watch all JavaScript, CSS and HTML files in the current directory and all subdirectories for changes, running `make` when a change is detected:
$ watchexec --exts js,css,html make
Call `make test` when any file changes in this directory/subdirectory, except for everything below `target`:
$ watchexec -i target make test
Call `ls -la` when any file changes in this directory/subdirectory:
$ watchexec -- ls -la
$ watchexec -e js,css,html npm run build
Call/restart `python server.py` when any Python file in the current directory (and all subdirectories) changes:
$ watchexec -e py -r python server.py
$ watchexec -r -e py -- python server.py
Call/restart `my_server` when any file in the current directory (and all subdirectories) changes, sending `SIGKILL` to stop the child process:
More usage examples: [in the CLI README](./crates/cli/#usage-examples)!
$ watchexec -r -s SIGKILL my_server
## Install
Send a SIGHUP to the child process upon changes (Note: with using `-n | --no-shell` here, we're executing `my_server` directly, instead of wrapping it in a shell:
<a href="https://repology.org/project/watchexec/versions"><img align="right" src="https://repology.org/badge/vertical-allrepos/watchexec.svg" alt="Packaging status"></a>
$ watchexec -n -s SIGHUP my_server
- With [your package manager](./doc/packages.md) for Arch, Debian, Homebrew, Nix, Scoop, Chocolatey…
- From binary with [Binstall](https://github.com/cargo-bins/cargo-binstall): `cargo binstall watchexec-cli`
- As [pre-built binary package from Github](https://github.com/watchexec/watchexec/releases/latest)
- From source with Cargo: `cargo install --locked watchexec-cli`
Run `make` when any file changes, using the `.gitignore` file in the current directory to filter:
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).
$ watchexec make
Run `make` when any file in `lib` or `src` changes:
## Augment
$ watchexec -w lib -w src make
Watchexec pairs well with:
## Installation
- [checkexec](https://github.com/kurtbuilds/checkexec): to run only when source files are newer than a target file
- [just](https://github.com/casey/just): a modern alternative to `make`
- [systemfd](https://github.com/mitsuhiko/systemfd): socket-passing in development
### Cargo
## Extend
watchexec requires Rust 1.26 or later. You can install it using cargo:
- [watchexec library](./crates/lib/): to create more specialised watchexec-powered tools.
- [watchexec-events](./crates/events/): event types for watchexec.
- [watchexec-signals](./crates/signals/): signal types for watchexec.
- [watchexec-supervisor](./crates/supervisor/): process lifecycle manager (the _exec_ part of watchexec).
- [clearscreen](https://github.com/watchexec/clearscreen): to clear the (terminal) screen on every platform.
- [command group](https://github.com/watchexec/command-group): to run commands in process groups.
- [ignore files](./crates/ignore-files/): to find, parse, and interpret ignore files.
- [project origins](./crates/project-origins/): to find the origin(s) directory of a project.
- [notify](https://github.com/notify-rs/notify): to respond to file modifications (third-party).
$ cargo install watchexec
### Downstreams
### OS X with Homebrew
Selected downstreams of watchexec and associated crates:
$ brew install watchexec
### Linux
For now, use the GitHub Releases tab to obtain the binary. PRs for packaging in unsupported distros are welcomed.
#### Debian
A deb package is available for amd64 architectures in the GitHub Releases.
#### Arch Linux
Available [on the AUR](https://aur.archlinux.org/packages/watchexec/):
$ yay -S watchexec
### Windows
Available [using scoop](https://scoop.sh/):
#> scoop install watchexec
Or just unzip the binary from the GitHub Releases.
## Building
Rust 1.26 or later is required.
## Credits
* [notify](https://github.com/passcod/notify) for doing most of the heavy-lifting
* [globset](https://crates.io/crates/globset) for super-fast glob matching
- [cargo watch](https://github.com/watchexec/cargo-watch): a specialised watcher for Rust/Cargo projects.
- [cargo lambda](https://github.com/cargo-lambda/cargo-lambda): a dev tool for Rust-powered AWS Lambda functions.
- [create-rust-app](https://create-rust-app.dev): a template for Rust+React web apps.
- [dotter](https://github.com/supercuber/dotter): a dotfile manager.
- [ghciwatch](https://github.com/mercurytechnologies/ghciwatch): a specialised watcher for Haskell projects.
- [tectonic](https://tectonic-typesetting.github.io/book/latest/): a TeX/LaTeX typesetting system.

View file

@ -1,44 +0,0 @@
environment:
global:
PROJECT_NAME: watchexec
matrix:
- TARGET: x86_64-pc-windows-gnu
CHANNEL: stable
# Install Rust and Cargo
# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml)
install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- if defined MSYS2_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS2_BITS%\bin
- rustc -V
- cargo -V
# ???
build: false
# Equivalent to Travis' `script` phase
test_script:
- cargo test --verbose
before_deploy:
# Generate artifacts for release
- cargo build --release
- mkdir staging
- copy target\release\watchexec.exe staging
- copy LICENSE staging\LICENSE.txt
- cd staging
- 7z a ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip *
- appveyor PushArtifact ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip
deploy:
description: 'Automatically deployed release'
artifact: /.*\.zip/
#auth_token:
# secure: OkayPFKJ16PAH15rWQBdEQDgd3tlNP/ZcssaunpK9AHU+t9zZlO5qvUAok1JT3lR
#provider: GitHub
provider: Webhook
url: https://example.com/goes-nowhere
on:
appveyor_repo_tag: true

7
bin/completions Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
cargo run -p watchexec-cli $* -- --completions bash > completions/bash
cargo run -p watchexec-cli $* -- --completions elvish > completions/elvish
cargo run -p watchexec-cli $* -- --completions fish > completions/fish
cargo run -p watchexec-cli $* -- --completions nu > completions/nu
cargo run -p watchexec-cli $* -- --completions powershell > completions/powershell
cargo run -p watchexec-cli $* -- --completions zsh > completions/zsh

10
bin/dates.mjs Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env node
const id = Math.floor(Math.random() * 100);
let n = 0;
const m = 5;
while (n < m) {
n += 1;
console.log(`[${id} : ${n}/${m}] ${new Date}`);
await new Promise(done => setTimeout(done, 2000));
}

3
bin/manpage Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
cargo run -p watchexec-cli -- --manual > doc/watchexec.1
pandoc doc/watchexec.1 -t markdown > doc/watchexec.1.md

View file

@ -1,44 +0,0 @@
#!/usr/bin/env bash
# Build script shamelessly stolen from ripgrep :)
cargo build --target $TARGET --release
build_dir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
out_dir=$(pwd)
name="${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}"
mkdir "$build_dir/$name"
cp target/$TARGET/release/watchexec "$build_dir/$name/"
cp {doc/watchexec.1,LICENSE} "$build_dir/$name/"
pushd $build_dir
tar cvf "$out_dir/$name.tar" *
popd
gzip -f9 "$name.tar"
if [[ "$TARGET" == "x86_64-unknown-linux-gnu" ]]; then
mkdir -p "$build_dir/deb/$name"
pushd "$build_dir/deb/$name"
mkdir -p DEBIAN usr/bin usr/share/man/man1
cp "../../$name/watchexec" usr/bin/
cp "../../$name/watchexec.1" usr/share/man/man1/
cat <<CONTROL > DEBIAN/control
Package: watchexec
Version: ${TRAVIS_TAG}
Architecture: amd64
Maintainer: Félix Saparelli <aur@passcod.name>
Installed-Size: $(du -d1 usr | tail -n1 | cut -d\t -f1)
Homepage: https://github.com/watchexec/watchexec
Description: Executes commands in response to file modifications.
Software development often involves running the same commands over and over. Boring!
Watchexec is a simple, standalone tool that watches a path and runs a command whenever it detects modifications.
CONTROL
cd ..
fakeroot dpkg -b "$name"
mv "$name.deb" "$out_dir/"
popd
fi

View file

@ -1,4 +0,0 @@
#!/usr/bin/env bash
rustup target add $TARGET
cargo clean --target $TARGET --verbose

View file

@ -1,4 +0,0 @@
#!/usr/bin/env bash
cargo build --target $TARGET --verbose
cargo test --target $TARGET --verbose

230
completions/bash Normal file
View file

@ -0,0 +1,230 @@
_watchexec() {
local i cur prev opts cmd
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
cmd=""
opts=""
for i in ${COMP_WORDS[@]}
do
case "${cmd},${i}" in
",$1")
cmd="watchexec"
;;
*)
;;
esac
done
case "${cmd}" in
watchexec)
opts="-w -W -F -c -o -r -s -d -p -n -E -1 -N -q -e -f -j -i -v -h -V --watch --watch-non-recursive --watch-file --clear --on-busy-update --restart --signal --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-environment --emit-events-to --only-emit-events --env --no-process-group --wrap-process --notify --color --timings --quiet --bell --project-origin --workdir --exts --filter --filter-file --filter-prog --ignore --ignore-file --fs-events --no-meta --print-events --manual --completions --verbose --log-file --help --version [COMMAND]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--watch)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-w)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--watch-non-recursive)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-W)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--watch-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-F)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--clear)
COMPREPLY=($(compgen -W "clear reset" -- "${cur}"))
return 0
;;
-c)
COMPREPLY=($(compgen -W "clear reset" -- "${cur}"))
return 0
;;
--on-busy-update)
COMPREPLY=($(compgen -W "queue do-nothing restart signal" -- "${cur}"))
return 0
;;
-o)
COMPREPLY=($(compgen -W "queue do-nothing restart signal" -- "${cur}"))
return 0
;;
--signal)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-s)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--stop-signal)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--stop-timeout)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--map-signal)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--debounce)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-d)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--delay-run)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--poll)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--shell)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--emit-events-to)
COMPREPLY=($(compgen -W "environment stdio file json-stdio json-file none" -- "${cur}"))
return 0
;;
--env)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-E)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--wrap-process)
COMPREPLY=($(compgen -W "group session none" -- "${cur}"))
return 0
;;
--color)
COMPREPLY=($(compgen -W "auto always never" -- "${cur}"))
return 0
;;
--project-origin)
COMPREPLY=()
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o plusdirs
fi
return 0
;;
--workdir)
COMPREPLY=()
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o plusdirs
fi
return 0
;;
--exts)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-e)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--filter)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-f)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--filter-file)
local oldifs
if [ -n "${IFS+x}" ]; then
oldifs="$IFS"
fi
IFS=$'\n'
COMPREPLY=($(compgen -f "${cur}"))
if [ -n "${oldifs+x}" ]; then
IFS="$oldifs"
fi
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o filenames
fi
return 0
;;
--filter-prog)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-j)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--ignore)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-i)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--ignore-file)
local oldifs
if [ -n "${IFS+x}" ]; then
oldifs="$IFS"
fi
IFS=$'\n'
COMPREPLY=($(compgen -f "${cur}"))
if [ -n "${oldifs+x}" ]; then
IFS="$oldifs"
fi
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o filenames
fi
return 0
;;
--fs-events)
COMPREPLY=($(compgen -W "access create remove rename modify metadata" -- "${cur}"))
return 0
;;
--completions)
COMPREPLY=($(compgen -W "bash elvish fish nu powershell zsh" -- "${cur}"))
return 0
;;
--log-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
esac
}
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
complete -F _watchexec -o nosort -o bashdefault -o default watchexec
else
complete -F _watchexec -o bashdefault -o default watchexec
fi

95
completions/elvish Normal file
View file

@ -0,0 +1,95 @@
use builtin;
use str;
set edit:completion:arg-completer[watchexec] = {|@words|
fn spaces {|n|
builtin:repeat $n ' ' | str:join ''
}
fn cand {|text desc|
edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
}
var command = 'watchexec'
for word $words[1..-1] {
if (str:has-prefix $word '-') {
break
}
set command = $command';'$word
}
var completions = [
&'watchexec'= {
cand -w 'Watch a specific file or directory'
cand --watch 'Watch a specific file or directory'
cand -W 'Watch a specific directory, non-recursively'
cand --watch-non-recursive 'Watch a specific directory, non-recursively'
cand -F 'Watch files and directories from a file'
cand --watch-file 'Watch files and directories from a file'
cand -c 'Clear screen before running command'
cand --clear 'Clear screen before running command'
cand -o 'What to do when receiving events while the command is running'
cand --on-busy-update 'What to do when receiving events while the command is running'
cand -s 'Send a signal to the process when it''s still running'
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'
cand --poll 'Poll for filesystem changes'
cand --shell 'Use a different shell'
cand --emit-events-to 'Configure event emission'
cand -E 'Add env vars to the command'
cand --env 'Add env vars to the command'
cand --wrap-process 'Configure how the process is wrapped'
cand --color 'When to use terminal colours'
cand --project-origin 'Set the project origin'
cand --workdir 'Set the working directory'
cand -e 'Filename extensions to filter to'
cand --exts 'Filename extensions to filter to'
cand -f 'Filename patterns to filter to'
cand --filter 'Filename patterns to filter to'
cand --filter-file 'Files to load filters from'
cand -j '[experimental] Filter programs'
cand --filter-prog '[experimental] Filter programs'
cand -i 'Filename patterns to filter out'
cand --ignore 'Filename patterns to filter out'
cand --ignore-file 'Files to load ignores from'
cand --fs-events 'Filesystem events to filter to'
cand --completions 'Generate a shell completions script'
cand --log-file 'Write diagnostic logs to a file'
cand -r 'Restart the process if it''s still running'
cand --restart 'Restart the process if it''s still running'
cand --stdin-quit 'Exit when stdin closes'
cand --no-vcs-ignore 'Don''t load gitignores'
cand --no-project-ignore 'Don''t load project-local ignores'
cand --no-global-ignore 'Don''t load global ignores'
cand --no-default-ignore 'Don''t use internal default ignores'
cand --no-discover-ignore 'Don''t discover ignore files at all'
cand --ignore-nothing 'Don''t ignore anything at all'
cand -p 'Wait until first change before running command'
cand --postpone 'Wait until first change before running command'
cand -n 'Shorthand for ''--shell=none'''
cand --no-environment 'Deprecated shorthand for ''--emit-events=none'''
cand --only-emit-events 'Only emit events to stdout, run no commands'
cand --no-process-group 'Don''t use a process group'
cand -1 'Testing only: exit Watchexec after the first run'
cand -N 'Alert when commands start and end'
cand --notify 'Alert when commands start and end'
cand --timings 'Print how long the command took to run'
cand -q 'Don''t print starting and stopping messages'
cand --quiet 'Don''t print starting and stopping messages'
cand --bell 'Ring the terminal bell on command completion'
cand --no-meta 'Don''t emit fs events for metadata changes'
cand --print-events 'Print events that trigger actions'
cand --manual 'Show the manual page'
cand -v 'Set diagnostic log level'
cand --verbose 'Set diagnostic log level'
cand -h 'Print help (see more with ''--help'')'
cand --help 'Print help (see more with ''--help'')'
cand -V 'Print version'
cand --version 'Print version'
}
]
$completions[$command]
}

52
completions/fish Normal file
View file

@ -0,0 +1,52 @@
complete -c watchexec -s w -l watch -d 'Watch a specific file or directory' -r -F
complete -c watchexec -s W -l watch-non-recursive -d 'Watch a specific directory, non-recursively' -r -F
complete -c watchexec -s F -l watch-file -d 'Watch files and directories from a file' -r -F
complete -c watchexec -s c -l clear -d 'Clear screen before running command' -r -f -a "{clear\t'',reset\t''}"
complete -c watchexec -s o -l on-busy-update -d 'What to do when receiving events while the command is running' -r -f -a "{queue\t'',do-nothing\t'',restart\t'',signal\t''}"
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
complete -c watchexec -l shell -d 'Use a different shell' -r
complete -c watchexec -l emit-events-to -d 'Configure event emission' -r -f -a "{environment\t'',stdio\t'',file\t'',json-stdio\t'',json-file\t'',none\t''}"
complete -c watchexec -s E -l env -d 'Add env vars to the command' -r
complete -c watchexec -l wrap-process -d 'Configure how the process is wrapped' -r -f -a "{group\t'',session\t'',none\t''}"
complete -c watchexec -l color -d 'When to use terminal colours' -r -f -a "{auto\t'',always\t'',never\t''}"
complete -c watchexec -l project-origin -d 'Set the project origin' -r -f -a "(__fish_complete_directories)"
complete -c watchexec -l workdir -d 'Set the working directory' -r -f -a "(__fish_complete_directories)"
complete -c watchexec -s e -l exts -d 'Filename extensions to filter to' -r
complete -c watchexec -s f -l filter -d 'Filename patterns to filter to' -r
complete -c watchexec -l filter-file -d 'Files to load filters from' -r -F
complete -c watchexec -s j -l filter-prog -d '[experimental] Filter programs' -r
complete -c watchexec -s i -l ignore -d 'Filename patterns to filter out' -r
complete -c watchexec -l ignore-file -d 'Files to load ignores from' -r -F
complete -c watchexec -l fs-events -d 'Filesystem events to filter to' -r -f -a "{access\t'',create\t'',remove\t'',rename\t'',modify\t'',metadata\t''}"
complete -c watchexec -l completions -d 'Generate a shell completions script' -r -f -a "{bash\t'',elvish\t'',fish\t'',nu\t'',powershell\t'',zsh\t''}"
complete -c watchexec -l log-file -d 'Write diagnostic logs to a file' -r -F
complete -c watchexec -s r -l restart -d 'Restart the process if it\'s still running'
complete -c watchexec -l stdin-quit -d 'Exit when stdin closes'
complete -c watchexec -l no-vcs-ignore -d 'Don\'t load gitignores'
complete -c watchexec -l no-project-ignore -d 'Don\'t load project-local ignores'
complete -c watchexec -l no-global-ignore -d 'Don\'t load global ignores'
complete -c watchexec -l no-default-ignore -d 'Don\'t use internal default ignores'
complete -c watchexec -l no-discover-ignore -d 'Don\'t discover ignore files at all'
complete -c watchexec -l ignore-nothing -d 'Don\'t ignore anything at all'
complete -c watchexec -s p -l postpone -d 'Wait until first change before running command'
complete -c watchexec -s n -d 'Shorthand for \'--shell=none\''
complete -c watchexec -l no-environment -d 'Deprecated shorthand for \'--emit-events=none\''
complete -c watchexec -l only-emit-events -d 'Only emit events to stdout, run no commands'
complete -c watchexec -l no-process-group -d 'Don\'t use a process group'
complete -c watchexec -s 1 -d 'Testing only: exit Watchexec after the first run'
complete -c watchexec -s N -l notify -d 'Alert when commands start and end'
complete -c watchexec -l timings -d 'Print how long the command took to run'
complete -c watchexec -s q -l quiet -d 'Don\'t print starting and stopping messages'
complete -c watchexec -l bell -d 'Ring the terminal bell on command completion'
complete -c watchexec -l no-meta -d 'Don\'t emit fs events for metadata changes'
complete -c watchexec -l print-events -d 'Print events that trigger actions'
complete -c watchexec -l manual -d 'Show the manual page'
complete -c watchexec -s v -l verbose -d 'Set diagnostic log level'
complete -c watchexec -s h -l help -d 'Print help (see more with \'--help\')'
complete -c watchexec -s V -l version -d 'Print version'

90
completions/nu Normal file
View file

@ -0,0 +1,90 @@
module completions {
def "nu-complete watchexec screen_clear" [] {
[ "clear" "reset" ]
}
def "nu-complete watchexec on_busy_update" [] {
[ "queue" "do-nothing" "restart" "signal" ]
}
def "nu-complete watchexec emit_events_to" [] {
[ "environment" "stdio" "file" "json-stdio" "json-file" "none" ]
}
def "nu-complete watchexec wrap_process" [] {
[ "group" "session" "none" ]
}
def "nu-complete watchexec color" [] {
[ "auto" "always" "never" ]
}
def "nu-complete watchexec filter_fs_events" [] {
[ "access" "create" "remove" "rename" "modify" "metadata" ]
}
def "nu-complete watchexec completions" [] {
[ "bash" "elvish" "fish" "nu" "powershell" "zsh" ]
}
# Execute commands when watched files change
export extern watchexec [
...command: string # Command to run on changes
--watch(-w): string # Watch a specific file or directory
--watch-non-recursive(-W): string # Watch a specific directory, non-recursively
--watch-file(-F): string # Watch files and directories from a file
--clear(-c): string@"nu-complete watchexec screen_clear" # Clear screen before running command
--on-busy-update(-o): string@"nu-complete watchexec on_busy_update" # What to do when receiving events while the command is running
--restart(-r) # Restart the process if it's still running
--signal(-s): string # Send a signal to the process when it's still running
--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
--no-project-ignore # Don't load project-local ignores
--no-global-ignore # Don't load global ignores
--no-default-ignore # Don't use internal default ignores
--no-discover-ignore # Don't discover ignore files at all
--ignore-nothing # Don't ignore anything at all
--postpone(-p) # Wait until first change before running command
--delay-run: string # Sleep before running the command
--poll: string # Poll for filesystem changes
--shell: string # Use a different shell
-n # Shorthand for '--shell=none'
--no-environment # Deprecated shorthand for '--emit-events=none'
--emit-events-to: string@"nu-complete watchexec emit_events_to" # Configure event emission
--only-emit-events # Only emit events to stdout, run no commands
--env(-E): string # Add env vars to the command
--no-process-group # Don't use a process group
--wrap-process: string@"nu-complete watchexec wrap_process" # Configure how the process is wrapped
-1 # Testing only: exit Watchexec after the first run
--notify(-N) # Alert when commands start and end
--color: string@"nu-complete watchexec color" # When to use terminal colours
--timings # Print how long the command took to run
--quiet(-q) # Don't print starting and stopping messages
--bell # Ring the terminal bell on command completion
--project-origin: string # Set the project origin
--workdir: string # Set the working directory
--exts(-e): string # Filename extensions to filter to
--filter(-f): string # Filename patterns to filter to
--filter-file: string # Files to load filters from
--filter-prog(-j): string # [experimental] Filter programs
--ignore(-i): string # Filename patterns to filter out
--ignore-file: string # Files to load ignores from
--fs-events: string@"nu-complete watchexec filter_fs_events" # Filesystem events to filter to
--no-meta # Don't emit fs events for metadata changes
--print-events # Print events that trigger actions
--manual # Show the manual page
--completions: string@"nu-complete watchexec completions" # Generate a shell completions script
--verbose(-v) # Set diagnostic log level
--log-file: string # Write diagnostic logs to a file
--help(-h) # Print help (see more with '--help')
--version(-V) # Print version
]
}
export use completions *

101
completions/powershell Normal file
View file

@ -0,0 +1,101 @@
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$commandElements = $commandAst.CommandElements
$command = @(
'watchexec'
for ($i = 1; $i -lt $commandElements.Count; $i++) {
$element = $commandElements[$i]
if ($element -isnot [StringConstantExpressionAst] -or
$element.StringConstantType -ne [StringConstantType]::BareWord -or
$element.Value.StartsWith('-') -or
$element.Value -eq $wordToComplete) {
break
}
$element.Value
}) -join ';'
$completions = @(switch ($command) {
'watchexec' {
[CompletionResult]::new('-w', '-w', [CompletionResultType]::ParameterName, 'Watch a specific file or directory')
[CompletionResult]::new('--watch', '--watch', [CompletionResultType]::ParameterName, 'Watch a specific file or directory')
[CompletionResult]::new('-W', '-W ', [CompletionResultType]::ParameterName, 'Watch a specific directory, non-recursively')
[CompletionResult]::new('--watch-non-recursive', '--watch-non-recursive', [CompletionResultType]::ParameterName, 'Watch a specific directory, non-recursively')
[CompletionResult]::new('-F', '-F ', [CompletionResultType]::ParameterName, 'Watch files and directories from a file')
[CompletionResult]::new('--watch-file', '--watch-file', [CompletionResultType]::ParameterName, 'Watch files and directories from a file')
[CompletionResult]::new('-c', '-c', [CompletionResultType]::ParameterName, 'Clear screen before running command')
[CompletionResult]::new('--clear', '--clear', [CompletionResultType]::ParameterName, 'Clear screen before running command')
[CompletionResult]::new('-o', '-o', [CompletionResultType]::ParameterName, 'What to do when receiving events while the command is running')
[CompletionResult]::new('--on-busy-update', '--on-busy-update', [CompletionResultType]::ParameterName, 'What to do when receiving events while the command is running')
[CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'Send a signal to the process when it''s still running')
[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')
[CompletionResult]::new('--poll', '--poll', [CompletionResultType]::ParameterName, 'Poll for filesystem changes')
[CompletionResult]::new('--shell', '--shell', [CompletionResultType]::ParameterName, 'Use a different shell')
[CompletionResult]::new('--emit-events-to', '--emit-events-to', [CompletionResultType]::ParameterName, 'Configure event emission')
[CompletionResult]::new('-E', '-E ', [CompletionResultType]::ParameterName, 'Add env vars to the command')
[CompletionResult]::new('--env', '--env', [CompletionResultType]::ParameterName, 'Add env vars to the command')
[CompletionResult]::new('--wrap-process', '--wrap-process', [CompletionResultType]::ParameterName, 'Configure how the process is wrapped')
[CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'When to use terminal colours')
[CompletionResult]::new('--project-origin', '--project-origin', [CompletionResultType]::ParameterName, 'Set the project origin')
[CompletionResult]::new('--workdir', '--workdir', [CompletionResultType]::ParameterName, 'Set the working directory')
[CompletionResult]::new('-e', '-e', [CompletionResultType]::ParameterName, 'Filename extensions to filter to')
[CompletionResult]::new('--exts', '--exts', [CompletionResultType]::ParameterName, 'Filename extensions to filter to')
[CompletionResult]::new('-f', '-f', [CompletionResultType]::ParameterName, 'Filename patterns to filter to')
[CompletionResult]::new('--filter', '--filter', [CompletionResultType]::ParameterName, 'Filename patterns to filter to')
[CompletionResult]::new('--filter-file', '--filter-file', [CompletionResultType]::ParameterName, 'Files to load filters from')
[CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, '[experimental] Filter programs')
[CompletionResult]::new('--filter-prog', '--filter-prog', [CompletionResultType]::ParameterName, '[experimental] Filter programs')
[CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'Filename patterns to filter out')
[CompletionResult]::new('--ignore', '--ignore', [CompletionResultType]::ParameterName, 'Filename patterns to filter out')
[CompletionResult]::new('--ignore-file', '--ignore-file', [CompletionResultType]::ParameterName, 'Files to load ignores from')
[CompletionResult]::new('--fs-events', '--fs-events', [CompletionResultType]::ParameterName, 'Filesystem events to filter to')
[CompletionResult]::new('--completions', '--completions', [CompletionResultType]::ParameterName, 'Generate a shell completions script')
[CompletionResult]::new('--log-file', '--log-file', [CompletionResultType]::ParameterName, 'Write diagnostic logs to a file')
[CompletionResult]::new('-r', '-r', [CompletionResultType]::ParameterName, 'Restart the process if it''s still running')
[CompletionResult]::new('--restart', '--restart', [CompletionResultType]::ParameterName, 'Restart the process if it''s still running')
[CompletionResult]::new('--stdin-quit', '--stdin-quit', [CompletionResultType]::ParameterName, 'Exit when stdin closes')
[CompletionResult]::new('--no-vcs-ignore', '--no-vcs-ignore', [CompletionResultType]::ParameterName, 'Don''t load gitignores')
[CompletionResult]::new('--no-project-ignore', '--no-project-ignore', [CompletionResultType]::ParameterName, 'Don''t load project-local ignores')
[CompletionResult]::new('--no-global-ignore', '--no-global-ignore', [CompletionResultType]::ParameterName, 'Don''t load global ignores')
[CompletionResult]::new('--no-default-ignore', '--no-default-ignore', [CompletionResultType]::ParameterName, 'Don''t use internal default ignores')
[CompletionResult]::new('--no-discover-ignore', '--no-discover-ignore', [CompletionResultType]::ParameterName, 'Don''t discover ignore files at all')
[CompletionResult]::new('--ignore-nothing', '--ignore-nothing', [CompletionResultType]::ParameterName, 'Don''t ignore anything at all')
[CompletionResult]::new('-p', '-p', [CompletionResultType]::ParameterName, 'Wait until first change before running command')
[CompletionResult]::new('--postpone', '--postpone', [CompletionResultType]::ParameterName, 'Wait until first change before running command')
[CompletionResult]::new('-n', '-n', [CompletionResultType]::ParameterName, 'Shorthand for ''--shell=none''')
[CompletionResult]::new('--no-environment', '--no-environment', [CompletionResultType]::ParameterName, 'Deprecated shorthand for ''--emit-events=none''')
[CompletionResult]::new('--only-emit-events', '--only-emit-events', [CompletionResultType]::ParameterName, 'Only emit events to stdout, run no commands')
[CompletionResult]::new('--no-process-group', '--no-process-group', [CompletionResultType]::ParameterName, 'Don''t use a process group')
[CompletionResult]::new('-1', '-1', [CompletionResultType]::ParameterName, 'Testing only: exit Watchexec after the first run')
[CompletionResult]::new('-N', '-N ', [CompletionResultType]::ParameterName, 'Alert when commands start and end')
[CompletionResult]::new('--notify', '--notify', [CompletionResultType]::ParameterName, 'Alert when commands start and end')
[CompletionResult]::new('--timings', '--timings', [CompletionResultType]::ParameterName, 'Print how long the command took to run')
[CompletionResult]::new('-q', '-q', [CompletionResultType]::ParameterName, 'Don''t print starting and stopping messages')
[CompletionResult]::new('--quiet', '--quiet', [CompletionResultType]::ParameterName, 'Don''t print starting and stopping messages')
[CompletionResult]::new('--bell', '--bell', [CompletionResultType]::ParameterName, 'Ring the terminal bell on command completion')
[CompletionResult]::new('--no-meta', '--no-meta', [CompletionResultType]::ParameterName, 'Don''t emit fs events for metadata changes')
[CompletionResult]::new('--print-events', '--print-events', [CompletionResultType]::ParameterName, 'Print events that trigger actions')
[CompletionResult]::new('--manual', '--manual', [CompletionResultType]::ParameterName, 'Show the manual page')
[CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'Set diagnostic log level')
[CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'Set diagnostic log level')
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
break
}
})
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
Sort-Object -Property ListItemText
}

103
completions/zsh Normal file
View file

@ -0,0 +1,103 @@
#compdef watchexec
autoload -U is-at-least
_watchexec() {
typeset -A opt_args
typeset -a _arguments_options
local ret=1
if is-at-least 5.2; then
_arguments_options=(-s -S -C)
else
_arguments_options=(-s -C)
fi
local context curcontext="$curcontext" state line
_arguments "${_arguments_options[@]}" : \
'*-w+[Watch a specific file or directory]:PATH:_files' \
'*--watch=[Watch a specific file or directory]:PATH:_files' \
'*-W+[Watch a specific directory, non-recursively]:PATH:_files' \
'*--watch-non-recursive=[Watch a specific directory, non-recursively]:PATH:_files' \
'-F+[Watch files and directories from a file]:PATH:_files' \
'--watch-file=[Watch files and directories from a file]:PATH:_files' \
'-c+[Clear screen before running command]' \
'--clear=[Clear screen before running command]' \
'-o+[What to do when receiving events while the command is running]:MODE:(queue do-nothing restart signal)' \
'--on-busy-update=[What to do when receiving events while the command is running]:MODE:(queue do-nothing restart signal)' \
'(-r --restart)-s+[Send a signal to the process when it'\''s still running]:SIGNAL: ' \
'(-r --restart)--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: ' \
'--poll=[Poll for filesystem changes]' \
'--shell=[Use a different shell]:SHELL: ' \
'--emit-events-to=[Configure event emission]:MODE:(environment stdio file json-stdio json-file none)' \
'*-E+[Add env vars to the command]:KEY=VALUE: ' \
'*--env=[Add env vars to the command]:KEY=VALUE: ' \
'--wrap-process=[Configure how the process is wrapped]:MODE:(group session none)' \
'--color=[When to use terminal colours]:MODE:(auto always never)' \
'--project-origin=[Set the project origin]:DIRECTORY:_files -/' \
'--workdir=[Set the working directory]:DIRECTORY:_files -/' \
'*-e+[Filename extensions to filter to]:EXTENSIONS: ' \
'*--exts=[Filename extensions to filter to]:EXTENSIONS: ' \
'*-f+[Filename patterns to filter to]:PATTERN: ' \
'*--filter=[Filename patterns to filter to]:PATTERN: ' \
'*--filter-file=[Files to load filters from]:PATH:_files' \
'*-j+[\[experimental\] Filter programs]:EXPRESSION: ' \
'*--filter-prog=[\[experimental\] Filter programs]:EXPRESSION: ' \
'*-i+[Filename patterns to filter out]:PATTERN: ' \
'*--ignore=[Filename patterns to filter out]:PATTERN: ' \
'*--ignore-file=[Files to load ignores from]:PATH:_files' \
'*--fs-events=[Filesystem events to filter to]:EVENTS:(access create remove rename modify metadata)' \
'(--manual)--completions=[Generate a shell completions script]:COMPLETIONS:(bash elvish fish nu powershell zsh)' \
'--log-file=[Write diagnostic logs to a file]' \
'(-o --on-busy-update)-r[Restart the process if it'\''s still running]' \
'(-o --on-busy-update)--restart[Restart the process if it'\''s still running]' \
'--stdin-quit[Exit when stdin closes]' \
'--no-vcs-ignore[Don'\''t load gitignores]' \
'--no-project-ignore[Don'\''t load project-local ignores]' \
'--no-global-ignore[Don'\''t load global ignores]' \
'--no-default-ignore[Don'\''t use internal default ignores]' \
'--no-discover-ignore[Don'\''t discover ignore files at all]' \
'--ignore-nothing[Don'\''t ignore anything at all]' \
'-p[Wait until first change before running command]' \
'--postpone[Wait until first change before running command]' \
'-n[Shorthand for '\''--shell=none'\'']' \
'--no-environment[Deprecated shorthand for '\''--emit-events=none'\'']' \
'(--completions --manual)--only-emit-events[Only emit events to stdout, run no commands]' \
'--no-process-group[Don'\''t use a process group]' \
'-1[Testing only\: exit Watchexec after the first run]' \
'-N[Alert when commands start and end]' \
'--notify[Alert when commands start and end]' \
'--timings[Print how long the command took to run]' \
'-q[Don'\''t print starting and stopping messages]' \
'--quiet[Don'\''t print starting and stopping messages]' \
'--bell[Ring the terminal bell on command completion]' \
'(--fs-events)--no-meta[Don'\''t emit fs events for metadata changes]' \
'--print-events[Print events that trigger actions]' \
'(--completions)--manual[Show the manual page]' \
'*-v[Set diagnostic log level]' \
'*--verbose[Set diagnostic log level]' \
'-h[Print help (see more with '\''--help'\'')]' \
'--help[Print help (see more with '\''--help'\'')]' \
'-V[Print version]' \
'--version[Print version]' \
'*::command -- Command to run on changes:_cmdstring' \
&& ret=0
}
(( $+functions[_watchexec_commands] )) ||
_watchexec_commands() {
local commands; commands=()
_describe -t commands 'watchexec commands' commands "$@"
}
if [ "$funcstack[1]" = "_watchexec" ]; then
_watchexec "$@"
else
compdef _watchexec watchexec
fi

View file

@ -0,0 +1,27 @@
# Changelog
## Next (YYYY-MM-DD)
## v1.1.1 (2024-10-14)
- Deps: gix 0.66
## v1.1.0 (2024-05-16)
- Add `git-describe` support (#832, by @lu-zero)
## v1.0.3 (2024-04-20)
- Deps: gix 0.62
## v1.0.2 (2023-11-26)
- Deps: upgrade to gix 0.55
## v1.0.1 (2023-07-02)
- Deps: upgrade to gix 0.44
## v1.0.0 (2023-03-05)
- Initial release.

40
crates/bosion/Cargo.toml Normal file
View file

@ -0,0 +1,40 @@
[package]
name = "bosion"
version = "1.1.1"
authors = ["Félix Saparelli <felix@passcod.name>"]
license = "Apache-2.0 OR MIT"
description = "Gather build information for verbose versions flags"
keywords = ["version", "git", "verbose", "long"]
documentation = "https://docs.rs/bosion"
repository = "https://github.com/watchexec/watchexec"
readme = "README.md"
rust-version = "1.64.0"
edition = "2021"
[dependencies.time]
version = "0.3.30"
features = ["macros", "formatting"]
[dependencies.gix]
version = "0.66.0"
optional = true
default-features = false
features = ["revision"]
[features]
default = ["git", "reproducible", "std"]
### Read from git repo, provide GIT_* vars
git = ["dep:gix"]
### Read from SOURCE_DATE_EPOCH when available
reproducible = []
### Provide a long_version_with() function to add extra info
###
### Specifically this is std support for the _using_ crate, not for the bosion crate itself. It's
### assumed that the bosion crate is always std, as it runs in build.rs.
std = []

147
crates/bosion/README.md Normal file
View file

@ -0,0 +1,147 @@
# Bosion
_Gather build information for verbose versions flags._
- **[API documentation][docs]**.
- Licensed under [Apache 2.0][license] or [MIT](https://passcod.mit-license.org).
- Status: maintained.
[docs]: https://docs.rs/bosion
[license]: ../../LICENSE
## Quick start
In your `Cargo.toml`:
```toml
[build-dependencies]
bosion = "1.1.1"
```
In your `build.rs`:
```rust ,no_run
fn main() {
bosion::gather();
}
```
In your `src/main.rs`:
```rust ,ignore
include!(env!("BOSION_PATH"));
fn main() {
// default output, like rustc -Vv
println!("{}", Bosion::LONG_VERSION);
// with additional fields
println!("{}", Bosion::long_version_with(&[
("custom data", "value"),
("LLVM version", "15.0.6"),
]));
// enabled features like +feature +an-other
println!("{}", Bosion::CRATE_FEATURE_STRING);
// the raw data
println!("{}", Bosion::GIT_COMMIT_HASH);
println!("{}", Bosion::GIT_COMMIT_SHORTHASH);
println!("{}", Bosion::GIT_COMMIT_DATE);
println!("{}", Bosion::GIT_COMMIT_DATETIME);
println!("{}", Bosion::CRATE_VERSION);
println!("{:?}", Bosion::CRATE_FEATURES);
println!("{}", Bosion::BUILD_DATE);
println!("{}", Bosion::BUILD_DATETIME);
}
```
## Advanced usage
Generating a struct with public visibility:
```rust ,no_run
// build.rs
bosion::gather_pub();
```
Customising the output file and struct names:
```rust ,no_run
// build.rs
bosion::gather_to("buildinfo.rs", "Build", /* public? */ false);
```
Outputting build-time environment variables instead of source:
```rust ,ignore
// build.rs
bosion::gather_to_env();
// src/main.rs
fn main() {
println!("{}", env!("BOSION_GIT_COMMIT_HASH"));
println!("{}", env!("BOSION_GIT_COMMIT_SHORTHASH"));
println!("{}", env!("BOSION_GIT_COMMIT_DATE"));
println!("{}", env!("BOSION_GIT_COMMIT_DATETIME"));
println!("{}", env!("BOSION_BUILD_DATE"));
println!("{}", env!("BOSION_BUILD_DATETIME"));
println!("{}", env!("BOSION_CRATE_VERSION"));
println!("{}", env!("BOSION_CRATE_FEATURES")); // comma-separated
}
```
Custom env prefix:
```rust ,no_run
// build.rs
bosion::gather_to_env_with_prefix("MYAPP_");
```
## Features
- `reproducible`: reads [`SOURCE_DATE_EPOCH`](https://reproducible-builds.org/docs/source-date-epoch/) (default).
- `git`: enables gathering git information (default).
- `std`: enables the `long_version_with` method (default).
Specifically, this is about the downstream crate's std support, not Bosion's, which always requires std.
## Why not...?
- [bugreport](https://github.com/sharkdp/bugreport): runtime library, for bug information.
- [git-testament](https://github.com/kinnison/git-testament): uses the `git` CLI instead of gitoxide.
- [human-panic](https://github.com/rust-cli/human-panic): runtime library, for panics.
- [shadow-rs](https://github.com/baoyachi/shadow-rs): uses libgit2 instead of gitoxide, doesn't rebuild on git changes.
- [vergen](https://github.com/rustyhorde/vergen): uses the `git` CLI instead of gitoxide.
Bosion also requires no dependencies outside of build.rs, and was specifically made for crates
installed in a variety of ways, like with `cargo install`, from pre-built binary, from source with
git, or from source without git (like a tarball), on a variety of platforms. Its default output with
[clap](https://clap.rs) is almost exactly like `rustc -Vv`.
## Examples
The [examples](./examples) directory contains a practical and runnable [clap-based example](./examples/clap/), as well
as several other crates which are actually used for integration testing.
Here is the output for the Watchexec CLI:
```plain
watchexec 1.21.1 (5026793 2023-03-05)
commit-hash: 5026793a12ff895edf2dafb92111e7bd1767650e
commit-date: 2023-03-05
build-date: 2023-03-05
release: 1.21.1
features:
```
For comparison, here's `rustc -Vv`:
```plain
rustc 1.67.1 (d5a82bbd2 2023-02-07)
binary: rustc
commit-hash: d5a82bbd26e1ad8b7401f6a718a9c57c96905483
commit-date: 2023-02-07
host: x86_64-unknown-linux-gnu
release: 1.67.1
LLVM version: 15.0.6
```

1303
crates/bosion/examples/clap/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
[package]
name = "bosion-example-clap"
version = "0.1.0"
publish = false
edition = "2021"
[workspace]
[features]
default = ["foo"]
foo = []
[build-dependencies.bosion]
version = "*"
path = "../.."
[dependencies.clap]
version = "4.1.8"
features = ["cargo", "derive"]

View file

@ -0,0 +1,3 @@
fn main() {
bosion::gather();
}

View file

@ -0,0 +1,41 @@
use clap::Parser;
include!(env!("BOSION_PATH"));
#[derive(Parser)]
#[clap(version, long_version = Bosion::LONG_VERSION)]
struct Args {
#[clap(long)]
extras: bool,
#[clap(long)]
features: bool,
#[clap(long)]
dates: bool,
#[clap(long)]
describe: bool,
}
fn main() {
let args = Args::parse();
if args.extras {
println!(
"{}",
Bosion::long_version_with(&[("extra", "field"), ("custom", "1.2.3"),])
);
} else if args.features {
println!("Features: {}", Bosion::CRATE_FEATURE_STRING);
} else if args.dates {
println!("commit date: {}", Bosion::GIT_COMMIT_DATE);
println!("commit datetime: {}", Bosion::GIT_COMMIT_DATETIME);
println!("build date: {}", Bosion::BUILD_DATE);
println!("build datetime: {}", Bosion::BUILD_DATETIME);
} else if args.describe {
println!("commit description: {}", Bosion::GIT_COMMIT_DESCRIPTION);
} else {
println!("{}", Bosion::LONG_VERSION);
}
}

1296
crates/bosion/examples/default/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
[package]
name = "bosion-test-default"
version = "0.1.0"
publish = false
edition = "2021"
[workspace]
[features]
default = ["foo"]
foo = []
[build-dependencies.bosion]
version = "*"
path = "../.."
[dependencies]
leon = { version = "2.0.1", default-features = false }
snapbox = "0.5.9"
time = { version = "0.3.30", features = ["formatting", "macros"] }

View file

@ -0,0 +1,3 @@
fn main() {
bosion::gather();
}

View file

@ -0,0 +1,68 @@
#[cfg(test)]
pub(crate) fn git_commit_info(format: &str) -> String {
let output = std::process::Command::new("git")
.arg("show")
.arg("--no-notes")
.arg("--no-patch")
.arg(format!("--pretty=format:{format}"))
.output()
.expect("git");
String::from_utf8(output.stdout)
.expect("git")
.trim()
.to_string()
}
#[macro_export]
macro_rules! test_snapshot {
($name:ident, $actual:expr) => {
#[cfg(test)]
#[test]
fn $name() {
use std::str::FromStr;
let gittime = ::time::OffsetDateTime::from_unix_timestamp(
i64::from_str(&crate::common::git_commit_info("%ct")).expect("git i64"),
)
.expect("git time");
::snapbox::Assert::new().matches(
::leon::Template::parse(
std::fs::read_to_string(format!("../snapshots/{}.txt", stringify!($name)))
.expect("read file")
.trim(),
)
.expect("leon parse")
.render(&[
(
"today date".to_string(),
::time::OffsetDateTime::now_utc()
.format(::time::macros::format_description!("[year]-[month]-[day]"))
.unwrap(),
),
("git hash".to_string(), crate::common::git_commit_info("%H")),
(
"git shorthash".to_string(),
crate::common::git_commit_info("%h"),
),
(
"git date".to_string(),
gittime
.format(::time::macros::format_description!("[year]-[month]-[day]"))
.expect("git date format"),
),
(
"git datetime".to_string(),
gittime
.format(::time::macros::format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second]"
))
.expect("git time format"),
),
])
.expect("leon render"),
$actual,
);
}
};
}

View file

@ -0,0 +1,27 @@
include!(env!("BOSION_PATH"));
mod common;
fn main() {}
test_snapshot!(crate_version, Bosion::CRATE_VERSION);
test_snapshot!(crate_features, format!("{:#?}", Bosion::CRATE_FEATURES));
test_snapshot!(build_date, Bosion::BUILD_DATE);
test_snapshot!(build_datetime, Bosion::BUILD_DATETIME);
test_snapshot!(git_commit_hash, Bosion::GIT_COMMIT_HASH);
test_snapshot!(git_commit_shorthash, Bosion::GIT_COMMIT_SHORTHASH);
test_snapshot!(git_commit_date, Bosion::GIT_COMMIT_DATE);
test_snapshot!(git_commit_datetime, Bosion::GIT_COMMIT_DATETIME);
test_snapshot!(default_long_version, Bosion::LONG_VERSION);
test_snapshot!(
default_long_version_with,
Bosion::long_version_with(&[("extra", "field"), ("custom", "1.2.3")])
);

336
crates/bosion/examples/no-git/Cargo.lock generated Normal file
View file

@ -0,0 +1,336 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "bosion"
version = "1.1.0"
dependencies = [
"time",
]
[[package]]
name = "bosion-test-no-git"
version = "0.1.0"
dependencies = [
"bosion",
"leon",
"snapbox",
"time",
]
[[package]]
name = "colorchoice"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "leon"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52df920dfe9751d43501ff2ee12dd81c457d9e810d3f64b5200ee461fe73800b"
dependencies = [
"thiserror",
]
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "similar"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
[[package]]
name = "snapbox"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f37d101fcafc8e73748fd8a1b7048f5979f93d372fd17027d7724c1643bc379b"
dependencies = [
"anstream",
"anstyle",
"normalize-line-endings",
"similar",
"snapbox-macros",
]
[[package]]
name = "snapbox-macros"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af"
dependencies = [
"anstream",
]
[[package]]
name = "syn"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View file

@ -0,0 +1,22 @@
[package]
name = "bosion-test-no-git"
version = "0.1.0"
publish = false
edition = "2021"
[workspace]
[features]
default = ["foo"]
foo = []
[build-dependencies.bosion]
version = "*"
path = "../.."
default-features = false
features = ["std"]
[dependencies]
leon = { version = "2.0.1", default-features = false }
snapbox = "0.5.9"
time = { version = "0.3.30", features = ["formatting", "macros"] }

View file

@ -0,0 +1,3 @@
fn main() {
bosion::gather();
}

View file

@ -0,0 +1,20 @@
include!(env!("BOSION_PATH"));
#[path = "../../default/src/common.rs"]
mod common;
fn main() {}
test_snapshot!(crate_version, Bosion::CRATE_VERSION);
test_snapshot!(crate_features, format!("{:#?}", Bosion::CRATE_FEATURES));
test_snapshot!(build_date, Bosion::BUILD_DATE);
test_snapshot!(build_datetime, Bosion::BUILD_DATETIME);
test_snapshot!(no_git_long_version, Bosion::LONG_VERSION);
test_snapshot!(
no_git_long_version_with,
Bosion::long_version_with(&[("extra", "field"), ("custom", "1.2.3")])
);

336
crates/bosion/examples/no-std/Cargo.lock generated Normal file
View file

@ -0,0 +1,336 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "bosion"
version = "1.1.0"
dependencies = [
"time",
]
[[package]]
name = "bosion-test-no-std"
version = "0.1.0"
dependencies = [
"bosion",
"leon",
"snapbox",
"time",
]
[[package]]
name = "colorchoice"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "leon"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52df920dfe9751d43501ff2ee12dd81c457d9e810d3f64b5200ee461fe73800b"
dependencies = [
"thiserror",
]
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "similar"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
[[package]]
name = "snapbox"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f37d101fcafc8e73748fd8a1b7048f5979f93d372fd17027d7724c1643bc379b"
dependencies = [
"anstream",
"anstyle",
"normalize-line-endings",
"similar",
"snapbox-macros",
]
[[package]]
name = "snapbox-macros"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af"
dependencies = [
"anstream",
]
[[package]]
name = "syn"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View file

@ -0,0 +1,27 @@
[package]
name = "bosion-test-no-std"
version = "0.1.0"
publish = false
edition = "2021"
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
[workspace]
[features]
default = ["foo"]
foo = []
[build-dependencies.bosion]
version = "*"
path = "../.."
default-features = false
[dependencies]
leon = { version = "2.0.1", default-features = false }
snapbox = "0.5.9"
time = { version = "0.3.30", features = ["formatting", "macros"] }

View file

@ -0,0 +1,3 @@
fn main() {
bosion::gather();
}

View file

@ -0,0 +1,32 @@
#![cfg_attr(not(test), no_main)]
#![cfg_attr(not(test), no_std)]
#[cfg(not(test))]
use core::panic::PanicInfo;
#[cfg(not(test))]
#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
loop {}
}
include!(env!("BOSION_PATH"));
#[cfg(test)]
#[path = "../../default/src/common.rs"]
mod common;
#[cfg(test)]
mod test {
use super::*;
test_snapshot!(crate_version, Bosion::CRATE_VERSION);
test_snapshot!(crate_features, format!("{:#?}", Bosion::CRATE_FEATURES));
test_snapshot!(build_date, Bosion::BUILD_DATE);
test_snapshot!(build_datetime, Bosion::BUILD_DATETIME);
test_snapshot!(no_git_long_version, Bosion::LONG_VERSION);
}

View file

@ -0,0 +1 @@
{today date}

View file

@ -0,0 +1 @@
{today date} [..]

View file

@ -0,0 +1,4 @@
[
"default",
"foo",
]

View file

@ -0,0 +1 @@
0.1.0

View file

@ -0,0 +1,6 @@
0.1.0 ({git shorthash} {git date}) +foo
commit-hash: {git hash}
commit-date: {git date}
build-date: {today date}
release: 0.1.0
features: default,foo

View file

@ -0,0 +1,8 @@
0.1.0 ({git shorthash} {git date}) +foo
commit-hash: {git hash}
commit-date: {git date}
build-date: {today date}
release: 0.1.0
features: default,foo
extra: field
custom: 1.2.3

View file

@ -0,0 +1 @@
{git date}

View file

@ -0,0 +1 @@
{git datetime}

View file

@ -0,0 +1 @@
{git hash}

View file

@ -0,0 +1 @@
{git shorthash}

View file

@ -0,0 +1,4 @@
0.1.0 ({today date}) +foo
build-date: {today date}
release: 0.1.0
features: default,foo

View file

@ -0,0 +1,6 @@
0.1.0 ({today date}) +foo
build-date: {today date}
release: 0.1.0
features: default,foo
extra: field
custom: 1.2.3

View file

@ -0,0 +1,17 @@
pre-release-commit-message = "release: bosion v{{version}}"
tag-prefix = "bosion-"
tag-message = "bosion {{version}}"
[[pre-release-replacements]]
file = "CHANGELOG.md"
search = "^## Next.*$"
replace = "## Next (YYYY-MM-DD)\n\n## v{{version}} ({{date}})"
prerelease = true
max = 1
[[pre-release-replacements]]
file = "README.md"
search = "^bosion = \".*\"$"
replace = "bosion = \"{{version}}\""
prerelease = true
max = 1

11
crates/bosion/run-tests.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/bash
set -euo pipefail
for test in default no-git no-std; do
echo "Testing $test"
pushd examples/$test
cargo check
cargo test
popd
done

172
crates/bosion/src/info.rs Normal file
View file

@ -0,0 +1,172 @@
use std::{env::var, path::PathBuf};
use time::{format_description::FormatItem, macros::format_description, OffsetDateTime};
/// Gathered build-time information
///
/// This struct contains all the information gathered by `bosion`. It is not meant to be used
/// directly under normal circumstances, but is public for documentation purposes and if you wish
/// to build your own frontend for whatever reason. In that case, note that no effort has been made
/// to make this usable outside of the build.rs environment.
///
/// The `git` field is only available when the `git` feature is enabled, and if there is a git
/// repository to read from. The repository is discovered by walking up the directory tree until one
/// is found, which means workspaces or more complex monorepos are automatically supported. If there
/// are any errors reading the repository, the `git` field will be `None` and a rustc warning will
/// be printed.
#[derive(Debug, Clone)]
pub struct Info {
/// The crate version, as read from the `CARGO_PKG_VERSION` environment variable.
pub crate_version: String,
/// The crate features, as found by the presence of `CARGO_FEATURE_*` environment variables.
///
/// These are normalised to lowercase and have underscores replaced by hyphens.
pub crate_features: Vec<String>,
/// The build date, in the format `YYYY-MM-DD`, at UTC.
///
/// This is either current as of build time, or from the timestamp specified by the
/// `SOURCE_DATE_EPOCH` environment variable, for
/// [reproducible builds](https://reproducible-builds.org/).
pub build_date: String,
/// The build datetime, in the format `YYYY-MM-DD HH:MM:SS`, at UTC.
///
/// This is either current as of build time, or from the timestamp specified by the
/// `SOURCE_DATE_EPOCH` environment variable, for
/// [reproducible builds](https://reproducible-builds.org/).
pub build_datetime: String,
/// Git repository information, if available.
pub git: Option<GitInfo>,
}
trait ErrString<T> {
fn err_string(self) -> Result<T, String>;
}
impl<T, E> ErrString<T> for Result<T, E>
where
E: std::fmt::Display,
{
fn err_string(self) -> Result<T, String> {
self.map_err(|e| e.to_string())
}
}
const DATE_FORMAT: &[FormatItem<'static>] = format_description!("[year]-[month]-[day]");
const DATETIME_FORMAT: &[FormatItem<'static>] =
format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
impl Info {
/// Gathers build-time information
///
/// This is not meant to be used directly under normal circumstances, but is public if you wish
/// to build your own frontend for whatever reason. In that case, note that no effort has been
/// made to make this usable outside of the build.rs environment.
pub fn gather() -> Result<Self, String> {
let build_date = Self::build_date()?;
Ok(Self {
crate_version: var("CARGO_PKG_VERSION").err_string()?,
crate_features: Self::features(),
build_date: build_date.format(DATE_FORMAT).err_string()?,
build_datetime: build_date.format(DATETIME_FORMAT).err_string()?,
#[cfg(feature = "git")]
git: GitInfo::gather()
.map_err(|e| {
println!("cargo:warning=git info gathering failed: {e}");
})
.ok(),
#[cfg(not(feature = "git"))]
git: None,
})
}
fn build_date() -> Result<OffsetDateTime, String> {
if cfg!(feature = "reproducible") {
if let Ok(date) = var("SOURCE_DATE_EPOCH") {
if let Ok(date) = date.parse::<i64>() {
return OffsetDateTime::from_unix_timestamp(date).err_string();
}
}
}
Ok(OffsetDateTime::now_utc())
}
fn features() -> Vec<String> {
let mut features = Vec::new();
for (key, _) in std::env::vars() {
if let Some(stripped) = key.strip_prefix("CARGO_FEATURE_") {
features.push(stripped.replace('_', "-").to_lowercase().to_string());
}
}
features
}
pub(crate) fn set_reruns(&self) {
if cfg!(feature = "reproducible") {
println!("cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH");
}
if let Some(git) = &self.git {
let git_head = git.git_root.join("HEAD");
println!("cargo:rerun-if-changed={}", git_head.display());
}
}
}
/// Git repository information
#[derive(Debug, Clone)]
pub struct GitInfo {
/// The absolute path to the git repository's data folder.
///
/// In a normal repository, this is `.git`, _not_ the index or working directory.
pub git_root: PathBuf,
/// The full hash of the current commit.
///
/// Note that this makes no effore to handle dirty working directories, so it may not be
/// representative of the current state of the code.
pub git_hash: String,
/// The short hash of the current commit.
///
/// This is read from git and not truncated manually, so it may be longer than 7 characters.
pub git_shorthash: String,
/// The date of the current commit, in the format `YYYY-MM-DD`, at UTC.
pub git_date: String,
/// The datetime of the current commit, in the format `YYYY-MM-DD HH:MM:SS`, at UTC.
pub git_datetime: String,
/// The `git describe` equivalent output
pub git_description: String,
}
#[cfg(feature = "git")]
impl GitInfo {
fn gather() -> Result<Self, String> {
use std::path::Path;
let (path, _) = gix::discover::upwards(Path::new(".")).err_string()?;
let repo = gix::discover(path).err_string()?;
let head = repo.head_commit().err_string()?;
let time = head.time().err_string()?;
let timestamp = OffsetDateTime::from_unix_timestamp(time.seconds as _).err_string()?;
Ok(Self {
git_root: repo.path().canonicalize().err_string()?,
git_hash: head.id().to_string(),
git_shorthash: head.short_id().err_string()?.to_string(),
git_date: timestamp.format(DATE_FORMAT).err_string()?,
git_datetime: timestamp.format(DATETIME_FORMAT).err_string()?,
git_description: head.describe().format().err_string()?.to_string(),
})
}
}

263
crates/bosion/src/lib.rs Normal file
View file

@ -0,0 +1,263 @@
#![doc = include_str!("../README.md")]
use std::{env::var, fs::File, io::Write, path::PathBuf};
pub use info::*;
mod info;
/// Gather build-time information for the current crate
///
/// See the crate-level documentation for a guide. This function is a convenience wrapper around
/// [`gather_to`] with the most common defaults: it writes to `bosion.rs` a pub(crate) struct named
/// `Bosion`.
pub fn gather() {
gather_to("bosion.rs", "Bosion", false);
}
/// Gather build-time information for the current crate (public visibility)
///
/// See the crate-level documentation for a guide. This function is a convenience wrapper around
/// [`gather_to`]: it writes to `bosion.rs` a pub struct named `Bosion`.
pub fn gather_pub() {
gather_to("bosion.rs", "Bosion", true);
}
/// Gather build-time information for the current crate (custom output)
///
/// Gathers a limited set of build-time information for the current crate and writes it to a file.
/// The file is always written to the `OUT_DIR` directory, as per Cargo conventions. It contains a
/// zero-size struct with a bunch of associated constants containing the gathered information, and a
/// `long_version_with` function (when the `std` feature is enabled) that takes a slice of extra
/// key-value pairs to append in the same format.
///
/// `public` controls whether the struct is `pub` (true) or `pub(crate)` (false).
///
/// The generated code is entirely documented, and will appear in your documentation (in docs.rs, it
/// only will if visibility is public).
///
/// See [`Info`] for a list of gathered data.
///
/// The constants include all the information from [`Info`], as well as the following:
///
/// - `LONG_VERSION`: A clap-ready long version string, including the crate version, features, build
/// date, and git information when available.
/// - `CRATE_FEATURE_STRING`: A string containing the crate features, in the format `+feat1 +feat2`.
///
/// We also instruct rustc to rerun the build script if the environment changes, as necessary.
pub fn gather_to(filename: &str, structname: &str, public: bool) {
let path = PathBuf::from(var("OUT_DIR").expect("bosion")).join(filename);
println!("cargo:rustc-env=BOSION_PATH={}", path.display());
let info = Info::gather().expect("bosion");
info.set_reruns();
let Info {
crate_version,
crate_features,
build_date,
build_datetime,
git,
} = info;
let crate_feature_string = crate_features
.iter()
.filter(|feat| *feat != "default")
.map(|feat| format!("+{feat}"))
.collect::<Vec<_>>()
.join(" ");
let crate_feature_list = crate_features.join(",");
let viz = if public { "pub" } else { "pub(crate)" };
let (git_render, long_version) = if let Some(GitInfo {
git_hash,
git_shorthash,
git_date,
git_datetime,
git_description,
..
}) = git
{
(format!(
"
/// The git commit hash
///
/// This is the full hash of the commit that was built. Note that if the repository was
/// dirty, this will be the hash of the last commit, not including the changes.
pub const GIT_COMMIT_HASH: &'static str = {git_hash:?};
/// The git commit hash, shortened
///
/// This is the shortened hash of the commit that was built. Same caveats as with
/// `GIT_COMMIT_HASH` apply. The length of the hash is as short as possible while still
/// being unambiguous, at build time. For large repositories, this may be longer than 7
/// characters.
pub const GIT_COMMIT_SHORTHASH: &'static str = {git_shorthash:?};
/// The git commit date
///
/// This is the date (`YYYY-MM-DD`) of the commit that was built. Same caveats as with
/// `GIT_COMMIT_HASH` apply.
pub const GIT_COMMIT_DATE: &'static str = {git_date:?};
/// The git commit date and time
///
/// This is the date and time (`YYYY-MM-DD HH:MM:SS`) of the commit that was built. Same
/// caveats as with `GIT_COMMIT_HASH` apply.
pub const GIT_COMMIT_DATETIME: &'static str = {git_datetime:?};
/// The git description
///
/// This is the string equivalent to what `git describe` would output
pub const GIT_COMMIT_DESCRIPTION: &'static str = {git_description:?};
"
), format!("{crate_version} ({git_shorthash} {git_date}) {crate_feature_string}\ncommit-hash: {git_hash}\ncommit-date: {git_date}\nbuild-date: {build_date}\nrelease: {crate_version}\nfeatures: {crate_feature_list}"))
} else {
(String::new(), format!("{crate_version} ({build_date}) {crate_feature_string}\nbuild-date: {build_date}\nrelease: {crate_version}\nfeatures: {crate_feature_list}"))
};
#[cfg(feature = "std")]
let long_version_with_fn = r#"
/// Returns the long version string with extra information tacked on
///
/// This is the same as `LONG_VERSION` but takes a slice of key-value pairs to append to the
/// end in the same format.
pub fn long_version_with(extra: &[(&str, &str)]) -> String {
let mut output = Self::LONG_VERSION.to_string();
for (k, v) in extra {
output.push_str(&format!("\n{k}: {v}"));
}
output
}
"#;
#[cfg(not(feature = "std"))]
let long_version_with_fn = "";
let bosion_version = env!("CARGO_PKG_VERSION");
let render = format!(
r#"
/// Build-time information
///
/// This struct is generated by the [bosion](https://docs.rs/bosion) crate at build time.
///
/// Bosion version: {bosion_version}
#[derive(Debug, Clone, Copy)]
{viz} struct {structname};
#[allow(dead_code)]
impl {structname} {{
/// Clap-compatible long version string
///
/// At minimum, this will be the crate version and build date.
///
/// It presents as a first "summary" line like `crate_version (build_date) features`,
/// followed by `key: value` pairs. This is the same format used by `rustc -Vv`.
///
/// If git info is available, it also includes the git hash, short hash and commit date,
/// and swaps the build date for the commit date in the summary line.
pub const LONG_VERSION: &'static str = {long_version:?};
/// The crate version, as reported by Cargo
///
/// You should probably prefer reading the `CARGO_PKG_VERSION` environment variable.
pub const CRATE_VERSION: &'static str = {crate_version:?};
/// The crate features
///
/// This is a list of the features that were enabled when this crate was built,
/// lowercased and with underscores replaced by hyphens.
pub const CRATE_FEATURES: &'static [&'static str] = &{crate_features:?};
/// The crate features, as a string
///
/// This is in format `+feature +feature2 +feature3`, lowercased with underscores
/// replaced by hyphens.
pub const CRATE_FEATURE_STRING: &'static str = {crate_feature_string:?};
/// The build date
///
/// This is the date that the crate was built, in the format `YYYY-MM-DD`. If the
/// environment variable `SOURCE_DATE_EPOCH` was set, it's used instead of the current
/// time, for [reproducible builds](https://reproducible-builds.org/).
pub const BUILD_DATE: &'static str = {build_date:?};
/// The build datetime
///
/// This is the date and time that the crate was built, in the format
/// `YYYY-MM-DD HH:MM:SS`. If the environment variable `SOURCE_DATE_EPOCH` was set, it's
/// used instead of the current time, for
/// [reproducible builds](https://reproducible-builds.org/).
pub const BUILD_DATETIME: &'static str = {build_datetime:?};
{git_render}
{long_version_with_fn}
}}
"#
);
let mut file = File::create(path).expect("bosion");
file.write_all(render.as_bytes()).expect("bosion");
}
/// Gather build-time information and write it to the environment
///
/// See the crate-level documentation for a guide. This function is a convenience wrapper around
/// [`gather_to_env_with_prefix`] with the most common default prefix of `BOSION_`.
pub fn gather_to_env() {
gather_to_env_with_prefix("BOSION_");
}
/// Gather build-time information and write it to the environment
///
/// Gathers a limited set of build-time information for the current crate and makes it available to
/// the crate as build environment variables. This is an alternative to [`include!`]ing a file which
/// is generated at build time, like for [`gather`] and variants, which doesn't create any new code
/// and doesn't include any information in the binary that you do not explicitly use.
///
/// The environment variables are prefixed with the given string, which should be generally be
/// uppercase and end with an underscore.
///
/// See [`Info`] for a list of gathered data.
///
/// Unlike [`gather`], there is no Clap-ready `LONG_VERSION` string, but you can of course generate
/// one yourself from the environment variables.
///
/// We also instruct rustc to rerun the build script if the environment changes, as necessary.
pub fn gather_to_env_with_prefix(prefix: &str) {
let info = Info::gather().expect("bosion");
info.set_reruns();
let Info {
crate_version,
crate_features,
build_date,
build_datetime,
git,
} = info;
println!("cargo:rustc-env={prefix}CRATE_VERSION={crate_version}");
println!(
"cargo:rustc-env={prefix}CRATE_FEATURES={}",
crate_features.join(",")
);
println!("cargo:rustc-env={prefix}BUILD_DATE={build_date}");
println!("cargo:rustc-env={prefix}BUILD_DATETIME={build_datetime}");
if let Some(GitInfo {
git_hash,
git_shorthash,
git_date,
git_datetime,
git_description,
..
}) = git
{
println!("cargo:rustc-env={prefix}GIT_COMMIT_HASH={git_hash}");
println!("cargo:rustc-env={prefix}GIT_COMMIT_SHORTHASH={git_shorthash}");
println!("cargo:rustc-env={prefix}GIT_COMMIT_DATE={git_date}");
println!("cargo:rustc-env={prefix}GIT_COMMIT_DATETIME={git_datetime}");
println!("cargo:rustc-env={prefix}GIT_COMMIT_DESCRIPTION={git_description}");
}
}

197
crates/cli/Cargo.toml Normal file
View file

@ -0,0 +1,197 @@
[package]
name = "watchexec-cli"
version = "2.2.0"
authors = ["Félix Saparelli <felix@passcod.name>", "Matt Green <mattgreenrocks@gmail.com>"]
license = "Apache-2.0"
description = "Executes commands in response to file modifications"
keywords = ["watcher", "filesystem", "cli", "watchexec"]
categories = ["command-line-utilities"]
documentation = "https://watchexec.github.io/docs/#watchexec"
homepage = "https://watchexec.github.io"
repository = "https://github.com/watchexec/watchexec"
readme = "README.md"
edition = "2021"
[[bin]]
name = "watchexec"
path = "src/main.rs"
[dependencies]
ahash = "0.8.6" # needs to be in sync with jaq's
argfile = "0.2.0"
chrono = "0.4.31"
clap_complete = "4.4.4"
clap_complete_nushell = "4.4.2"
clap_mangen = "0.2.15"
clearscreen = "3.0.0"
dashmap = "6.1.0"
dirs = "5.0.0"
dunce = "1.0.4"
futures = "0.3.29"
humantime = "2.1.0"
indexmap = "2.2.6" # needs to be in sync with jaq's
is-terminal = "0.4.4"
jaq-core = "1.2.1"
jaq-interpret = "1.2.1"
jaq-parse = "1.0.2"
jaq-std = "1.2.1"
jaq-syn = "1.1.0"
notify-rust = "4.9.0"
once_cell = "1.17.1"
serde_json = "1.0.107"
tempfile = "3.8.1"
termcolor = "1.4.0"
tracing = "0.1.40"
tracing-appender = "0.2.3"
which = "6.0.1"
[dependencies.blake3]
version = "1.3.3"
features = ["rayon"]
[dependencies.clap]
version = "4.4.7"
features = ["cargo", "derive", "env", "wrap_help"]
[dependencies.console-subscriber]
version = "0.4.0"
optional = true
[dependencies.eyra]
version = "0.19.0"
features = ["log", "env_logger"]
optional = true
[dependencies.ignore-files]
version = "3.0.2"
path = "../ignore-files"
[dependencies.miette]
version = "7.2.0"
features = ["fancy"]
[dependencies.pid1]
version = "0.1.1"
optional = true
[dependencies.project-origins]
version = "1.4.0"
path = "../project-origins"
[dependencies.watchexec]
version = "5.0.0"
path = "../lib"
[dependencies.watchexec-events]
version = "4.0.0"
path = "../events"
features = ["serde"]
[dependencies.watchexec-signals]
version = "4.0.0"
path = "../signals"
[dependencies.watchexec-filterer-globset]
version = "6.0.0"
path = "../filterer/globset"
[dependencies.tokio]
version = "1.33.0"
features = [
"fs",
"io-std",
"process",
"rt",
"rt-multi-thread",
"signal",
"sync",
]
[dependencies.tracing-subscriber]
version = "0.3.6"
features = [
"env-filter",
"fmt",
"json",
"tracing-log",
"ansi",
]
[target.'cfg(target_env = "musl")'.dependencies]
mimalloc = "0.1.39"
[build-dependencies]
embed-resource = "2.4.0"
[build-dependencies.bosion]
version = "1.1.1"
path = "../bosion"
[dev-dependencies]
tracing-test = "0.2.4"
uuid = { workspace = true, features = [ "v4", "fast-rng" ] }
rand = { workspace = true }
[features]
default = ["pid1"]
## Build using Eyra's pure-Rust libc
eyra = ["dep:eyra"]
## Enables PID1 handling.
pid1 = ["dep:pid1"]
## Enables logging for PID1 handling.
pid1-withlog = ["pid1"]
## For debugging only: enables the Tokio Console.
dev-console = ["dep:console-subscriber"]
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/watchexec-{ version }-{ target }.{ archive-format }"
bin-dir = "watchexec-{ version }-{ target }/{ bin }{ binary-ext }"
pkg-fmt = "txz"
[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
pkg-fmt = "zip"
[package.metadata.deb]
maintainer = "Félix Saparelli <felix@passcod.name>"
license-file = ["../../LICENSE", "0"]
section = "utility"
depends = "libc6, libgcc-s1" # not needed for musl, but see below
# conf-files = [] # look me up when config file lands
assets = [
["../../target/release/watchexec", "usr/bin/watchexec", "755"],
["README.md", "usr/share/doc/watchexec/README", "644"],
["../../doc/watchexec.1.md", "usr/share/doc/watchexec/watchexec.1.md", "644"],
["../../doc/watchexec.1", "usr/share/man/man1/watchexec.1", "644"],
["../../completions/bash", "usr/share/bash-completion/completions/watchexec", "644"],
["../../completions/fish", "usr/share/fish/vendor_completions.d/watchexec.fish", "644"],
["../../completions/zsh", "usr/share/zsh/site-functions/_watchexec", "644"],
["../../doc/logo.svg", "usr/share/icons/hicolor/scalable/apps/watchexec.svg", "644"],
]
[package.metadata.generate-rpm]
assets = [
{ source = "../../target/release/watchexec", dest = "/usr/bin/watchexec", mode = "755" },
{ source = "README.md", dest = "/usr/share/doc/watchexec/README", mode = "644", doc = true },
{ source = "../../doc/watchexec.1.md", dest = "/usr/share/doc/watchexec/watchexec.1.md", mode = "644", doc = true },
{ source = "../../doc/watchexec.1", dest = "/usr/share/man/man1/watchexec.1", mode = "644" },
{ source = "../../completions/bash", dest = "/usr/share/bash-completion/completions/watchexec", mode = "644" },
{ source = "../../completions/fish", dest = "/usr/share/fish/vendor_completions.d/watchexec.fish", mode = "644" },
{ source = "../../completions/zsh", dest = "/usr/share/zsh/site-functions/_watchexec", mode = "644" },
{ source = "../../doc/logo.svg", dest = "/usr/share/icons/hicolor/scalable/apps/watchexec.svg", mode = "644" },
# set conf = true for config file when that lands
]
auto-req = "disabled"
# technically incorrect when using musl, but these are probably
# present on every rpm-using system, so let's worry about it if
# someone asks.
[package.metadata.generate-rpm.requires]
glibc = "*"
libgcc = "*"

197
crates/cli/README.md Normal file
View file

@ -0,0 +1,197 @@
# Watchexec CLI
A simple standalone tool that watches a path and runs a command whenever it detects modifications.
Example use cases:
* Automatically run unit tests
* Run linters/syntax checkers
## Features
* Simple invocation and use
* Runs on Linux, Mac, Windows, and more
* Monitors current directory and all subdirectories for changes
* Uses efficient event polling mechanism (on Linux, Mac, Windows, BSD)
* Coalesces multiple filesystem events into one, for editors that use swap/backup files during saving
* By default, uses `.gitignore`, `.ignore`, and other such files to determine which files to ignore notifications for
* Support for watching files with a specific extension
* Support for filtering/ignoring events based on [glob patterns](https://docs.rs/globset/*/globset/#syntax)
* Launches the command in a new process group (can be disabled with `--no-process-group`)
* Optionally clears screen between executions
* Optionally restarts the command with every modification (good for servers)
* Optionally sends a desktop notification on command start and end
* Does not require a language runtime
* Sets the following environment variables in the process:
`$WATCHEXEC_COMMON_PATH` is set to the longest common path of all of the below variables, and so should be prepended to each path to obtain the full/real path.
| Variable name | Event kind |
|---|---|
| `$WATCHEXEC_CREATED_PATH` | files/folders were created |
| `$WATCHEXEC_REMOVED_PATH` | files/folders were removed |
| `$WATCHEXEC_RENAMED_PATH` | files/folders were renamed |
| `$WATCHEXEC_WRITTEN_PATH` | files/folders were modified |
| `$WATCHEXEC_META_CHANGED_PATH` | files/folders' metadata were modified |
| `$WATCHEXEC_OTHERWISE_CHANGED_PATH` | every other kind of event |
These variables may contain multiple paths: these are separated by the platform's path separator, as with the `PATH` system environment variable. On Unix that is `:`, and on Windows `;`. Within each variable, paths are deduplicated and sorted in binary order (i.e. neither Unicode nor locale aware).
This can be disabled with `--emit-events=none` or changed to JSON events on STDIN with `--emit-events=json-stdio`.
## Anti-Features
* Not tied to any particular language or ecosystem
* Not tied to Git or the presence of a repository/project
* Does not require a cryptic command line involving `xargs`
## Usage Examples
Watch all JavaScript, CSS and HTML files in the current directory and all subdirectories for changes, running `make` when a change is detected:
$ watchexec --exts js,css,html make
Call `make test` when any file changes in this directory/subdirectory, except for everything below `target`:
$ watchexec -i "target/**" make test
Call `ls -la` when any file changes in this directory/subdirectory:
$ watchexec -- ls -la
Call/restart `python server.py` when any Python file in the current directory (and all subdirectories) changes:
$ watchexec -e py -r python server.py
Call/restart `my_server` when any file in the current directory (and all subdirectories) changes, sending `SIGKILL` to stop the command:
$ watchexec -r --stop-signal SIGKILL my_server
Send a SIGHUP to the command upon changes (Note: using `-n` here we're executing `my_server` directly, instead of wrapping it in a shell:
$ watchexec -n --signal SIGHUP my_server
Run `make` when any file changes, using the `.gitignore` file in the current directory to filter:
$ watchexec make
Run `make` when any file in `lib` or `src` changes:
$ watchexec -w lib -w src make
Run `bundle install` when the `Gemfile` changes:
$ watchexec -w Gemfile bundle install
Run two commands:
$ watchexec 'date; make'
Get desktop ("toast") notifications when the command starts and finishes:
$ watchexec -N go build
Only run when files are created:
$ watchexec --fs-events create -- s3 sync . s3://my-bucket
If you come from `entr`, note that the watchexec command is run in a shell by default. You can use `-n` or `--shell=none` to not do that:
$ watchexec -n -- echo ';' lorem ipsum
On Windows, you may prefer to use Powershell:
$ watchexec --shell=pwsh -- Test-Connection example.com
You can eschew running commands entirely and get a stream of events to process on your own:
```console
$ watchexec --emit-events-to=json-stdio --only-emit-events
{"tags":[{"kind":"source","source":"filesystem"},{"kind":"fs","simple":"modify","full":"Modify(Data(Any))"},{"kind":"path","absolute":"/home/code/rust/watchexec/crates/cli/README.md","filetype":"file"}]}
{"tags":[{"kind":"source","source":"filesystem"},{"kind":"fs","simple":"modify","full":"Modify(Data(Any))"},{"kind":"path","absolute":"/home/code/rust/watchexec/crates/lib/Cargo.toml","filetype":"file"}]}
{"tags":[{"kind":"source","source":"filesystem"},{"kind":"fs","simple":"modify","full":"Modify(Data(Any))"},{"kind":"path","absolute":"/home/code/rust/watchexec/crates/cli/src/args.rs","filetype":"file"}]}
```
Print the time commands take to run:
```console
$ watchexec --timings -- make
[Running: make]
...
[Command was successful, lasted 52.748081074s]
```
## Installation
### Package manager
Watchexec is in many package managers. A full list of [known packages](../../doc/packages.md) is available,
and there may be more out there! Please contribute any you find to the list :)
Common package managers:
- Alpine: `$ apk add watchexec`
- ArchLinux: `$ pacman -S watchexec`
- Nix: `$ nix-shell -p watchexec`
- Debian/Ubuntu via [apt.cli.rs](https://apt.cli.rs): `$ apt install watchexec`
- Homebrew on Mac: `$ brew install watchexec`
- Chocolatey on Windows: `#> choco install watchexec`
### [Binstall](https://github.com/cargo-bins/cargo-binstall)
$ cargo binstall watchexec-cli
### Pre-built binaries
Use the download section on [Github](https://github.com/watchexec/watchexec/releases/latest)
or [the website](https://watchexec.github.io/downloads/) to obtain the package appropriate for your
platform and architecture, extract it, and place it in your `PATH`.
There are also Debian/Ubuntu (DEB) and Fedora/RedHat (RPM) packages.
Checksums and signatures are available.
### Cargo (from source)
Only the latest Rust stable is supported, but older versions may work.
$ cargo install watchexec-cli
## Shell completions
Currently available shell completions:
- bash: `completions/bash` should be installed to `/usr/share/bash-completion/completions/watchexec`
- elvish: `completions/elvish` should be installed to `$XDG_CONFIG_HOME/elvish/completions/`
- fish: `completions/fish` should be installed to `/usr/share/fish/vendor_completions.d/watchexec.fish`
- nu: `completions/nu` should be installed to `$XDG_CONFIG_HOME/nu/completions/`
- powershell: `completions/powershell` should be installed to `$PROFILE/`
- zsh: `completions/zsh` should be installed to `/usr/share/zsh/site-functions/_watchexec`
If not bundled, you can generate completions for your shell with `watchexec --completions <shell>`.
## Manual
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).
Note that it is automatically generated from the help text, so it is not as pretty as a carefully hand-written one.
## Advanced builds
These are additional options available with custom builds by setting features:
### PID1
If you're using Watchexec as PID1 (most frequently in containers or namespaces), and it's not doing what you expect, you can create a build with PID1 early logging: `--features pid1-withlog`.
If you don't need PID1 support, or if you're doing something that conflicts with this program's PID1 support, you can disable it with `--no-default-features`.
### Eyra
[Eyra](https://github.com/sunfishcode/eyra) is a system to build Linux programs with no dependency on C code (in the libc path). To build Watchexec like this, use `--features eyra` and a Nightly compiler.
This feature also lets you get early logging into program startup, with `RUST_LOG=trace`.

8
crates/cli/build.rs Normal file
View file

@ -0,0 +1,8 @@
fn main() {
embed_resource::compile("watchexec-manifest.rc", embed_resource::NONE);
bosion::gather();
if std::env::var("CARGO_FEATURE_EYRA").is_ok() {
println!("cargo:rustc-link-arg=-nostartfiles");
}
}

7
crates/cli/integration/env.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/bash
set -euxo pipefail
watchexec=${WATCHEXEC_BIN:-watchexec}
$watchexec -1 --env FOO=BAR echo '$FOO' | grep BAR

View file

@ -0,0 +1,7 @@
#!/bin/bash
set -euxo pipefail
watchexec=${WATCHEXEC_BIN:-watchexec}
$watchexec -1 -n echo 'foo bar' | grep 'foo bar'

View file

@ -0,0 +1,7 @@
#!/bin/bash
set -euxo pipefail
watchexec=${WATCHEXEC_BIN:-watchexec}
timeout -s9 30s sh -c "sleep 10 | $watchexec --stdin-quit echo"

View file

@ -0,0 +1,7 @@
#!/bin/bash
set -euxo pipefail
watchexec=${WATCHEXEC_BIN:-watchexec}
$watchexec -1 -- echo @trailingargfile

26
crates/cli/release.toml Normal file
View file

@ -0,0 +1,26 @@
pre-release-commit-message = "release: cli v{{version}}"
tag-prefix = ""
tag-message = "watchexec {{version}}"
pre-release-hook = ["sh", "-c", "cd ../.. && bin/completions && bin/manpage"]
[[pre-release-replacements]]
file = "watchexec.exe.manifest"
search = "^ version=\"[\\d.]+[.]0\""
replace = " version=\"{{version}}.0\""
prerelease = false
max = 1
[[pre-release-replacements]]
file = "../../CITATION.cff"
search = "^version: \"?[\\d.]+(-.+)?\"?"
replace = "version: \"{{version}}\""
prerelease = true
max = 1
[[pre-release-replacements]]
file = "../../CITATION.cff"
search = "^date-released: .+"
replace = "date-released: {{date}}"
prerelease = true
max = 1

13
crates/cli/run-tests.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash
set -euo pipefail
export WATCHEXEC_BIN=$(realpath ${WATCHEXEC_BIN:-$(which watchexec)})
cd "$(dirname "${BASH_SOURCE[0]}")/integration"
for test in *.sh; do
echo
echo
echo "======= Testing $test ======="
./$test
done

1344
crates/cli/src/args.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,132 @@
use std::{env::var, io::stderr, path::PathBuf};
use clap::{ArgAction, Parser, ValueHint};
use miette::{bail, Result};
use tokio::fs::metadata;
use tracing::{info, warn};
use tracing_appender::{non_blocking, non_blocking::WorkerGuard, rolling};
#[derive(Debug, Clone, Parser)]
pub struct LoggingArgs {
/// Set diagnostic log level
///
/// This enables diagnostic logging, which is useful for investigating bugs or gaining more
/// insight into faulty filters or "missing" events. Use multiple times to increase verbosity.
///
/// Goes up to '-vvvv'. When submitting bug reports, default to a '-vvv' log level.
///
/// You may want to use with '--log-file' to avoid polluting your terminal.
///
/// Setting $RUST_LOG also works, and takes precedence, but is not recommended. However, using
/// $RUST_LOG is the only way to get logs from before these options are parsed.
#[arg(
long,
short,
help_heading = super::OPTSET_DEBUGGING,
action = ArgAction::Count,
default_value = "0",
num_args = 0,
)]
pub verbose: u8,
/// Write diagnostic logs to a file
///
/// This writes diagnostic logs to a file, instead of the terminal, in JSON format. If a log
/// level was not already specified, this will set it to '-vvv'.
///
/// If a path is not provided, the default is the working directory. Note that with
/// '--ignore-nothing', the write events to the log will likely get picked up by Watchexec,
/// causing a loop; prefer setting a path outside of the watched directory.
///
/// If the path provided is a directory, a file will be created in that directory. The file name
/// will be the current date and time, in the format 'watchexec.YYYY-MM-DDTHH-MM-SSZ.log'.
#[arg(
long,
help_heading = super::OPTSET_DEBUGGING,
num_args = 0..=1,
default_missing_value = ".",
value_hint = ValueHint::AnyPath,
value_name = "PATH",
)]
pub log_file: Option<PathBuf>,
}
pub fn preargs() -> bool {
let mut log_on = false;
#[cfg(feature = "dev-console")]
match console_subscriber::try_init() {
Ok(_) => {
warn!("dev-console enabled");
log_on = true;
}
Err(e) => {
eprintln!("Failed to initialise tokio console, falling back to normal logging\n{e}")
}
}
if !log_on && var("RUST_LOG").is_ok() {
match tracing_subscriber::fmt::try_init() {
Ok(()) => {
warn!(RUST_LOG=%var("RUST_LOG").unwrap(), "logging configured from RUST_LOG");
log_on = true;
}
Err(e) => eprintln!("Failed to initialise logging with RUST_LOG, falling back\n{e}"),
}
}
log_on
}
pub async fn postargs(args: &LoggingArgs) -> Result<Option<WorkerGuard>> {
if args.verbose == 0 {
return Ok(None);
}
let (log_writer, guard) = if let Some(file) = &args.log_file {
let is_dir = metadata(&file).await.map_or(false, |info| info.is_dir());
let (dir, filename) = if is_dir {
(
file.to_owned(),
PathBuf::from(format!(
"watchexec.{}.log",
chrono::Utc::now().format("%Y-%m-%dT%H-%M-%SZ")
)),
)
} else if let (Some(parent), Some(file_name)) = (file.parent(), file.file_name()) {
(parent.into(), PathBuf::from(file_name))
} else {
bail!("Failed to determine log file name");
};
non_blocking(rolling::never(dir, filename))
} else {
non_blocking(stderr())
};
let mut builder = tracing_subscriber::fmt().with_env_filter(match args.verbose {
0 => unreachable!("checked by if earlier"),
1 => "warn",
2 => "info",
3 => "debug",
_ => "trace",
});
if args.verbose > 2 {
use tracing_subscriber::fmt::format::FmtSpan;
builder = builder.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE);
}
match if args.log_file.is_some() {
builder.json().with_writer(log_writer).try_init()
} else if args.verbose > 3 {
builder.pretty().with_writer(log_writer).try_init()
} else {
builder.with_writer(log_writer).try_init()
} {
Ok(()) => info!("logging initialised"),
Err(e) => eprintln!("Failed to initialise logging, continuing with none\n{e}"),
}
Ok(Some(guard))
}

708
crates/cli/src/config.rs Normal file
View file

@ -0,0 +1,708 @@
use std::{
borrow::Cow,
collections::HashMap,
env::var,
ffi::{OsStr, OsString},
fs::File,
io::{IsTerminal, Write},
process::Stdio,
sync::{
atomic::{AtomicBool, AtomicU8, Ordering},
Arc,
},
time::Duration,
};
use clearscreen::ClearScreen;
use miette::{miette, IntoDiagnostic, Report, Result};
use notify_rust::Notification;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use tokio::{process::Command as TokioCommand, time::sleep};
use tracing::{debug, debug_span, error, instrument, trace, trace_span, Instrument};
use watchexec::{
action::ActionHandler,
command::{Command, Program, Shell, SpawnOptions},
error::RuntimeError,
job::{CommandState, Job},
sources::fs::Watcher,
Config, ErrorHook, Id,
};
use watchexec_events::{Event, Keyboard, ProcessEnd, Tag};
use watchexec_signals::Signal;
use crate::{
args::{Args, ClearMode, ColourMode, EmitEvents, OnBusyUpdate, SignalMapping, WrapMode},
state::RotatingTempFile,
};
use crate::{emits::events_to_simple_format, state::State};
#[derive(Clone, Copy, Debug)]
struct OutputFlags {
quiet: bool,
colour: ColorChoice,
timings: bool,
bell: bool,
toast: bool,
}
pub fn make_config(args: &Args, state: &State) -> Result<Config> {
let _span = debug_span!("args-runtime").entered();
let config = Config::default();
config.on_error(|err: ErrorHook| {
if let RuntimeError::IoError {
about: "waiting on process group",
..
} = err.error
{
// "No child processes" and such
// these are often spurious, so condemn them to -v only
error!("{}", err.error);
return;
}
if cfg!(debug_assertions) {
eprintln!("[[{:?}]]", err.error);
}
eprintln!("[[Error (not fatal)]]\n{}", Report::new(err.error));
});
config.pathset(args.paths.clone());
config.throttle(args.debounce.0);
config.keyboard_events(args.stdin_quit);
if let Some(interval) = args.poll {
config.file_watcher(Watcher::Poll(interval.0));
}
let once = args.once;
let clear = args.screen_clear;
let emit_events_to = args.emit_events_to;
let emit_file = state.emit_file.clone();
if args.only_emit_events {
config.on_action(move |mut action| {
// if we got a terminate or interrupt signal, quit
if action
.signals()
.any(|sig| sig == Signal::Terminate || sig == Signal::Interrupt)
{
// no need to be graceful as there's no commands
action.quit();
return action;
}
// clear the screen before printing events
if let Some(mode) = clear {
match mode {
ClearMode::Clear => {
clearscreen::clear().ok();
}
ClearMode::Reset => {
reset_screen();
}
}
}
match emit_events_to {
EmitEvents::Stdio => {
println!(
"{}",
events_to_simple_format(action.events.as_ref()).unwrap_or_default()
);
}
EmitEvents::JsonStdio => {
for event in action.events.iter().filter(|e| !e.is_empty()) {
println!("{}", serde_json::to_string(event).unwrap_or_default());
}
}
other => unreachable!(
"emit_events_to should have been validated earlier: {:?}",
other
),
}
action
});
return Ok(config);
}
let delay_run = args.delay_run.map(|ts| ts.0);
let on_busy = args.on_busy_update;
let stdin_quit = args.stdin_quit;
let signal = args.signal;
let stop_signal = args.stop_signal;
let stop_timeout = args.stop_timeout.0;
let print_events = args.print_events;
let outflags = OutputFlags {
quiet: args.quiet,
colour: match args.color {
ColourMode::Auto if !std::io::stdin().is_terminal() => ColorChoice::Never,
ColourMode::Auto => ColorChoice::Auto,
ColourMode::Always => ColorChoice::Always,
ColourMode::Never => ColorChoice::Never,
},
timings: args.timings,
bell: args.bell,
toast: args.notify,
};
let workdir = Arc::new(args.workdir.clone());
let mut add_envs = HashMap::new();
for pair in &args.env {
if let Some((k, v)) = pair.split_once('=') {
add_envs.insert(k.to_owned(), OsString::from(v));
} else {
return Err(miette!("{pair} is not in key=value format"));
}
}
debug!(
?add_envs,
"additional environment variables to add to command"
);
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(),
);
let queued = Arc::new(AtomicBool::new(false));
let quit_again = Arc::new(AtomicU8::new(0));
config.on_action_async(move |mut action| {
let add_envs = add_envs.clone();
let command = command.clone();
let emit_file = emit_file.clone();
let queued = queued.clone();
let quit_again = quit_again.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 queued = queued.clone();
let quit_again = quit_again.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();
if let Some(ref workdir) = workdir.as_ref() {
debug!(?workdir, "set command workdir");
command.command_mut().current_dir(workdir);
}
emit_events_to_command(
command.command_mut(),
events,
emit_file,
emit_events_to,
add_envs,
);
});
let show_events = {
let events = action.events.clone();
move || {
if print_events {
trace!("print events to stderr");
for (n, event) in events.iter().enumerate() {
eprintln!("[EVENT {n}] {event}");
}
}
}
};
let clear_screen = {
let events = action.events.clone();
move || {
if let Some(mode) = clear {
match mode {
ClearMode::Clear => {
clearscreen::clear().ok();
debug!("cleared screen");
}
ClearMode::Reset => {
reset_screen();
debug!("hard-reset screen");
}
}
}
// re-show events after clearing
if print_events {
trace!("print events to stderr");
for (n, event) in events.iter().enumerate() {
eprintln!("[EVENT {n}] {event}");
}
}
}
};
let quit = |mut action: ActionHandler| {
match quit_again.fetch_add(1, Ordering::Relaxed) {
0 => {
eprintln!("[Waiting {stop_timeout:?} for processes to exit before stopping...]");
// eprintln!("[Waiting {stop_timeout:?} for processes to exit before stopping... Ctrl-C again to exit faster]");
// see TODO in action/worker.rs
action.quit_gracefully(
stop_signal.unwrap_or(Signal::Terminate),
stop_timeout,
);
}
1 => {
action.quit_gracefully(Signal::ForceStop, Duration::ZERO);
}
_ => {
action.quit();
}
}
action
};
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;
return quit(action);
}
let is_keyboard_eof = action
.events
.iter()
.any(|e| e.tags.contains(&Tag::Keyboard(Keyboard::Eof)));
if stdin_quit && is_keyboard_eof {
debug!("keyboard EOF, quit");
show_events();
return quit(action);
}
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();
return quit(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;
}
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| {
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| {
clear_screen();
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| {
clear_screen();
setup_process(
innerjob.clone(),
context.command.clone(),
outflags,
)
});
}
OnBusyUpdate::Queue => {
let job = job.clone();
let already_queued =
queued.fetch_or(true, Ordering::SeqCst);
if already_queued {
debug!("next start is already queued, do nothing");
} else {
debug!("queueing next start of job");
tokio::spawn({
let queued = queued.clone();
async move {
trace!("waiting for job to finish");
job.to_wait().await;
trace!("job finished, starting queued");
job.start();
job.run(move |context| {
clear_screen();
setup_process(
innerjob.clone(),
context.command.clone(),
outflags,
)
})
.await;
trace!("resetting queued state");
queued.store(false, Ordering::SeqCst);
}
});
}
}
}
} else {
trace!("job is not running, start it");
job.start();
job.run(move |context| {
clear_screen();
setup_process(
innerjob.clone(),
context.command.clone(),
outflags,
)
});
}
})
}
});
action
}
.instrument(trace_span!("action handler")),
)
});
Ok(config)
}
#[instrument(level = "debug")]
fn interpret_command_args(args: &Args) -> Result<Arc<Command>> {
let mut cmd = args.command.clone();
if cmd.is_empty() {
panic!("(clap) Bug: command is not present");
}
let shell = if args.no_shell {
None
} else {
let shell = args.shell.clone().or_else(|| var("SHELL").ok());
match shell
.as_deref()
.or_else(|| {
if cfg!(not(windows)) {
Some("sh")
} else if var("POWERSHELL_DISTRIBUTION_CHANNEL").is_ok()
&& (which::which("pwsh").is_ok() || which::which("pwsh.exe").is_ok())
{
trace!("detected pwsh");
Some("pwsh")
} else if var("PSModulePath").is_ok()
&& (which::which("powershell").is_ok()
|| which::which("powershell.exe").is_ok())
{
trace!("detected powershell");
Some("powershell")
} else {
Some("cmd")
}
})
.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"))),
})
}
}
};
let program = if let Some(shell) = shell {
Program::Shell {
shell,
command: cmd.join(" "),
args: Vec::new(),
}
} else {
Program::Exec {
prog: cmd.remove(0).into(),
args: cmd,
}
};
Ok(Arc::new(Command {
program,
options: SpawnOptions {
grouped: matches!(args.wrap_process, WrapMode::Group),
session: matches!(args.wrap_process, WrapMode::Session),
..Default::default()
},
}))
}
#[instrument(level = "trace")]
fn setup_process(job: Job, command: Arc<Command>, outflags: OutputFlags) {
if outflags.toast {
Notification::new()
.summary("Watchexec: change detected")
.body(&format!("Running {command}"))
.show()
.map_or_else(
|err| {
eprintln!("[[Failed to send desktop notification: {err}]]");
},
drop,
);
}
if !outflags.quiet {
let mut stderr = StandardStream::stderr(outflags.colour);
stderr.reset().ok();
stderr
.set_color(ColorSpec::new().set_fg(Some(Color::Green)))
.ok();
writeln!(&mut stderr, "[Running: {command}]").ok();
stderr.reset().ok();
}
tokio::spawn(async move {
job.to_wait().await;
job.run(move |context| end_of_process(context.current, outflags));
});
}
#[instrument(level = "trace")]
fn end_of_process(state: &CommandState, outflags: OutputFlags) {
let CommandState::Finished {
status,
started,
finished,
} = state
else {
return;
};
let duration = *finished - *started;
let timing = if outflags.timings {
format!(", lasted {duration:?}")
} else {
String::new()
};
let (msg, fg) = match status {
ProcessEnd::ExitError(code) => (format!("Command exited with {code}{timing}"), Color::Red),
ProcessEnd::ExitSignal(sig) => {
(format!("Command killed by {sig:?}{timing}"), Color::Magenta)
}
ProcessEnd::ExitStop(sig) => (format!("Command stopped by {sig:?}{timing}"), Color::Blue),
ProcessEnd::Continued => (format!("Command continued{timing}"), Color::Cyan),
ProcessEnd::Exception(ex) => (
format!("Command ended by exception {ex:#x}{timing}"),
Color::Yellow,
),
ProcessEnd::Success => (format!("Command was successful{timing}"), Color::Green),
};
if outflags.toast {
Notification::new()
.summary("Watchexec: command ended")
.body(&msg)
.show()
.map_or_else(
|err| {
eprintln!("[[Failed to send desktop notification: {err}]]");
},
drop,
);
}
if !outflags.quiet {
let mut stderr = StandardStream::stderr(outflags.colour);
stderr.reset().ok();
stderr.set_color(ColorSpec::new().set_fg(Some(fg))).ok();
writeln!(&mut stderr, "[{msg}]").ok();
stderr.reset().ok();
}
if outflags.bell {
let mut stdout = std::io::stdout();
stdout.write_all(b"\x07").ok();
stdout.flush().ok();
}
}
#[instrument(level = "trace")]
fn emit_events_to_command(
command: &mut TokioCommand,
events: Arc<[Event]>,
emit_file: RotatingTempFile,
emit_events_to: EmitEvents,
mut add_envs: HashMap<String, OsString>,
) {
use crate::emits::*;
let mut stdin = None;
match emit_events_to {
EmitEvents::Environment => {
add_envs.extend(emits_to_environment(&events));
}
EmitEvents::Stdio => match emits_to_file(&emit_file, &events)
.and_then(|path| File::open(path).into_diagnostic())
{
Ok(file) => {
stdin.replace(Stdio::from(file));
}
Err(err) => {
error!("Failed to write events to stdin, continuing without it: {err}");
}
},
EmitEvents::File => match emits_to_file(&emit_file, &events) {
Ok(path) => {
add_envs.insert("WATCHEXEC_EVENTS_FILE".into(), path.into());
}
Err(err) => {
error!("Failed to write WATCHEXEC_EVENTS_FILE, continuing without it: {err}");
}
},
EmitEvents::JsonStdio => match emits_to_json_file(&emit_file, &events)
.and_then(|path| File::open(path).into_diagnostic())
{
Ok(file) => {
stdin.replace(Stdio::from(file));
}
Err(err) => {
error!("Failed to write events to stdin, continuing without it: {err}");
}
},
EmitEvents::JsonFile => match emits_to_json_file(&emit_file, &events) {
Ok(path) => {
add_envs.insert("WATCHEXEC_EVENTS_FILE".into(), path.into());
}
Err(err) => {
error!("Failed to write WATCHEXEC_EVENTS_FILE, continuing without it: {err}");
}
},
EmitEvents::None => {}
}
for (k, v) in add_envs {
debug!(?k, ?v, "inserting environment variable");
command.env(k, v);
}
if let Some(stdin) = stdin {
debug!("set command stdin");
command.stdin(stdin);
}
}
pub(crate) fn reset_screen() {
for cs in [
ClearScreen::WindowsCooked,
ClearScreen::WindowsVt,
ClearScreen::VtLeaveAlt,
ClearScreen::VtWellDone,
ClearScreen::default(),
] {
cs.clear().ok();
}
}

222
crates/cli/src/dirs.rs Normal file
View file

@ -0,0 +1,222 @@
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use ignore_files::{IgnoreFile, IgnoreFilesFromOriginArgs};
use miette::{miette, IntoDiagnostic, Result};
use project_origins::ProjectType;
use tokio::fs::canonicalize;
use tracing::{debug, info, warn};
use watchexec::paths::common_prefix;
use crate::args::Args;
pub async fn project_origin(args: &Args) -> Result<PathBuf> {
let project_origin = if let Some(origin) = &args.project_origin {
debug!(?origin, "project origin override");
canonicalize(origin).await.into_diagnostic()?
} else {
let homedir = match dirs::home_dir() {
None => None,
Some(dir) => Some(canonicalize(dir).await.into_diagnostic()?),
};
debug!(?homedir, "home directory");
let homedir_requested = homedir.as_ref().map_or(false, |home| {
args.paths
.binary_search_by_key(home, |w| PathBuf::from(w.clone()))
.is_ok()
});
debug!(
?homedir_requested,
"resolved whether the homedir is explicitly requested"
);
let mut origins = HashSet::new();
for path in &args.paths {
origins.extend(project_origins::origins(path).await);
}
match (homedir, homedir_requested) {
(Some(ref dir), false) if origins.contains(dir) => {
debug!("removing homedir from origins");
origins.remove(dir);
}
_ => {}
}
if origins.is_empty() {
debug!("no origins, using current directory");
origins.insert(args.workdir.clone().unwrap());
}
debug!(?origins, "resolved all project origins");
// This canonicalize is probably redundant
canonicalize(
common_prefix(&origins)
.ok_or_else(|| miette!("no common prefix, but this should never fail"))?,
)
.await
.into_diagnostic()?
};
debug!(?project_origin, "resolved common/project origin");
Ok(project_origin)
}
pub async fn vcs_types(origin: &Path) -> Vec<ProjectType> {
let vcs_types = project_origins::types(origin)
.await
.into_iter()
.filter(|pt| pt.is_vcs())
.collect::<Vec<_>>();
info!(?vcs_types, "effective vcs types");
vcs_types
}
pub async fn ignores(args: &Args, vcs_types: &[ProjectType]) -> Result<Vec<IgnoreFile>> {
let origin = args.project_origin.clone().unwrap();
let mut skip_git_global_excludes = false;
let mut ignores = if args.no_project_ignore {
Vec::new()
} else {
let ignore_files = args.ignore_files.iter().map(|path| {
if path.is_absolute() {
path.into()
} else {
origin.join(path)
}
});
let (mut ignores, errors) = ignore_files::from_origin(
IgnoreFilesFromOriginArgs::new_unchecked(
&origin,
args.paths.iter().map(PathBuf::from),
ignore_files,
)
.canonicalise()
.await
.into_diagnostic()?,
)
.await;
for err in errors {
warn!("while discovering project-local ignore files: {}", err);
}
debug!(?ignores, "discovered ignore files from project origin");
if !vcs_types.is_empty() {
ignores = ignores
.into_iter()
.filter(|ig| match ig.applies_to {
Some(pt) if pt.is_vcs() => vcs_types.contains(&pt),
_ => true,
})
.inspect(|ig| {
if let IgnoreFile {
applies_to: Some(ProjectType::Git),
applies_in: None,
..
} = ig
{
warn!("project git config overrides the global excludes");
skip_git_global_excludes = true;
}
})
.collect::<Vec<_>>();
debug!(?ignores, "filtered ignores to only those for project vcs");
}
ignores
};
let global_ignores = if args.no_global_ignore {
Vec::new()
} else {
let (mut global_ignores, errors) = ignore_files::from_environment(Some("watchexec")).await;
for err in errors {
warn!("while discovering global ignore files: {}", err);
}
debug!(?global_ignores, "discovered ignore files from environment");
if skip_git_global_excludes {
global_ignores = global_ignores
.into_iter()
.filter(|gig| {
!matches!(
gig,
IgnoreFile {
applies_to: Some(ProjectType::Git),
applies_in: None,
..
}
)
})
.collect::<Vec<_>>();
debug!(
?global_ignores,
"filtered global ignores to exclude global git ignores"
);
}
global_ignores
};
ignores.extend(global_ignores.into_iter().filter(|ig| match ig.applies_to {
Some(pt) if pt.is_vcs() => vcs_types.contains(&pt),
_ => true,
}));
debug!(
?ignores,
?vcs_types,
"combined and applied overall vcs filter over ignores"
);
ignores.extend(args.ignore_files.iter().map(|ig| IgnoreFile {
applies_to: None,
applies_in: None,
path: ig.clone(),
}));
debug!(
?ignores,
?args.ignore_files,
"combined with ignore files from command line / env"
);
if args.no_project_ignore {
ignores = ignores
.into_iter()
.filter(|ig| {
!ig.applies_in
.as_ref()
.map_or(false, |p| p.starts_with(&origin))
})
.collect::<Vec<_>>();
debug!(
?ignores,
"filtered ignores to exclude project-local ignores"
);
}
if args.no_global_ignore {
ignores = ignores
.into_iter()
.filter(|ig| ig.applies_in.is_some())
.collect::<Vec<_>>();
debug!(?ignores, "filtered ignores to exclude global ignores");
}
if args.no_vcs_ignore {
ignores = ignores
.into_iter()
.filter(|ig| ig.applies_to.is_none())
.collect::<Vec<_>>();
debug!(?ignores, "filtered ignores to exclude VCS-specific ignores");
}
info!(files=?ignores.iter().map(|ig| ig.path.as_path()).collect::<Vec<_>>(), "found some ignores");
Ok(ignores)
}

71
crates/cli/src/emits.rs Normal file
View file

@ -0,0 +1,71 @@
use std::{ffi::OsString, fmt::Write, path::PathBuf};
use miette::{IntoDiagnostic, Result};
use watchexec::paths::summarise_events_to_env;
use watchexec_events::{filekind::FileEventKind, Event, Tag};
use crate::state::RotatingTempFile;
pub fn emits_to_environment(events: &[Event]) -> impl Iterator<Item = (String, OsString)> {
summarise_events_to_env(events.iter())
.into_iter()
.map(|(k, v)| (format!("WATCHEXEC_{k}_PATH"), v))
}
pub fn events_to_simple_format(events: &[Event]) -> Result<String> {
let mut buf = String::new();
for event in events {
let feks = event
.tags
.iter()
.filter_map(|tag| match tag {
Tag::FileEventKind(kind) => Some(kind),
_ => None,
})
.collect::<Vec<_>>();
for path in event.paths().map(|(p, _)| p) {
if feks.is_empty() {
writeln!(&mut buf, "other:{}", path.to_string_lossy()).into_diagnostic()?;
continue;
}
for fek in &feks {
writeln!(
&mut buf,
"{}:{}",
match fek {
FileEventKind::Any | FileEventKind::Other => "other",
FileEventKind::Access(_) => "access",
FileEventKind::Create(_) => "create",
FileEventKind::Modify(_) => "modify",
FileEventKind::Remove(_) => "remove",
},
path.to_string_lossy()
)
.into_diagnostic()?;
}
}
}
Ok(buf)
}
pub fn emits_to_file(target: &RotatingTempFile, events: &[Event]) -> Result<PathBuf> {
target.rotate()?;
target.write(events_to_simple_format(events)?.as_bytes())?;
Ok(target.path())
}
pub fn emits_to_json_file(target: &RotatingTempFile, events: &[Event]) -> Result<PathBuf> {
target.rotate()?;
for event in events {
if event.is_empty() {
continue;
}
target.write(&serde_json::to_vec(event).into_diagnostic()?)?;
target.write(b"\n")?;
}
Ok(target.path())
}

188
crates/cli/src/filterer.rs Normal file
View file

@ -0,0 +1,188 @@
use std::{
ffi::OsString,
path::{Path, PathBuf, MAIN_SEPARATOR},
sync::Arc,
};
use miette::{IntoDiagnostic, Result};
use tokio::io::{AsyncBufReadExt, BufReader};
use tracing::{info, trace, trace_span};
use watchexec::{error::RuntimeError, filter::Filterer};
use watchexec_events::{
filekind::{FileEventKind, ModifyKind},
Event, Priority, Tag,
};
use watchexec_filterer_globset::GlobsetFilterer;
use crate::args::{Args, FsEvent};
pub(crate) mod parse;
mod proglib;
mod progs;
mod syncval;
/// A custom filterer that combines the library's Globset filterer and a switch for --no-meta
#[derive(Debug)]
pub struct WatchexecFilterer {
inner: GlobsetFilterer,
fs_events: Vec<FsEvent>,
progs: Option<progs::FilterProgs>,
}
impl Filterer for WatchexecFilterer {
#[tracing::instrument(level = "trace", skip(self))]
fn check_event(&self, event: &Event, priority: Priority) -> Result<bool, RuntimeError> {
for tag in &event.tags {
if let Tag::FileEventKind(fek) = tag {
let normalised = match fek {
FileEventKind::Access(_) => FsEvent::Access,
FileEventKind::Modify(ModifyKind::Name(_)) => FsEvent::Rename,
FileEventKind::Modify(ModifyKind::Metadata(_)) => FsEvent::Metadata,
FileEventKind::Modify(_) => FsEvent::Modify,
FileEventKind::Create(_) => FsEvent::Create,
FileEventKind::Remove(_) => FsEvent::Remove,
_ => continue,
};
trace!(allowed=?self.fs_events, this=?normalised, "check against fs event filter");
if !self.fs_events.contains(&normalised) {
return Ok(false);
}
}
}
trace!("check against original event");
if !self.inner.check_event(event, priority)? {
return Ok(false);
}
if let Some(progs) = &self.progs {
trace!("check against program filters");
if !progs.check(event)? {
return Ok(false);
}
}
Ok(true)
}
}
impl WatchexecFilterer {
/// Create a new filterer from the given arguments
pub async fn new(args: &Args) -> Result<Arc<Self>> {
let project_origin = args.project_origin.clone().unwrap();
let workdir = args.workdir.clone().unwrap();
let ignore_files = if args.no_discover_ignore {
Vec::new()
} else {
let vcs_types = crate::dirs::vcs_types(&project_origin).await;
crate::dirs::ignores(args, &vcs_types).await?
};
let mut ignores = Vec::new();
if !args.no_default_ignore {
ignores.extend([
(format!("**{MAIN_SEPARATOR}.DS_Store"), None),
(String::from("watchexec.*.log"), None),
(String::from("*.py[co]"), None),
(String::from("#*#"), None),
(String::from(".#*"), None),
(String::from(".*.kate-swp"), None),
(String::from(".*.sw?"), None),
(String::from(".*.sw?x"), None),
(format!("**{MAIN_SEPARATOR}.bzr{MAIN_SEPARATOR}**"), None),
(format!("**{MAIN_SEPARATOR}_darcs{MAIN_SEPARATOR}**"), None),
(
format!("**{MAIN_SEPARATOR}.fossil-settings{MAIN_SEPARATOR}**"),
None,
),
(format!("**{MAIN_SEPARATOR}.git{MAIN_SEPARATOR}**"), None),
(format!("**{MAIN_SEPARATOR}.hg{MAIN_SEPARATOR}**"), None),
(format!("**{MAIN_SEPARATOR}.pijul{MAIN_SEPARATOR}**"), None),
(format!("**{MAIN_SEPARATOR}.svn{MAIN_SEPARATOR}**"), None),
]);
}
let whitelist = args
.paths
.iter()
.map(|p| p.into())
.filter(|p: &PathBuf| p.is_file());
let mut filters = args
.filter_patterns
.iter()
.map(|f| (f.to_owned(), Some(workdir.clone())))
.collect::<Vec<_>>();
for filter_file in &args.filter_files {
filters.extend(read_filter_file(filter_file).await?);
}
ignores.extend(
args.ignore_patterns
.iter()
.map(|f| (f.to_owned(), Some(workdir.clone()))),
);
let exts = args
.filter_extensions
.iter()
.map(|e| OsString::from(e.strip_prefix('.').unwrap_or(e)));
info!("initialising Globset filterer");
Ok(Arc::new(Self {
inner: GlobsetFilterer::new(
project_origin,
filters,
ignores,
whitelist,
ignore_files,
exts,
)
.await
.into_diagnostic()?,
fs_events: args.filter_fs_events.clone(),
progs: if args.filter_programs_parsed.is_empty() {
None
} else {
Some(progs::FilterProgs::new(args)?)
},
}))
}
}
async fn read_filter_file(path: &Path) -> Result<Vec<(String, Option<PathBuf>)>> {
let _span = trace_span!("loading filter file", ?path).entered();
let file = tokio::fs::File::open(path).await.into_diagnostic()?;
let metadata_len = file
.metadata()
.await
.map(|m| usize::try_from(m.len()))
.unwrap_or(Ok(0))
.into_diagnostic()?;
let filter_capacity = if metadata_len == 0 {
0
} else {
metadata_len / 20
};
let mut filters = Vec::with_capacity(filter_capacity);
let reader = BufReader::new(file);
let mut lines = reader.lines();
while let Some(line) = lines.next_line().await.into_diagnostic()? {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
trace!(?line, "adding filter line");
filters.push((line.to_owned(), Some(path.to_owned())));
}
Ok(filters)
}

View file

@ -0,0 +1,17 @@
use miette::{miette, Result};
pub fn parse_filter_program((n, prog): (usize, String)) -> Result<jaq_syn::Main> {
let parser = jaq_parse::main();
let (main, errs) = jaq_parse::parse(&prog, parser);
if !errs.is_empty() {
let errs = errs
.into_iter()
.map(|err| err.to_string())
.collect::<Vec<_>>()
.join("\n");
return Err(miette!("{}", errs).wrap_err(format!("failed to load filter program #{}", n)));
}
main.ok_or_else(|| miette!("failed to load filter program #{} (no reason given)", n))
}

View file

@ -0,0 +1,27 @@
use jaq_interpret::ParseCtx;
use miette::Result;
use tracing::debug;
mod file;
mod hash;
mod kv;
mod macros;
mod output;
pub fn jaq_lib() -> Result<ParseCtx> {
let mut jaq = ParseCtx::new(Vec::new());
debug!("loading jaq core library");
jaq.insert_natives(jaq_core::core());
debug!("loading jaq std library");
jaq.insert_defs(jaq_std::std());
debug!("loading jaq watchexec library");
file::load(&mut jaq);
hash::load(&mut jaq);
kv::load(&mut jaq);
output::load(&mut jaq);
Ok(jaq)
}

View file

@ -0,0 +1,173 @@
use std::{
fs::{metadata, File, FileType, Metadata},
io::{BufReader, Read},
iter::once,
time::{SystemTime, UNIX_EPOCH},
};
use jaq_interpret::{Error, Native, ParseCtx, Val};
use serde_json::{json, Value};
use tracing::{debug, error, trace};
use super::macros::*;
pub fn load(jaq: &mut ParseCtx) {
trace!("jaq: add file_read filter");
jaq.insert_native(
"file_read".into(),
1,
Native::new({
move |args, (ctx, val)| {
let path = match &val {
Val::Str(v) => v.to_string(),
_ => return_err!(Err(Error::str("expected string (path) but got {val:?}"))),
};
let bytes = match int_arg!(args, 0, ctx, &val) {
Ok(v) => v,
Err(e) => return_err!(Err(e)),
};
Box::new(once(Ok(match File::open(&path) {
Ok(file) => {
let buf_reader = BufReader::new(file);
let mut limited = buf_reader.take(bytes);
let mut buffer = String::with_capacity(bytes as _);
match limited.read_to_string(&mut buffer) {
Ok(read) => {
debug!("jaq: read {read} bytes from {path:?}");
Val::Str(buffer.into())
}
Err(err) => {
error!("jaq: failed to read from {path:?}: {err:?}");
Val::Null
}
}
}
Err(err) => {
error!("jaq: failed to open file {path:?}: {err:?}");
Val::Null
}
})))
}
}),
);
trace!("jaq: add file_meta filter");
jaq.insert_native(
"file_meta".into(),
0,
Native::new({
move |_, (_, val)| {
let path = match &val {
Val::Str(v) => v.to_string(),
_ => return_err!(Err(Error::str("expected string (path) but got {val:?}"))),
};
Box::new(once(Ok(match metadata(&path) {
Ok(meta) => Val::from(json_meta(meta)),
Err(err) => {
error!("jaq: failed to open {path:?}: {err:?}");
Val::Null
}
})))
}
}),
);
trace!("jaq: add file_size filter");
jaq.insert_native(
"file_size".into(),
0,
Native::new({
move |_, (_, val)| {
let path = match &val {
Val::Str(v) => v.to_string(),
_ => return_err!(Err(Error::str("expected string (path) but got {val:?}"))),
};
Box::new(once(Ok(match metadata(&path) {
Ok(meta) => Val::Int(meta.len() as _),
Err(err) => {
error!("jaq: failed to open {path:?}: {err:?}");
Val::Null
}
})))
}
}),
);
}
fn json_meta(meta: Metadata) -> Value {
let perms = meta.permissions();
let mut val = json!({
"type": filetype_str(meta.file_type()),
"size": meta.len(),
"modified": fs_time(meta.modified()),
"accessed": fs_time(meta.accessed()),
"created": fs_time(meta.created()),
"dir": meta.is_dir(),
"file": meta.is_file(),
"symlink": meta.is_symlink(),
"readonly": perms.readonly(),
});
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let map = val.as_object_mut().unwrap();
map.insert(
"mode".to_string(),
Value::String(format!("{:o}", perms.mode())),
);
map.insert("mode_byte".to_string(), Value::from(perms.mode()));
map.insert(
"executable".to_string(),
Value::Bool(perms.mode() & 0o111 != 0),
);
}
val
}
fn filetype_str(filetype: FileType) -> &'static str {
#[cfg(unix)]
{
use std::os::unix::fs::FileTypeExt;
if filetype.is_char_device() {
return "char";
} else if filetype.is_block_device() {
return "block";
} else if filetype.is_fifo() {
return "fifo";
} else if filetype.is_socket() {
return "socket";
}
}
#[cfg(windows)]
{
use std::os::windows::fs::FileTypeExt;
if filetype.is_symlink_dir() {
return "symdir";
} else if filetype.is_symlink_file() {
return "symfile";
}
}
if filetype.is_dir() {
"dir"
} else if filetype.is_file() {
"file"
} else if filetype.is_symlink() {
"symlink"
} else {
"unknown"
}
}
fn fs_time(time: std::io::Result<SystemTime>) -> Option<u64> {
time.ok()
.and_then(|time| time.duration_since(UNIX_EPOCH).ok())
.map(|dur| dur.as_secs())
}

View file

@ -0,0 +1,62 @@
use std::{fs::File, io::Read, iter::once};
use jaq_interpret::{Error, Native, ParseCtx, Val};
use tracing::{debug, error, trace};
use super::macros::*;
pub fn load(jaq: &mut ParseCtx) {
trace!("jaq: add hash filter");
jaq.insert_native(
"hash".into(),
0,
Native::new({
move |_, (_, val)| {
let string = match &val {
Val::Str(v) => v.to_string(),
_ => return_err!(Err(Error::str("expected string but got {val:?}"))),
};
Box::new(once(Ok(Val::Str(
blake3::hash(string.as_bytes()).to_hex().to_string().into(),
))))
}
}),
);
trace!("jaq: add file_hash filter");
jaq.insert_native(
"file_hash".into(),
0,
Native::new({
move |_, (_, val)| {
let path = match &val {
Val::Str(v) => v.to_string(),
_ => return_err!(Err(Error::str("expected string but got {val:?}"))),
};
Box::new(once(Ok(match File::open(&path) {
Ok(mut file) => {
const BUFFER_SIZE: usize = 1024 * 1024;
let mut hasher = blake3::Hasher::new();
let mut buf = vec![0; BUFFER_SIZE];
while let Ok(bytes) = file.read(&mut buf) {
debug!("jaq: read {bytes} bytes from {path:?}");
if bytes == 0 {
break;
}
hasher.update(&buf[..bytes]);
buf = vec![0; BUFFER_SIZE];
}
Val::Str(hasher.finalize().to_hex().to_string().into())
}
Err(err) => {
error!("jaq: failed to open file {path:?}: {err:?}");
Val::Null
}
})))
}
}),
);
}

View file

@ -0,0 +1,69 @@
use std::{iter::once, sync::Arc};
use dashmap::DashMap;
use jaq_interpret::{Error, Native, ParseCtx, Val};
use once_cell::sync::OnceCell;
use tracing::trace;
use crate::filterer::syncval::SyncVal;
use super::macros::*;
type KvStore = Arc<DashMap<String, SyncVal>>;
fn kv_store() -> KvStore {
static KV_STORE: OnceCell<KvStore> = OnceCell::new();
KV_STORE.get_or_init(|| KvStore::default()).clone()
}
pub fn load(jaq: &mut ParseCtx) {
trace!("jaq: add kv_clear filter");
jaq.insert_native(
"kv_clear".into(),
0,
Native::new({
move |_, (_, val)| {
let kv = kv_store();
kv.clear();
Box::new(once(Ok(val)))
}
}),
);
trace!("jaq: add kv_store filter");
jaq.insert_native(
"kv_store".into(),
1,
Native::new({
move |args, (ctx, val)| {
let kv = kv_store();
let key = match string_arg!(args, 0, ctx, val) {
Ok(v) => v,
Err(e) => return_err!(Err(e)),
};
kv.insert(key, (&val).into());
Box::new(once(Ok(val)))
}
}),
);
trace!("jaq: add kv_fetch filter");
jaq.insert_native(
"kv_fetch".into(),
1,
Native::new({
move |args, (ctx, val)| {
let kv = kv_store();
let key = match string_arg!(args, 0, ctx, val) {
Ok(v) => v,
Err(e) => return_err!(Err(e)),
};
Box::new(once(Ok(kv
.get(&key)
.map(|val| val.value().into())
.unwrap_or(Val::Null))))
}
}),
);
}

View file

@ -0,0 +1,30 @@
macro_rules! return_err {
($err:expr) => {
return Box::new(once($err))
};
}
pub(crate) use return_err;
macro_rules! string_arg {
($args:expr, $n:expr, $ctx:expr, $val:expr) => {
match ::jaq_interpret::FilterT::run($args.get($n), ($ctx.clone(), $val.clone())).next() {
Some(Ok(Val::Str(v))) => Ok(v.to_string()),
Some(Ok(val)) => Err(Error::str(format!("expected string but got {val:?}"))),
Some(Err(e)) => Err(e),
None => Err(Error::str("value expected but none found")),
}
};
}
pub(crate) use string_arg;
macro_rules! int_arg {
($args:expr, $n:expr, $ctx:expr, $val:expr) => {
match ::jaq_interpret::FilterT::run($args.get($n), ($ctx.clone(), $val.clone())).next() {
Some(Ok(Val::Int(v))) => Ok(v as _),
Some(Ok(val)) => Err(Error::str(format!("expected int but got {val:?}"))),
Some(Err(e)) => Err(e),
None => Err(Error::str("value expected but none found")),
}
};
}
pub(crate) use int_arg;

View file

@ -0,0 +1,83 @@
use std::iter::once;
use jaq_interpret::{Error, Native, ParseCtx, Val};
use tracing::{debug, error, info, trace, warn};
use super::macros::*;
macro_rules! log_action {
($level:expr, $val:expr) => {
match $level.to_ascii_lowercase().as_str() {
"trace" => trace!("jaq: {}", $val),
"debug" => debug!("jaq: {}", $val),
"info" => info!("jaq: {}", $val),
"warn" => warn!("jaq: {}", $val),
"error" => error!("jaq: {}", $val),
_ => return_err!(Err(Error::str("invalid log level"))),
}
};
}
pub fn load(jaq: &mut ParseCtx) {
trace!("jaq: add log filter");
jaq.insert_native(
"log".into(),
1,
Native::with_update(
|args, (ctx, val)| {
let level = match string_arg!(args, 0, ctx, val) {
Ok(v) => v,
Err(e) => return_err!(Err(e)),
};
log_action!(level, val);
// passthrough
Box::new(once(Ok(val)))
},
|args, (ctx, val), _| {
let level = match string_arg!(args, 0, ctx, val) {
Ok(v) => v,
Err(e) => return_err!(Err(e)),
};
log_action!(level, val);
// passthrough
Box::new(once(Ok(val)))
},
),
);
trace!("jaq: add printout filter");
jaq.insert_native(
"printout".into(),
0,
Native::with_update(
|_, (_, val)| {
println!("{}", val);
Box::new(once(Ok(val)))
},
|_, (_, val), _| {
println!("{}", val);
Box::new(once(Ok(val)))
},
),
);
trace!("jaq: add printerr filter");
jaq.insert_native(
"printerr".into(),
0,
Native::with_update(
|_, (_, val)| {
eprintln!("{}", val);
Box::new(once(Ok(val)))
},
|_, (_, val), _| {
eprintln!("{}", val);
Box::new(once(Ok(val)))
},
),
);
}

View file

@ -0,0 +1,143 @@
use std::{iter::empty, marker::PhantomData};
use jaq_interpret::{Ctx, FilterT, RcIter, Val};
use miette::miette;
use tokio::{
sync::{mpsc, oneshot},
task::{block_in_place, spawn_blocking},
};
use tracing::{error, trace, warn};
use watchexec::error::RuntimeError;
use watchexec_events::Event;
use crate::args::Args;
const BUFFER: usize = 128;
#[derive(Debug)]
pub struct FilterProgs {
channel: Requester<Event, bool>,
}
#[derive(Debug, Clone)]
pub struct Requester<S, R> {
sender: mpsc::Sender<(S, oneshot::Sender<R>)>,
_receiver: PhantomData<R>,
}
impl<S, R> Requester<S, R>
where
S: Send + Sync,
R: Send + Sync,
{
pub fn new(capacity: usize) -> (Self, mpsc::Receiver<(S, oneshot::Sender<R>)>) {
let (sender, receiver) = mpsc::channel(capacity);
(
Self {
sender,
_receiver: PhantomData,
},
receiver,
)
}
pub fn call(&self, value: S) -> Result<R, RuntimeError> {
// FIXME: this should really be async with a timeout, but that needs filtering in general
// to be async, which should be done at some point
block_in_place(|| {
let (sender, receiver) = oneshot::channel();
self.sender.blocking_send((value, sender)).map_err(|err| {
RuntimeError::External(miette!("filter progs internal channel: {}", err).into())
})?;
receiver
.blocking_recv()
.map_err(|err| RuntimeError::External(Box::new(err)))
})
}
}
impl FilterProgs {
pub fn check(&self, event: &Event) -> Result<bool, RuntimeError> {
self.channel.call(event.clone())
}
pub fn new(args: &Args) -> miette::Result<Self> {
let progs = args.filter_programs_parsed.clone();
eprintln!(
"EXPERIMENTAL: filter programs are unstable and may change/vanish without notice"
);
let (requester, mut receiver) = Requester::<Event, bool>::new(BUFFER);
let task =
spawn_blocking(move || {
'chan: while let Some((event, sender)) = receiver.blocking_recv() {
let val = serde_json::to_value(&event)
.map_err(|err| miette!("failed to serialize event: {}", err))
.map(Val::from)?;
for (n, prog) in progs.iter().enumerate() {
trace!(?n, "trying filter program");
let mut jaq = super::proglib::jaq_lib()?;
let filter = jaq.compile(prog.clone());
if !jaq.errs.is_empty() {
for (error, span) in jaq.errs {
error!(%error, "failed to compile filter program #{n}@{}:{}", span.start, span.end);
}
continue;
}
let inputs = RcIter::new(empty());
let mut results = filter.run((Ctx::new([], &inputs), val.clone()));
if let Some(res) = results.next() {
match res {
Ok(Val::Bool(false)) => {
trace!(
?n,
verdict = false,
"filter program finished; fail so stopping there"
);
sender
.send(false)
.unwrap_or_else(|_| warn!("failed to send filter result"));
continue 'chan;
}
Ok(Val::Bool(true)) => {
trace!(
?n,
verdict = true,
"filter program finished; pass so trying next"
);
continue;
}
Ok(val) => {
error!(?n, ?val, "filter program returned non-boolean, ignoring and trying next");
continue;
}
Err(err) => {
error!(?n, error=%err, "filter program failed, so trying next");
continue;
}
}
}
}
trace!("all filters failed, sending pass as default");
sender
.send(true)
.unwrap_or_else(|_| warn!("failed to send filter result"));
}
Ok(()) as miette::Result<()>
});
tokio::spawn(async {
match task.await {
Ok(Ok(())) => {}
Ok(Err(err)) => error!("filter progs task failed: {}", err),
Err(err) => error!("filter progs task panicked: {}", err),
}
});
Ok(Self { channel: requester })
}
}

View file

@ -0,0 +1,71 @@
/// Jaq's [Val](jaq_interpret::Val) uses Rc, but we want to use in Sync contexts. UGH!
use std::{rc::Rc, sync::Arc};
use indexmap::IndexMap;
use jaq_interpret::Val;
#[derive(Clone, Debug)]
pub enum SyncVal {
Null,
Bool(bool),
Int(isize),
Float(f64),
Num(Arc<str>),
Str(Arc<str>),
Arr(Arc<[SyncVal]>),
Obj(Arc<IndexMap<Arc<str>, SyncVal>>),
}
impl From<&Val> for SyncVal {
fn from(val: &Val) -> Self {
match val {
Val::Null => Self::Null,
Val::Bool(b) => Self::Bool(*b),
Val::Int(i) => Self::Int(*i),
Val::Float(f) => Self::Float(*f),
Val::Num(s) => Self::Num(s.to_string().into()),
Val::Str(s) => Self::Str(s.to_string().into()),
Val::Arr(a) => Self::Arr({
let mut arr = Vec::with_capacity(a.len());
for v in a.iter() {
arr.push(v.into());
}
arr.into()
}),
Val::Obj(m) => Self::Obj(Arc::new({
let mut map = IndexMap::new();
for (k, v) in m.iter() {
map.insert(k.to_string().into(), v.into());
}
map
})),
}
}
}
impl From<&SyncVal> for Val {
fn from(val: &SyncVal) -> Self {
match val {
SyncVal::Null => Self::Null,
SyncVal::Bool(b) => Self::Bool(*b),
SyncVal::Int(i) => Self::Int(*i),
SyncVal::Float(f) => Self::Float(*f),
SyncVal::Num(s) => Self::Num(s.to_string().into()),
SyncVal::Str(s) => Self::Str(s.to_string().into()),
SyncVal::Arr(a) => Self::Arr({
let mut arr = Vec::with_capacity(a.len());
for v in a.iter() {
arr.push(v.into());
}
arr.into()
}),
SyncVal::Obj(m) => Self::Obj(Rc::new({
let mut map: IndexMap<_, _, ahash::RandomState> = Default::default();
for (k, v) in m.iter() {
map.insert(k.to_string().into(), v.into());
}
map
})),
}
}
}

128
crates/cli/src/lib.rs Normal file
View file

@ -0,0 +1,128 @@
#![deny(rust_2018_idioms)]
#![allow(clippy::missing_const_for_fn, clippy::future_not_send)]
use std::{io::Write, process::Stdio};
use args::{Args, ShellCompletion};
use clap::CommandFactory;
use clap_complete::{Generator, Shell};
use clap_mangen::Man;
use is_terminal::IsTerminal;
use miette::{IntoDiagnostic, Result};
use tokio::{io::AsyncWriteExt, process::Command};
use tracing::{debug, info};
use watchexec::Watchexec;
use watchexec_events::{Event, Priority};
use crate::filterer::WatchexecFilterer;
pub mod args;
mod config;
mod dirs;
mod emits;
mod filterer;
mod state;
async fn run_watchexec(args: Args) -> Result<()> {
info!(version=%env!("CARGO_PKG_VERSION"), "constructing Watchexec from CLI");
let state = state::State::default();
let config = config::make_config(&args, &state)?;
config.filterer(WatchexecFilterer::new(&args).await?);
info!("initialising Watchexec runtime");
let wx = Watchexec::with_config(config)?;
if !args.postpone {
debug!("kicking off with empty event");
wx.send_event(Event::default(), Priority::Urgent).await?;
}
info!("running main loop");
wx.main().await.into_diagnostic()??;
if matches!(args.screen_clear, Some(args::ClearMode::Reset)) {
config::reset_screen();
}
info!("done with main loop");
Ok(())
}
async fn run_manpage(_args: Args) -> Result<()> {
info!(version=%env!("CARGO_PKG_VERSION"), "constructing manpage");
let man = Man::new(Args::command().long_version(None));
let mut buffer: Vec<u8> = Default::default();
man.render(&mut buffer).into_diagnostic()?;
if std::io::stdout().is_terminal() && which::which("man").is_ok() {
let mut child = Command::new("man")
.arg("-l")
.arg("-")
.stdin(Stdio::piped())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.kill_on_drop(true)
.spawn()
.into_diagnostic()?;
child
.stdin
.as_mut()
.unwrap()
.write_all(&buffer)
.await
.into_diagnostic()?;
if let Some(code) = child
.wait()
.await
.into_diagnostic()?
.code()
.and_then(|code| if code == 0 { None } else { Some(code) })
{
return Err(miette::miette!("Exited with status code {}", code));
}
} else {
std::io::stdout()
.lock()
.write_all(&buffer)
.into_diagnostic()?;
}
Ok(())
}
#[allow(clippy::unused_async)]
async fn run_completions(shell: ShellCompletion) -> Result<()> {
fn generate(generator: impl Generator) {
let mut cmd = Args::command();
clap_complete::generate(generator, &mut cmd, "watchexec", &mut std::io::stdout());
}
info!(version=%env!("CARGO_PKG_VERSION"), "constructing completions");
match shell {
ShellCompletion::Bash => generate(Shell::Bash),
ShellCompletion::Elvish => generate(Shell::Elvish),
ShellCompletion::Fish => generate(Shell::Fish),
ShellCompletion::Nu => generate(clap_complete_nushell::Nushell),
ShellCompletion::Powershell => generate(Shell::PowerShell),
ShellCompletion::Zsh => generate(Shell::Zsh),
}
Ok(())
}
pub async fn run() -> Result<()> {
let (args, _log_guard) = args::get_args().await?;
if args.manual {
run_manpage(args).await
} else if let Some(shell) = args.completions {
run_completions(shell).await
} else {
run_watchexec(args).await
}
}

22
crates/cli/src/main.rs Normal file
View file

@ -0,0 +1,22 @@
#[cfg(feature = "eyra")]
extern crate eyra;
use miette::IntoDiagnostic;
#[cfg(target_env = "musl")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
fn main() -> miette::Result<()> {
#[cfg(feature = "pid1")]
pid1::Pid1Settings::new()
.enable_log(cfg!(feature = "pid1-withlog"))
.launch()
.into_diagnostic()?;
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async { watchexec_cli::run().await })
}

48
crates/cli/src/state.rs Normal file
View file

@ -0,0 +1,48 @@
use std::{
env::var_os,
io::Write,
path::PathBuf,
sync::{Arc, Mutex},
};
use miette::{IntoDiagnostic, Result};
use tempfile::NamedTempFile;
#[derive(Clone, Debug, Default)]
pub struct State {
pub emit_file: RotatingTempFile,
}
#[derive(Clone, Debug, Default)]
pub struct RotatingTempFile(Arc<Mutex<Option<NamedTempFile>>>);
impl RotatingTempFile {
pub fn rotate(&self) -> Result<()> {
// implicitly drops the old file
*self.0.lock().unwrap() = Some(
if let Some(dir) = var_os("WATCHEXEC_TMPDIR") {
NamedTempFile::new_in(dir)
} else {
NamedTempFile::new()
}
.into_diagnostic()?,
);
Ok(())
}
pub fn write(&self, data: &[u8]) -> Result<()> {
if let Some(file) = self.0.lock().unwrap().as_mut() {
file.write_all(data).into_diagnostic()?;
}
Ok(())
}
pub fn path(&self) -> PathBuf {
if let Some(file) = self.0.lock().unwrap().as_ref() {
file.path().to_owned()
} else {
PathBuf::new()
}
}
}

View file

@ -0,0 +1,121 @@
use std::path::PathBuf;
use std::{fs, sync::OnceLock};
use miette::{Context, IntoDiagnostic, Result};
use rand::Rng;
static PLACEHOLDER_DATA: OnceLock<String> = OnceLock::new();
fn get_placeholder_data() -> &'static str {
PLACEHOLDER_DATA.get_or_init(|| "PLACEHOLDER\n".repeat(500))
}
/// The amount of nesting that will be used for generated files
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum GeneratedFileNesting {
/// Only one level of files
Flat,
/// Random, up to a certiain maximum
RandomToMax(usize),
}
/// Configuration for creating testing subfolders
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct TestSubfolderConfiguration {
/// The amount of nesting that will be used when folders are generated
pub(crate) nesting: GeneratedFileNesting,
/// Number of files the folder should contain
pub(crate) file_count: usize,
/// Subfolder name
pub(crate) name: String,
}
/// Options for generating test files
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct GenerateTestFilesArgs {
/// The path where the files should be generated
/// if None, the current working directory will be used.
pub(crate) path: Option<PathBuf>,
/// Configurations for subfolders to generate
pub(crate) subfolder_configs: Vec<TestSubfolderConfiguration>,
}
/// Generate test files
///
/// This returns the same number of paths that were requested via subfolder_configs.
pub(crate) fn generate_test_files(args: GenerateTestFilesArgs) -> Result<Vec<PathBuf>> {
// Use or create a temporary directory for the test files
let tmpdir = if let Some(p) = args.path {
p
} else {
tempfile::tempdir()
.into_diagnostic()
.wrap_err("failed to build tempdir")?
.into_path()
};
let mut paths = vec![tmpdir.clone()];
// Generate subfolders matching each config
for subfolder_config in args.subfolder_configs.iter() {
// Create the subfolder path
let subfolder_path = tmpdir.join(&subfolder_config.name);
fs::create_dir(&subfolder_path)
.into_diagnostic()
.wrap_err(format!(
"failed to create path for dir [{}]",
subfolder_path.display()
))?;
paths.push(subfolder_path.clone());
// Fill the subfolder with files
match subfolder_config.nesting {
GeneratedFileNesting::Flat => {
for idx in 0..subfolder_config.file_count {
// Write stub file contents
fs::write(
subfolder_path.join(format!("stub-file-{idx}")),
get_placeholder_data(),
)
.into_diagnostic()
.wrap_err(format!(
"failed to write temporary file in subfolder {} @ idx {idx}",
subfolder_path.display()
))?;
}
}
GeneratedFileNesting::RandomToMax(max_depth) => {
let mut generator = rand::thread_rng();
for idx in 0..subfolder_config.file_count {
// Build a randomized path up to max depth
let mut generated_path = subfolder_path.clone();
let depth = generator.gen_range(0..max_depth);
for _ in 0..depth {
generated_path.push("stub-dir");
}
// Create the path
fs::create_dir_all(&generated_path)
.into_diagnostic()
.wrap_err(format!(
"failed to create randomly generated path [{}]",
generated_path.display()
))?;
// Write stub file contents @ the new randomized path
fs::write(
generated_path.join(format!("stub-file-{idx}")),
get_placeholder_data(),
)
.into_diagnostic()
.wrap_err(format!(
"failed to write temporary file in subfolder {} @ idx {idx}",
subfolder_path.display()
))?;
}
}
}
}
Ok(paths)
}

146
crates/cli/tests/ignore.rs Normal file
View file

@ -0,0 +1,146 @@
use std::{
path::{Path, PathBuf},
process::Stdio,
time::Duration,
};
use miette::{IntoDiagnostic, Result, WrapErr};
use tokio::{process::Command, time::Instant};
use tracing_test::traced_test;
use uuid::Uuid;
mod common;
use common::{generate_test_files, GenerateTestFilesArgs};
use crate::common::{GeneratedFileNesting, TestSubfolderConfiguration};
/// Directory name that will be sued for the dir that *should* be watched
const WATCH_DIR_NAME: &str = "watch";
/// The token that watch will echo every time a match is found
const WATCH_TOKEN: &str = "updated";
/// Ensure that watchexec runtime does not increase with the
/// number of *ignored* files in a given folder
///
/// This test creates two separate folders, one small and the other large
///
/// Each folder has two subfolders:
/// - a shallow one to be watched, with a few files of single depth (20 files)
/// - a deep one to be ignored, with many files at varying depths (small case 200 files, large case 200,000 files)
///
/// watchexec, when executed on *either* folder should *not* experience a more
/// than 10x degradation in performance, because the vast majority of the files
/// are supposed to be ignored to begin with.
///
/// When running the CLI on the root folders, it should *not* take a long time to start de
#[tokio::test]
#[traced_test]
async fn e2e_ignore_many_files_200_000() -> Result<()> {
// Create a tempfile so that drop will clean it up
let small_test_dir = tempfile::tempdir()
.into_diagnostic()
.wrap_err("failed to create tempdir for test use")?;
// Determine the watchexec bin to use & build arguments
let wexec_bin = std::env::var("TEST_WATCHEXEC_BIN").unwrap_or(
option_env!("CARGO_BIN_EXE_watchexec")
.map(std::string::ToString::to_string)
.unwrap_or("watchexec".into()),
);
let token = format!("{WATCH_TOKEN}-{}", Uuid::new_v4());
let args: Vec<String> = vec![
"-1".into(), // exit as soon as watch completes
"--watch".into(),
WATCH_DIR_NAME.into(),
"echo".into(),
token.clone(),
];
// Generate a small directory of files containing dirs that *will* and will *not* be watched
let [ref root_dir_path, _, _] = generate_test_files(GenerateTestFilesArgs {
path: Some(PathBuf::from(small_test_dir.path())),
subfolder_configs: vec![
// Shallow folder will have a small number of files and won't be watched
TestSubfolderConfiguration {
name: "watch".into(),
nesting: GeneratedFileNesting::Flat,
file_count: 5,
},
// Deep folder will have *many* amll files and will be watched
TestSubfolderConfiguration {
name: "unrelated".into(),
nesting: GeneratedFileNesting::RandomToMax(42),
file_count: 200,
},
],
})?[..] else {
panic!("unexpected number of paths returned from generate_test_files");
};
// Get the number of elapsed
let small_elapsed = run_watchexec_cmd(&wexec_bin, root_dir_path, args.clone()).await?;
// Create a tempfile so that drop will clean it up
let large_test_dir = tempfile::tempdir()
.into_diagnostic()
.wrap_err("failed to create tempdir for test use")?;
// Generate a *large* directory of files
let [ref root_dir_path, _, _] = generate_test_files(GenerateTestFilesArgs {
path: Some(PathBuf::from(large_test_dir.path())),
subfolder_configs: vec![
// Shallow folder will have a small number of files and won't be watched
TestSubfolderConfiguration {
name: "watch".into(),
nesting: GeneratedFileNesting::Flat,
file_count: 5,
},
// Deep folder will have *many* amll files and will be watched
TestSubfolderConfiguration {
name: "unrelated".into(),
nesting: GeneratedFileNesting::RandomToMax(42),
file_count: 200_000,
},
],
})?[..] else {
panic!("unexpected number of paths returned from generate_test_files");
};
// Get the number of elapsed
let large_elapsed = run_watchexec_cmd(&wexec_bin, root_dir_path, args.clone()).await?;
// We expect the ignores to not impact watchexec startup time at all
// whether there are 200 files in there or 200k
assert!(
large_elapsed < small_elapsed * 10,
"200k ignore folder ({:?}) took more than 10x more time ({:?}) than 200 ignore folder ({:?})",
large_elapsed,
small_elapsed * 10,
small_elapsed,
);
Ok(())
}
/// Run a watchexec command once
async fn run_watchexec_cmd(
wexec_bin: impl AsRef<str>,
dir: impl AsRef<Path>,
args: impl Into<Vec<String>>,
) -> Result<Duration> {
// Build the subprocess command
let mut cmd = Command::new(wexec_bin.as_ref());
cmd.args(args.into());
cmd.current_dir(dir);
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());
let start = Instant::now();
cmd.kill_on_drop(true)
.output()
.await
.into_diagnostic()
.wrap_err("fixed")?;
Ok(start.elapsed())
}

View file

@ -0,0 +1,2 @@
#define RT_MANIFEST 24
1 RT_MANIFEST "watchexec.exe.manifest"

Some files were not shown because too many files have changed in this diff Show more