Compare commits

...

19 Commits

Author SHA1 Message Date
Егор Куклин
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
35 changed files with 493 additions and 91 deletions

View File

@ -104,10 +104,10 @@ jobs:
experimental: false experimental: false
- name: mac-arm64 - name: mac-arm64
os: macos-11.0 os: macos-latest
target: aarch64-apple-darwin target: aarch64-apple-darwin
cross: true cross: true
experimental: true experimental: false
- name: windows-x86-64 - name: windows-x86-64
os: windows-latest os: windows-latest
@ -197,9 +197,15 @@ jobs:
tool: cross tool: cross
- name: Build - name: Build
run: ${{ matrix.cross && 'cross' || 'cargo' }} build --package watchexec-cli --release --locked --target ${{ matrix.target }} shell: bash
run: |
${{ matrix.cross && 'cross' || 'cargo' }} build \
-p watchexec-cli \
--release --locked \
--target ${{ matrix.target }}
- name: Make manpage - name: Make manpage
shell: bash
run: | run: |
cargo run -p watchexec-cli \ cargo run -p watchexec-cli \
${{ (!matrix.cross) && '--release --target' || '' }} \ ${{ (!matrix.cross) && '--release --target' || '' }} \
@ -207,6 +213,7 @@ jobs:
--locked -- --manual > doc/watchexec.1 --locked -- --manual > doc/watchexec.1
- name: Make completions - name: Make completions
shell: bash
run: | run: |
bin/completions \ bin/completions \
${{ (!matrix.cross) && '--release --target' || '' }} \ ${{ (!matrix.cross) && '--release --target' || '' }} \

View File

@ -3,8 +3,8 @@ message: |
If you use this software, please cite it using these metadata. 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" title: "Watchexec: a tool to react to filesystem changes, and a crate ecosystem to power it"
version: "2.0.0" version: "2.1.2"
date-released: 2024-04-20 date-released: 2024-06-30
repository-code: https://github.com/watchexec/watchexec repository-code: https://github.com/watchexec/watchexec
license: Apache-2.0 license: Apache-2.0

51
Cargo.lock generated
View File

@ -488,7 +488,7 @@ dependencies = [
[[package]] [[package]]
name = "bosion" name = "bosion"
version = "1.0.3" version = "1.1.0"
dependencies = [ dependencies = [
"gix", "gix",
"time", "time",
@ -1317,6 +1317,7 @@ dependencies = [
"gix-glob", "gix-glob",
"gix-hash", "gix-hash",
"gix-hashtable", "gix-hashtable",
"gix-index",
"gix-lock", "gix-lock",
"gix-macros", "gix-macros",
"gix-object", "gix-object",
@ -1354,6 +1355,15 @@ dependencies = [
"winnow 0.6.6", "winnow 0.6.6",
] ]
[[package]]
name = "gix-bitmap"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a371db66cbd4e13f0ed9dc4c0fea712d7276805fccc877f77e96374d317e87ae"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "gix-chunk" name = "gix-chunk"
version = "0.4.8" version = "0.4.8"
@ -1513,6 +1523,33 @@ dependencies = [
"parking_lot", "parking_lot",
] ]
[[package]]
name = "gix-index"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "881ab3b1fa57f497601a5add8289e72a7ae09471fc0b9bbe483b628ae8e418a1"
dependencies = [
"bitflags 2.5.0",
"bstr",
"filetime",
"fnv",
"gix-bitmap",
"gix-features",
"gix-fs",
"gix-hash",
"gix-lock",
"gix-object",
"gix-traverse",
"gix-utils",
"hashbrown 0.14.3",
"itoa",
"libc",
"memmap2",
"rustix",
"smallvec",
"thiserror",
]
[[package]] [[package]]
name = "gix-lock" name = "gix-lock"
version = "13.1.1" version = "13.1.1"
@ -1988,7 +2025,7 @@ dependencies = [
[[package]] [[package]]
name = "ignore-files" name = "ignore-files"
version = "3.0.0" version = "3.0.1"
dependencies = [ dependencies = [
"dunce", "dunce",
"futures", "futures",
@ -4007,7 +4044,7 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]] [[package]]
name = "watchexec" name = "watchexec"
version = "4.0.0" version = "4.1.0"
dependencies = [ dependencies = [
"async-priority-channel", "async-priority-channel",
"async-recursion", "async-recursion",
@ -4032,7 +4069,7 @@ dependencies = [
[[package]] [[package]]
name = "watchexec-cli" name = "watchexec-cli"
version = "2.0.0" version = "2.1.2"
dependencies = [ dependencies = [
"ahash", "ahash",
"argfile", "argfile",
@ -4048,6 +4085,7 @@ dependencies = [
"console-subscriber", "console-subscriber",
"dashmap", "dashmap",
"dirs 5.0.1", "dirs 5.0.1",
"dunce",
"embed-resource", "embed-resource",
"eyra", "eyra",
"futures", "futures",
@ -4097,10 +4135,11 @@ dependencies = [
[[package]] [[package]]
name = "watchexec-filterer-globset" name = "watchexec-filterer-globset"
version = "4.0.0" version = "4.0.1"
dependencies = [ dependencies = [
"ignore", "ignore",
"ignore-files", "ignore-files",
"tempfile",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -4111,7 +4150,7 @@ dependencies = [
[[package]] [[package]]
name = "watchexec-filterer-ignore" name = "watchexec-filterer-ignore"
version = "4.0.0" version = "4.0.1"
dependencies = [ dependencies = [
"dunce", "dunce",
"ignore", "ignore",

View File

@ -19,7 +19,7 @@ _watchexec() {
case "${cmd}" in case "${cmd}" in
watchexec) watchexec)
opts="-w -W -c -o -r -s -d -p -n -E -1 -N -q -e -f -j -i -v -h -V --watch --watch-non-recursive --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]..." 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 if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
@ -41,6 +41,14 @@ _watchexec() {
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
--watch-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-F)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--clear) --clear)
COMPREPLY=($(compgen -W "clear reset" -- "${cur}")) COMPREPLY=($(compgen -W "clear reset" -- "${cur}"))
return 0 return 0

View File

@ -22,6 +22,8 @@ set edit:completion:arg-completer[watchexec] = {|@words|
cand --watch 'Watch a specific file or directory' cand --watch 'Watch a specific file or directory'
cand -W 'Watch a specific directory, non-recursively' cand -W 'Watch a specific directory, non-recursively'
cand --watch-non-recursive '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 -c 'Clear screen before running command'
cand --clear '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 -o 'What to do when receiving events while the command is running'

View File

@ -1,5 +1,6 @@
complete -c watchexec -s w -l watch -d 'Watch a specific file or directory' -r -F 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 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 '',reset ''}" complete -c watchexec -s c -l clear -d 'Clear screen before running command' -r -f -a "{clear '',reset ''}"
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 '',do-nothing '',restart '',signal ''}" 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 '',do-nothing '',restart '',signal ''}"
complete -c watchexec -s s -l signal -d 'Send a signal to the process when it\'s still running' -r complete -c watchexec -s s -l signal -d 'Send a signal to the process when it\'s still running' -r

View File

@ -33,6 +33,7 @@ module completions {
...command: string # Command to run on changes ...command: string # Command to run on changes
--watch(-w): string # Watch a specific file or directory --watch(-w): string # Watch a specific file or directory
--watch-non-recursive(-W): string # Watch a specific directory, non-recursively --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 --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 --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 --restart(-r) # Restart the process if it's still running

View File

@ -25,6 +25,8 @@ Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock {
[CompletionResult]::new('--watch', 'watch', [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('-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('--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('-c', 'c', [CompletionResultType]::ParameterName, 'Clear screen before running command')
[CompletionResult]::new('--clear', 'clear', [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('-o', 'o', [CompletionResultType]::ParameterName, 'What to do when receiving events while the command is running')

View File

@ -19,6 +19,8 @@ _watchexec() {
'*--watch=[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' \ '*-W+[Watch a specific directory, non-recursively]:PATH:_files' \
'*--watch-non-recursive=[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]' \ '-c+[Clear screen before running command]' \
'--clear=[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)' \ '-o+[What to do when receiving events while the command is running]:MODE:(queue do-nothing restart signal)' \

View File

@ -2,6 +2,10 @@
## Next (YYYY-MM-DD) ## Next (YYYY-MM-DD)
## v1.1.0 (2024-05-16)
- Add `git-describe` support (#832, by @lu-zero)
## v1.0.3 (2024-04-20) ## v1.0.3 (2024-04-20)
- Deps: gix 0.62 - Deps: gix 0.62

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bosion" name = "bosion"
version = "1.0.3" version = "1.1.0"
authors = ["Félix Saparelli <felix@passcod.name>"] authors = ["Félix Saparelli <felix@passcod.name>"]
license = "Apache-2.0 OR MIT" license = "Apache-2.0 OR MIT"
@ -22,6 +22,7 @@ features = ["macros", "formatting"]
version = "0.62.0" version = "0.62.0"
optional = true optional = true
default-features = false default-features = false
features = ["revision"]
[features] [features]
default = ["git", "reproducible", "std"] default = ["git", "reproducible", "std"]

View File

@ -15,7 +15,7 @@ In your `Cargo.toml`:
```toml ```toml
[build-dependencies] [build-dependencies]
bosion = "1.0.3" bosion = "1.1.0"
``` ```
In your `build.rs`: In your `build.rs`:

View File

@ -8,6 +8,24 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.13" version = "0.6.13"
@ -82,7 +100,7 @@ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]] [[package]]
name = "bosion" name = "bosion"
version = "1.0.2" version = "1.0.3"
dependencies = [ dependencies = [
"gix", "gix",
"time", "time",
@ -211,6 +229,18 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"windows-sys",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.28" version = "1.0.28"
@ -221,6 +251,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.2.1"
@ -247,6 +283,7 @@ dependencies = [
"gix-glob", "gix-glob",
"gix-hash", "gix-hash",
"gix-hashtable", "gix-hashtable",
"gix-index",
"gix-lock", "gix-lock",
"gix-macros", "gix-macros",
"gix-object", "gix-object",
@ -284,6 +321,15 @@ dependencies = [
"winnow", "winnow",
] ]
[[package]]
name = "gix-bitmap"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a371db66cbd4e13f0ed9dc4c0fea712d7276805fccc877f77e96374d317e87ae"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "gix-chunk" name = "gix-chunk"
version = "0.4.8" version = "0.4.8"
@ -443,6 +489,33 @@ dependencies = [
"parking_lot", "parking_lot",
] ]
[[package]]
name = "gix-index"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "881ab3b1fa57f497601a5add8289e72a7ae09471fc0b9bbe483b628ae8e418a1"
dependencies = [
"bitflags 2.5.0",
"bstr",
"filetime",
"fnv",
"gix-bitmap",
"gix-features",
"gix-fs",
"gix-hash",
"gix-lock",
"gix-object",
"gix-traverse",
"gix-utils",
"hashbrown",
"itoa",
"libc",
"memmap2",
"rustix",
"smallvec",
"thiserror",
]
[[package]] [[package]]
name = "gix-lock" name = "gix-lock"
version = "13.1.1" version = "13.1.1"
@ -702,6 +775,10 @@ name = "hashbrown"
version = "0.14.3" version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]] [[package]]
name = "heck" name = "heck"
@ -1076,6 +1153,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.5.0" version = "2.5.0"
@ -1255,3 +1338,23 @@ checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "zerocopy"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@ -13,6 +13,9 @@ struct Args {
#[clap(long)] #[clap(long)]
dates: bool, dates: bool,
#[clap(long)]
describe: bool,
} }
fn main() { fn main() {
@ -23,17 +26,15 @@ fn main() {
"{}", "{}",
Bosion::long_version_with(&[("extra", "field"), ("custom", "1.2.3"),]) Bosion::long_version_with(&[("extra", "field"), ("custom", "1.2.3"),])
); );
} else } else if args.features {
if args.features {
println!("Features: {}", Bosion::CRATE_FEATURE_STRING); println!("Features: {}", Bosion::CRATE_FEATURE_STRING);
} else } else if args.dates {
if args.dates {
println!("commit date: {}", Bosion::GIT_COMMIT_DATE); println!("commit date: {}", Bosion::GIT_COMMIT_DATE);
println!("commit datetime: {}", Bosion::GIT_COMMIT_DATETIME); println!("commit datetime: {}", Bosion::GIT_COMMIT_DATETIME);
println!("build date: {}", Bosion::BUILD_DATE); println!("build date: {}", Bosion::BUILD_DATE);
println!("build datetime: {}", Bosion::BUILD_DATETIME); println!("build datetime: {}", Bosion::BUILD_DATETIME);
} else if args.describe {
println!("commit description: {}", Bosion::GIT_COMMIT_DESCRIPTION);
} else { } else {
println!("{}", Bosion::LONG_VERSION); println!("{}", Bosion::LONG_VERSION);
} }

View File

@ -145,6 +145,9 @@ pub struct GitInfo {
/// The datetime of the current commit, in the format `YYYY-MM-DD HH:MM:SS`, at UTC. /// The datetime of the current commit, in the format `YYYY-MM-DD HH:MM:SS`, at UTC.
pub git_datetime: String, pub git_datetime: String,
/// The `git describe` equivalent output
pub git_description: String,
} }
#[cfg(feature = "git")] #[cfg(feature = "git")]
@ -163,6 +166,7 @@ impl GitInfo {
git_shorthash: head.short_id().err_string()?.to_string(), git_shorthash: head.short_id().err_string()?.to_string(),
git_date: timestamp.format(DATE_FORMAT).err_string()?, git_date: timestamp.format(DATE_FORMAT).err_string()?,
git_datetime: timestamp.format(DATETIME_FORMAT).err_string()?, git_datetime: timestamp.format(DATETIME_FORMAT).err_string()?,
git_description: head.describe().format().err_string()?.to_string(),
}) })
} }
} }

View File

@ -74,6 +74,7 @@ pub fn gather_to(filename: &str, structname: &str, public: bool) {
git_shorthash, git_shorthash,
git_date, git_date,
git_datetime, git_datetime,
git_description,
.. ..
}) = git }) = git
{ {
@ -104,6 +105,11 @@ pub fn gather_to(filename: &str, structname: &str, public: bool) {
/// This is the date and time (`YYYY-MM-DD HH:MM:SS`) of the commit that was built. Same /// 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. /// caveats as with `GIT_COMMIT_HASH` apply.
pub const GIT_COMMIT_DATETIME: &'static str = {git_datetime:?}; 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}")) ), 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 { } else {
@ -244,6 +250,7 @@ pub fn gather_to_env_with_prefix(prefix: &str) {
git_shorthash, git_shorthash,
git_date, git_date,
git_datetime, git_datetime,
git_description,
.. ..
}) = git }) = git
{ {
@ -251,5 +258,6 @@ pub fn gather_to_env_with_prefix(prefix: &str) {
println!("cargo:rustc-env={prefix}GIT_COMMIT_SHORTHASH={git_shorthash}"); 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_DATE={git_date}");
println!("cargo:rustc-env={prefix}GIT_COMMIT_DATETIME={git_datetime}"); println!("cargo:rustc-env={prefix}GIT_COMMIT_DATETIME={git_datetime}");
println!("cargo:rustc-env={prefix}GIT_COMMIT_DESCRIPTION={git_description}");
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "watchexec-cli" name = "watchexec-cli"
version = "2.0.0" version = "2.1.2"
authors = ["Félix Saparelli <felix@passcod.name>", "Matt Green <mattgreenrocks@gmail.com>"] authors = ["Félix Saparelli <felix@passcod.name>", "Matt Green <mattgreenrocks@gmail.com>"]
license = "Apache-2.0" license = "Apache-2.0"
@ -29,6 +29,7 @@ clap_mangen = "0.2.15"
clearscreen = "3.0.0" clearscreen = "3.0.0"
dashmap = "5.4.0" dashmap = "5.4.0"
dirs = "5.0.0" dirs = "5.0.0"
dunce = "1.0.4"
futures = "0.3.29" futures = "0.3.29"
humantime = "2.1.0" humantime = "2.1.0"
indexmap = "2.2.6" # needs to be in sync with jaq's indexmap = "2.2.6" # needs to be in sync with jaq's
@ -69,7 +70,7 @@ features = ["log", "env_logger"]
optional = true optional = true
[dependencies.ignore-files] [dependencies.ignore-files]
version = "3.0.0" version = "3.0.1"
path = "../ignore-files" path = "../ignore-files"
[dependencies.miette] [dependencies.miette]
@ -85,7 +86,7 @@ version = "1.4.0"
path = "../project-origins" path = "../project-origins"
[dependencies.watchexec] [dependencies.watchexec]
version = "4.0.0" version = "4.1.0"
path = "../lib" path = "../lib"
[dependencies.watchexec-events] [dependencies.watchexec-events]
@ -98,7 +99,7 @@ version = "3.0.0"
path = "../signals" path = "../signals"
[dependencies.watchexec-filterer-globset] [dependencies.watchexec-filterer-globset]
version = "4.0.0" version = "4.0.1"
path = "../filterer/globset" path = "../filterer/globset"
[dependencies.tokio] [dependencies.tokio]
@ -130,7 +131,7 @@ mimalloc = "0.1.39"
embed-resource = "2.4.0" embed-resource = "2.4.0"
[build-dependencies.bosion] [build-dependencies.bosion]
version = "1.0.3" version = "1.1.0"
path = "../bosion" path = "../bosion"
[dev-dependencies] [dev-dependencies]
@ -171,7 +172,7 @@ assets = [
["../../target/release/watchexec", "usr/bin/watchexec", "755"], ["../../target/release/watchexec", "usr/bin/watchexec", "755"],
["README.md", "usr/share/doc/watchexec/README", "644"], ["README.md", "usr/share/doc/watchexec/README", "644"],
["../../doc/watchexec.1.md", "usr/share/doc/watchexec/watchexec.1.md", "644"], ["../../doc/watchexec.1.md", "usr/share/doc/watchexec/watchexec.1.md", "644"],
["../../doc/watchexec.1", "usr/share/man/man1/watchexec.1.html", "644"], ["../../doc/watchexec.1", "usr/share/man/man1/watchexec.1", "644"],
["../../completions/bash", "usr/share/bash-completion/completions/watchexec", "644"], ["../../completions/bash", "usr/share/bash-completion/completions/watchexec", "644"],
["../../completions/fish", "usr/share/fish/vendor_completions.d/watchexec.fish", "644"], ["../../completions/fish", "usr/share/fish/vendor_completions.d/watchexec.fish", "644"],
["../../completions/zsh", "usr/share/zsh/site-functions/_watchexec", "644"], ["../../completions/zsh", "usr/share/zsh/site-functions/_watchexec", "644"],
@ -183,7 +184,7 @@ assets = [
{ source = "../../target/release/watchexec", dest = "/usr/bin/watchexec", mode = "755" }, { source = "../../target/release/watchexec", dest = "/usr/bin/watchexec", mode = "755" },
{ source = "README.md", dest = "/usr/share/doc/watchexec/README", mode = "644", doc = true }, { 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.md", dest = "/usr/share/doc/watchexec/watchexec.1.md", mode = "644", doc = true },
{ source = "../../doc/watchexec.1", dest = "/usr/share/man/man1/watchexec.1.html", mode = "644" }, { 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/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/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 = "../../completions/zsh", dest = "/usr/share/zsh/site-functions/_watchexec", mode = "644" },

View File

@ -1,19 +1,23 @@
use std::{ use std::{
collections::BTreeSet, collections::BTreeSet,
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
fs::canonicalize,
mem::take, mem::take,
path::{Path, PathBuf}, path::{Path, PathBuf},
str::FromStr, str::FromStr,
time::Duration, time::Duration,
}; };
use dunce::canonicalize;
use clap::{ use clap::{
builder::TypedValueParser, error::ErrorKind, Arg, Command, CommandFactory, Parser, ValueEnum, builder::TypedValueParser, error::ErrorKind, Arg, Command, CommandFactory, Parser, ValueEnum,
ValueHint, ValueHint,
}; };
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use tokio::{fs::File, io::AsyncReadExt}; use tokio::{
fs::File,
io::{AsyncBufReadExt, AsyncReadExt, BufReader},
};
use tracing::{debug, info, trace, warn}; use tracing::{debug, info, trace, warn};
use tracing_appender::non_blocking::WorkerGuard; use tracing_appender::non_blocking::WorkerGuard;
use watchexec::{paths::PATH_SEPARATOR, sources::fs::WatchedPath}; use watchexec::{paths::PATH_SEPARATOR, sources::fs::WatchedPath};
@ -151,6 +155,23 @@ pub struct Args {
)] )]
pub non_recursive_paths: Vec<PathBuf>, pub non_recursive_paths: Vec<PathBuf>,
/// Watch files and directories from a file
///
/// Each line in the file will be interpreted as if given to '-w'.
///
/// For more complex uses (like watching non-recursively), use the argfile capability: build a
/// file containing command-line options and pass it to watchexec with `@path/to/argfile`.
///
/// The special value '-' will read from STDIN; this in incompatible with '--stdin-quit'.
#[arg(
short = 'F',
long,
help_heading = OPTSET_FILTERING,
value_hint = ValueHint::AnyPath,
value_name = "PATH",
)]
pub watch_file: Option<PathBuf>,
#[doc(hidden)] #[doc(hidden)]
#[arg(skip)] #[arg(skip)]
pub paths: Vec<WatchedPath>, pub paths: Vec<WatchedPath>,
@ -1216,6 +1237,15 @@ pub async fn get_args() -> Result<(Args, Option<WorkerGuard>)> {
.exit(); .exit();
} }
if args.stdin_quit && args.watch_file == Some(PathBuf::from("-")) {
Args::command()
.error(
ErrorKind::InvalidValue,
"stdin-quit cannot be used when --watch-file=-",
)
.exit();
}
let workdir = if let Some(w) = take(&mut args.workdir) { let workdir = if let Some(w) = take(&mut args.workdir) {
w w
} else { } else {
@ -1233,6 +1263,22 @@ pub async fn get_args() -> Result<(Args, Option<WorkerGuard>)> {
info!(path=?project_origin, "effective project origin"); info!(path=?project_origin, "effective project origin");
args.project_origin = Some(project_origin.clone()); args.project_origin = Some(project_origin.clone());
if let Some(watch_file) = args.watch_file.as_ref() {
if watch_file == Path::new("-") {
let file = tokio::io::stdin();
let mut lines = BufReader::new(file).lines();
while let Ok(Some(line)) = lines.next_line().await {
args.recursive_paths.push(line.into());
}
} else {
let file = File::open(watch_file).await.into_diagnostic()?;
let mut lines = BufReader::new(file).lines();
while let Ok(Some(line)) = lines.next_line().await {
args.recursive_paths.push(line.into());
}
};
}
args.paths = take(&mut args.recursive_paths) args.paths = take(&mut args.recursive_paths)
.into_iter() .into_iter()
.map(|path| { .map(|path| {
@ -1243,7 +1289,7 @@ pub async fn get_args() -> Result<(Args, Option<WorkerGuard>)> {
canonicalize(project_origin.join(path)).into_diagnostic() canonicalize(project_origin.join(path)).into_diagnostic()
} }
} }
.map(WatchedPath::non_recursive) .map(WatchedPath::recursive)
}) })
.chain(take(&mut args.non_recursive_paths).into_iter().map(|path| { .chain(take(&mut args.non_recursive_paths).into_iter().map(|path| {
{ {

View File

@ -105,6 +105,12 @@ impl WatchexecFilterer {
]); ]);
} }
let whitelist = args
.paths
.iter()
.map(|p| p.into())
.filter(|p: &PathBuf| p.is_file());
let mut filters = args let mut filters = args
.filter_patterns .filter_patterns
.iter() .iter()
@ -128,9 +134,16 @@ impl WatchexecFilterer {
info!("initialising Globset filterer"); info!("initialising Globset filterer");
Ok(Arc::new(Self { Ok(Arc::new(Self {
inner: GlobsetFilterer::new(project_origin, filters, ignores, ignore_files, exts) inner: GlobsetFilterer::new(
.await project_origin,
.into_diagnostic()?, filters,
ignores,
whitelist,
ignore_files,
exts,
)
.await
.into_diagnostic()?,
fs_events: args.filter_fs_events.clone(), fs_events: args.filter_fs_events.clone(),
progs: if args.filter_programs_parsed.is_empty() { progs: if args.filter_programs_parsed.is_empty() {
None None

View File

@ -3,7 +3,7 @@
<assemblyIdentity <assemblyIdentity
type="win32" type="win32"
name="Watchexec.Cli.watchexec" name="Watchexec.Cli.watchexec"
version="2.0.0.0" version="2.1.2.0"
/> />
<trustInfo> <trustInfo>

View File

@ -2,6 +2,8 @@
## Next (YYYY-MM-DD) ## Next (YYYY-MM-DD)
## v4.0.1 (2024-04-28)
- Hide fmt::Debug spew from ignore crate, use `full_debug` feature to restore. - Hide fmt::Debug spew from ignore crate, use `full_debug` feature to restore.
## v4.0.0 (2024-04-20) ## v4.0.0 (2024-04-20)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "watchexec-filterer-globset" name = "watchexec-filterer-globset"
version = "4.0.0" version = "4.0.1"
authors = ["Matt Green <mattgreenrocks@gmail.com>", "Félix Saparelli <felix@passcod.name>"] authors = ["Matt Green <mattgreenrocks@gmail.com>", "Félix Saparelli <felix@passcod.name>"]
license = "Apache-2.0" license = "Apache-2.0"
@ -20,11 +20,11 @@ ignore = "0.4.18"
tracing = "0.1.40" tracing = "0.1.40"
[dependencies.ignore-files] [dependencies.ignore-files]
version = "3.0.0" version = "3.0.1"
path = "../../ignore-files" path = "../../ignore-files"
[dependencies.watchexec] [dependencies.watchexec]
version = "4.0.0" version = "4.1.0"
path = "../../lib" path = "../../lib"
[dependencies.watchexec-events] [dependencies.watchexec-events]
@ -32,11 +32,12 @@ version = "3.0.0"
path = "../../events" path = "../../events"
[dependencies.watchexec-filterer-ignore] [dependencies.watchexec-filterer-ignore]
version = "4.0.0" version = "4.0.1"
path = "../ignore" path = "../ignore"
[dev-dependencies] [dev-dependencies]
tracing-subscriber = "0.3.6" tracing-subscriber = "0.3.6"
tempfile = "3"
[dev-dependencies.tokio] [dev-dependencies.tokio]
version = "1.33.0" version = "1.33.0"

View File

@ -28,6 +28,7 @@ pub struct GlobsetFilterer {
origin: PathBuf, origin: PathBuf,
filters: Gitignore, filters: Gitignore,
ignores: Gitignore, ignores: Gitignore,
whitelist: Vec<PathBuf>,
ignore_files: IgnoreFilterer, ignore_files: IgnoreFilterer,
extensions: Vec<OsString>, extensions: Vec<OsString>,
} }
@ -51,6 +52,8 @@ impl GlobsetFilterer {
/// The first list is used to filter paths (only matching paths will pass the filter), the /// The first list is used to filter paths (only matching paths will pass the filter), the
/// second is used to ignore paths (matching paths will fail the pattern). If the filter list is /// second is used to ignore paths (matching paths will fail the pattern). If the filter list is
/// empty, only the ignore list will be used. If both lists are empty, the filter always passes. /// empty, only the ignore list will be used. If both lists are empty, the filter always passes.
/// Whitelist is used to automatically accept files even if they would be filtered out
/// otherwise. It is passed as an absolute path to the file that should not be filtered.
/// ///
/// Ignores and filters are passed as a tuple of the glob pattern as a string and an optional /// Ignores and filters are passed as a tuple of the glob pattern as a string and an optional
/// path of the folder the pattern should apply in (e.g. the folder a gitignore file is in). /// path of the folder the pattern should apply in (e.g. the folder a gitignore file is in).
@ -64,6 +67,7 @@ impl GlobsetFilterer {
origin: impl AsRef<Path>, origin: impl AsRef<Path>,
filters: impl IntoIterator<Item = (String, Option<PathBuf>)>, filters: impl IntoIterator<Item = (String, Option<PathBuf>)>,
ignores: impl IntoIterator<Item = (String, Option<PathBuf>)>, ignores: impl IntoIterator<Item = (String, Option<PathBuf>)>,
whitelist: impl IntoIterator<Item = PathBuf>,
ignore_files: impl IntoIterator<Item = IgnoreFile>, ignore_files: impl IntoIterator<Item = IgnoreFile>,
extensions: impl IntoIterator<Item = OsString>, extensions: impl IntoIterator<Item = OsString>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
@ -99,6 +103,8 @@ impl GlobsetFilterer {
ignore_files.finish(); ignore_files.finish();
let ignore_files = IgnoreFilterer(ignore_files); let ignore_files = IgnoreFilterer(ignore_files);
let whitelist = whitelist.into_iter().collect::<Vec<_>>();
debug!( debug!(
?origin, ?origin,
num_filters=%filters.num_ignores(), num_filters=%filters.num_ignores(),
@ -113,6 +119,7 @@ impl GlobsetFilterer {
origin: origin.into(), origin: origin.into(),
filters, filters,
ignores, ignores,
whitelist,
ignore_files, ignore_files,
extensions, extensions,
}) })
@ -126,6 +133,19 @@ impl Filterer for GlobsetFilterer {
fn check_event(&self, event: &Event, priority: Priority) -> Result<bool, RuntimeError> { fn check_event(&self, event: &Event, priority: Priority) -> Result<bool, RuntimeError> {
let _span = trace_span!("filterer_check").entered(); let _span = trace_span!("filterer_check").entered();
{
trace!("checking internal whitelist");
// Ideally check path equality backwards for better perf
// There could be long matching prefixes so we will exit late
if event
.paths()
.any(|(p, _)| self.whitelist.iter().any(|w| w == p))
{
trace!("internal whitelist filterer matched (success)");
return Ok(true);
}
}
{ {
trace!("checking internal ignore filterer"); trace!("checking internal ignore filterer");
if !self if !self

View File

@ -1,9 +1,10 @@
mod helpers; mod helpers;
use helpers::globset::*; use helpers::globset::*;
use std::io::Write;
#[tokio::test] #[tokio::test]
async fn empty_filter_passes_everything() { async fn empty_filter_passes_everything() {
let filterer = filt(&[], &[], &[]).await; let filterer = filt(&[], &[], &[], &[], &[]).await;
filterer.file_does_pass("Cargo.toml"); filterer.file_does_pass("Cargo.toml");
filterer.file_does_pass("Cargo.json"); filterer.file_does_pass("Cargo.json");
@ -23,7 +24,7 @@ async fn empty_filter_passes_everything() {
#[tokio::test] #[tokio::test]
async fn exact_filename() { async fn exact_filename() {
let filterer = filt(&["Cargo.toml"], &[], &[]).await; let filterer = filt(&["Cargo.toml"], &[], &[], &[], &[]).await;
filterer.file_does_pass("Cargo.toml"); filterer.file_does_pass("Cargo.toml");
filterer.file_does_pass("/test/foo/bar/Cargo.toml"); filterer.file_does_pass("/test/foo/bar/Cargo.toml");
@ -36,7 +37,7 @@ async fn exact_filename() {
#[tokio::test] #[tokio::test]
async fn exact_filename_in_folder() { async fn exact_filename_in_folder() {
let filterer = filt(&["sub/Cargo.toml"], &[], &[]).await; let filterer = filt(&["sub/Cargo.toml"], &[], &[], &[], &[]).await;
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
filterer.file_does_pass("sub/Cargo.toml"); filterer.file_does_pass("sub/Cargo.toml");
@ -50,7 +51,7 @@ async fn exact_filename_in_folder() {
#[tokio::test] #[tokio::test]
async fn exact_filename_in_hidden_folder() { async fn exact_filename_in_hidden_folder() {
let filterer = filt(&[".sub/Cargo.toml"], &[], &[]).await; let filterer = filt(&[".sub/Cargo.toml"], &[], &[], &[], &[]).await;
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
filterer.file_does_pass(".sub/Cargo.toml"); filterer.file_does_pass(".sub/Cargo.toml");
@ -64,7 +65,7 @@ async fn exact_filename_in_hidden_folder() {
#[tokio::test] #[tokio::test]
async fn exact_filenames_multiple() { async fn exact_filenames_multiple() {
let filterer = filt(&["Cargo.toml", "package.json"], &[], &[]).await; let filterer = filt(&["Cargo.toml", "package.json"], &[], &[], &[], &[]).await;
filterer.file_does_pass("Cargo.toml"); filterer.file_does_pass("Cargo.toml");
filterer.file_does_pass("/test/foo/bar/Cargo.toml"); filterer.file_does_pass("/test/foo/bar/Cargo.toml");
@ -81,7 +82,7 @@ async fn exact_filenames_multiple() {
#[tokio::test] #[tokio::test]
async fn glob_single_final_ext_star() { async fn glob_single_final_ext_star() {
let filterer = filt(&["Cargo.*"], &[], &[]).await; let filterer = filt(&["Cargo.*"], &[], &[], &[], &[]).await;
filterer.file_does_pass("Cargo.toml"); filterer.file_does_pass("Cargo.toml");
filterer.file_does_pass("Cargo.json"); filterer.file_does_pass("Cargo.json");
@ -93,7 +94,7 @@ async fn glob_single_final_ext_star() {
#[tokio::test] #[tokio::test]
async fn glob_star_trailing_slash() { async fn glob_star_trailing_slash() {
let filterer = filt(&["Cargo.*/"], &[], &[]).await; let filterer = filt(&["Cargo.*/"], &[], &[], &[], &[]).await;
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
filterer.file_doesnt_pass("Cargo.json"); filterer.file_doesnt_pass("Cargo.json");
@ -106,7 +107,7 @@ async fn glob_star_trailing_slash() {
#[tokio::test] #[tokio::test]
async fn glob_star_leading_slash() { async fn glob_star_leading_slash() {
let filterer = filt(&["/Cargo.*"], &[], &[]).await; let filterer = filt(&["/Cargo.*"], &[], &[], &[], &[]).await;
filterer.file_does_pass("Cargo.toml"); filterer.file_does_pass("Cargo.toml");
filterer.file_does_pass("Cargo.json"); filterer.file_does_pass("Cargo.json");
@ -118,7 +119,7 @@ async fn glob_star_leading_slash() {
#[tokio::test] #[tokio::test]
async fn glob_leading_double_star() { async fn glob_leading_double_star() {
let filterer = filt(&["**/possum"], &[], &[]).await; let filterer = filt(&["**/possum"], &[], &[], &[], &[]).await;
filterer.file_does_pass("possum"); filterer.file_does_pass("possum");
filterer.file_does_pass("foo/bar/possum"); filterer.file_does_pass("foo/bar/possum");
@ -133,7 +134,7 @@ async fn glob_leading_double_star() {
#[tokio::test] #[tokio::test]
async fn glob_trailing_double_star() { async fn glob_trailing_double_star() {
let filterer = filt(&["possum/**"], &[], &[]).await; let filterer = filt(&["possum/**"], &[], &[], &[], &[]).await;
// these do work by expectation and in v1 // these do work by expectation and in v1
filterer.file_does_pass("/test/possum/foo/bar"); filterer.file_does_pass("/test/possum/foo/bar");
@ -147,7 +148,7 @@ async fn glob_trailing_double_star() {
#[tokio::test] #[tokio::test]
async fn glob_middle_double_star() { async fn glob_middle_double_star() {
let filterer = filt(&["apples/**/oranges"], &[], &[]).await; let filterer = filt(&["apples/**/oranges"], &[], &[], &[], &[]).await;
filterer.dir_doesnt_pass("/a/folder"); filterer.dir_doesnt_pass("/a/folder");
filterer.file_does_pass("apples/carrots/oranges"); filterer.file_does_pass("apples/carrots/oranges");
@ -162,7 +163,7 @@ async fn glob_middle_double_star() {
#[tokio::test] #[tokio::test]
async fn glob_double_star_trailing_slash() { async fn glob_double_star_trailing_slash() {
let filterer = filt(&["apples/**/oranges/"], &[], &[]).await; let filterer = filt(&["apples/**/oranges/"], &[], &[], &[], &[]).await;
filterer.dir_doesnt_pass("/a/folder"); filterer.dir_doesnt_pass("/a/folder");
filterer.file_doesnt_pass("apples/carrots/oranges"); filterer.file_doesnt_pass("apples/carrots/oranges");
@ -180,7 +181,7 @@ async fn glob_double_star_trailing_slash() {
#[tokio::test] #[tokio::test]
async fn ignore_exact_filename() { async fn ignore_exact_filename() {
let filterer = filt(&[], &["Cargo.toml"], &[]).await; let filterer = filt(&[], &["Cargo.toml"], &[], &[], &[]).await;
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
filterer.file_doesnt_pass("/test/foo/bar/Cargo.toml"); filterer.file_doesnt_pass("/test/foo/bar/Cargo.toml");
@ -193,7 +194,7 @@ async fn ignore_exact_filename() {
#[tokio::test] #[tokio::test]
async fn ignore_exact_filename_in_folder() { async fn ignore_exact_filename_in_folder() {
let filterer = filt(&[], &["sub/Cargo.toml"], &[]).await; let filterer = filt(&[], &["sub/Cargo.toml"], &[], &[], &[]).await;
filterer.file_does_pass("Cargo.toml"); filterer.file_does_pass("Cargo.toml");
filterer.file_doesnt_pass("sub/Cargo.toml"); filterer.file_doesnt_pass("sub/Cargo.toml");
@ -207,7 +208,7 @@ async fn ignore_exact_filename_in_folder() {
#[tokio::test] #[tokio::test]
async fn ignore_exact_filename_in_hidden_folder() { async fn ignore_exact_filename_in_hidden_folder() {
let filterer = filt(&[], &[".sub/Cargo.toml"], &[]).await; let filterer = filt(&[], &[".sub/Cargo.toml"], &[], &[], &[]).await;
filterer.file_does_pass("Cargo.toml"); filterer.file_does_pass("Cargo.toml");
filterer.file_doesnt_pass(".sub/Cargo.toml"); filterer.file_doesnt_pass(".sub/Cargo.toml");
@ -221,7 +222,7 @@ async fn ignore_exact_filename_in_hidden_folder() {
#[tokio::test] #[tokio::test]
async fn ignore_exact_filenames_multiple() { async fn ignore_exact_filenames_multiple() {
let filterer = filt(&[], &["Cargo.toml", "package.json"], &[]).await; let filterer = filt(&[], &["Cargo.toml", "package.json"], &[], &[], &[]).await;
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
filterer.file_doesnt_pass("/test/foo/bar/Cargo.toml"); filterer.file_doesnt_pass("/test/foo/bar/Cargo.toml");
@ -238,7 +239,7 @@ async fn ignore_exact_filenames_multiple() {
#[tokio::test] #[tokio::test]
async fn ignore_glob_single_final_ext_star() { async fn ignore_glob_single_final_ext_star() {
let filterer = filt(&[], &["Cargo.*"], &[]).await; let filterer = filt(&[], &["Cargo.*"], &[], &[], &[]).await;
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
filterer.file_doesnt_pass("Cargo.json"); filterer.file_doesnt_pass("Cargo.json");
@ -250,7 +251,7 @@ async fn ignore_glob_single_final_ext_star() {
#[tokio::test] #[tokio::test]
async fn ignore_glob_star_trailing_slash() { async fn ignore_glob_star_trailing_slash() {
let filterer = filt(&[], &["Cargo.*/"], &[]).await; let filterer = filt(&[], &["Cargo.*/"], &[], &[], &[]).await;
filterer.file_does_pass("Cargo.toml"); filterer.file_does_pass("Cargo.toml");
filterer.file_does_pass("Cargo.json"); filterer.file_does_pass("Cargo.json");
@ -263,7 +264,7 @@ async fn ignore_glob_star_trailing_slash() {
#[tokio::test] #[tokio::test]
async fn ignore_glob_star_leading_slash() { async fn ignore_glob_star_leading_slash() {
let filterer = filt(&[], &["/Cargo.*"], &[]).await; let filterer = filt(&[], &["/Cargo.*"], &[], &[], &[]).await;
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
filterer.file_doesnt_pass("Cargo.json"); filterer.file_doesnt_pass("Cargo.json");
@ -275,7 +276,7 @@ async fn ignore_glob_star_leading_slash() {
#[tokio::test] #[tokio::test]
async fn ignore_glob_leading_double_star() { async fn ignore_glob_leading_double_star() {
let filterer = filt(&[], &["**/possum"], &[]).await; let filterer = filt(&[], &["**/possum"], &[], &[], &[]).await;
filterer.file_doesnt_pass("possum"); filterer.file_doesnt_pass("possum");
filterer.file_doesnt_pass("foo/bar/possum"); filterer.file_doesnt_pass("foo/bar/possum");
@ -290,7 +291,7 @@ async fn ignore_glob_leading_double_star() {
#[tokio::test] #[tokio::test]
async fn ignore_glob_trailing_double_star() { async fn ignore_glob_trailing_double_star() {
let filterer = filt(&[], &["possum/**"], &[]).await; let filterer = filt(&[], &["possum/**"], &[], &[], &[]).await;
filterer.file_does_pass("possum"); filterer.file_does_pass("possum");
filterer.file_doesnt_pass("possum/foo/bar"); filterer.file_doesnt_pass("possum/foo/bar");
@ -309,7 +310,7 @@ async fn ignore_glob_trailing_double_star() {
#[tokio::test] #[tokio::test]
async fn ignore_glob_middle_double_star() { async fn ignore_glob_middle_double_star() {
let filterer = filt(&[], &["apples/**/oranges"], &[]).await; let filterer = filt(&[], &["apples/**/oranges"], &[], &[], &[]).await;
filterer.dir_does_pass("/a/folder"); filterer.dir_does_pass("/a/folder");
filterer.file_doesnt_pass("apples/carrots/oranges"); filterer.file_doesnt_pass("apples/carrots/oranges");
@ -324,7 +325,7 @@ async fn ignore_glob_middle_double_star() {
#[tokio::test] #[tokio::test]
async fn ignore_glob_double_star_trailing_slash() { async fn ignore_glob_double_star_trailing_slash() {
let filterer = filt(&[], &["apples/**/oranges/"], &[]).await; let filterer = filt(&[], &["apples/**/oranges/"], &[], &[], &[]).await;
filterer.dir_does_pass("/a/folder"); filterer.dir_does_pass("/a/folder");
filterer.file_does_pass("apples/carrots/oranges"); filterer.file_does_pass("apples/carrots/oranges");
@ -342,7 +343,14 @@ async fn ignore_glob_double_star_trailing_slash() {
#[tokio::test] #[tokio::test]
async fn ignores_take_precedence() { async fn ignores_take_precedence() {
let filterer = filt(&["*.docx", "*.toml", "*.json"], &["*.toml", "*.json"], &[]).await; let filterer = filt(
&["*.docx", "*.toml", "*.json"],
&["*.toml", "*.json"],
&[],
&[],
&[],
)
.await;
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
filterer.file_doesnt_pass("/test/foo/bar/Cargo.toml"); filterer.file_doesnt_pass("/test/foo/bar/Cargo.toml");
@ -355,7 +363,7 @@ async fn ignores_take_precedence() {
#[tokio::test] #[tokio::test]
async fn extensions_fail_dirs() { async fn extensions_fail_dirs() {
let filterer = filt(&[], &[], &["py"]).await; let filterer = filt(&[], &[], &[], &["py"], &[]).await;
filterer.file_does_pass("Cargo.py"); filterer.file_does_pass("Cargo.py");
filterer.file_doesnt_pass("Cargo.toml"); filterer.file_doesnt_pass("Cargo.toml");
@ -366,7 +374,7 @@ async fn extensions_fail_dirs() {
#[tokio::test] #[tokio::test]
async fn extensions_fail_extensionless() { async fn extensions_fail_extensionless() {
let filterer = filt(&[], &[], &["py"]).await; let filterer = filt(&[], &[], &[], &["py"], &[]).await;
filterer.file_does_pass("Cargo.py"); filterer.file_does_pass("Cargo.py");
filterer.file_doesnt_pass("Cargo"); filterer.file_doesnt_pass("Cargo");
@ -377,7 +385,7 @@ async fn multipath_allow_on_any_one_pass() {
use watchexec::filter::Filterer; use watchexec::filter::Filterer;
use watchexec_events::{Event, FileType, Tag}; use watchexec_events::{Event, FileType, Tag};
let filterer = filt(&[], &[], &["py"]).await; let filterer = filt(&[], &[], &[], &["py"], &[]).await;
let origin = tokio::fs::canonicalize(".").await.unwrap(); let origin = tokio::fs::canonicalize(".").await.unwrap();
let event = Event { let event = Event {
@ -403,7 +411,7 @@ async fn multipath_allow_on_any_one_pass() {
#[tokio::test] #[tokio::test]
async fn extensions_and_filters_glob() { async fn extensions_and_filters_glob() {
let filterer = filt(&["*/justfile"], &[], &["md", "css"]).await; let filterer = filt(&["*/justfile"], &[], &[], &["md", "css"], &[]).await;
filterer.file_does_pass("foo/justfile"); filterer.file_does_pass("foo/justfile");
filterer.file_does_pass("bar.md"); filterer.file_does_pass("bar.md");
@ -417,7 +425,7 @@ async fn extensions_and_filters_glob() {
#[tokio::test] #[tokio::test]
async fn extensions_and_filters_slash() { async fn extensions_and_filters_slash() {
let filterer = filt(&["/justfile"], &[], &["md", "css"]).await; let filterer = filt(&["/justfile"], &[], &[], &["md", "css"], &[]).await;
filterer.file_does_pass("justfile"); filterer.file_does_pass("justfile");
filterer.file_does_pass("bar.md"); filterer.file_does_pass("bar.md");
@ -427,7 +435,7 @@ async fn extensions_and_filters_slash() {
#[tokio::test] #[tokio::test]
async fn leading_single_glob_file() { async fn leading_single_glob_file() {
let filterer = filt(&["*/justfile"], &[], &[]).await; let filterer = filt(&["*/justfile"], &[], &[], &[], &[]).await;
filterer.file_does_pass("foo/justfile"); filterer.file_does_pass("foo/justfile");
filterer.file_doesnt_pass("notfile"); filterer.file_doesnt_pass("notfile");
@ -443,7 +451,7 @@ async fn nonpath_event_passes() {
use watchexec::filter::Filterer; use watchexec::filter::Filterer;
use watchexec_events::{Event, Source, Tag}; use watchexec_events::{Event, Source, Tag};
let filterer = filt(&[], &[], &["py"]).await; let filterer = filt(&[], &[], &[], &["py"], &[]).await;
assert!(filterer assert!(filterer
.check_event( .check_event(
@ -470,7 +478,7 @@ async fn nonpath_event_passes() {
#[tokio::test] #[tokio::test]
async fn ignore_folder_incorrectly_with_bare_match() { async fn ignore_folder_incorrectly_with_bare_match() {
let filterer = filt(&[], &["prunes"], &[]).await; let filterer = filt(&[], &["prunes"], &[], &[], &[]).await;
filterer.file_does_pass("apples"); filterer.file_does_pass("apples");
filterer.file_does_pass("apples/carrots/cauliflowers/oranges"); filterer.file_does_pass("apples/carrots/cauliflowers/oranges");
@ -501,7 +509,7 @@ async fn ignore_folder_incorrectly_with_bare_match() {
#[tokio::test] #[tokio::test]
async fn ignore_folder_incorrectly_with_bare_and_leading_slash() { async fn ignore_folder_incorrectly_with_bare_and_leading_slash() {
let filterer = filt(&[], &["/prunes"], &[]).await; let filterer = filt(&[], &["/prunes"], &[], &[], &[]).await;
filterer.file_does_pass("apples"); filterer.file_does_pass("apples");
filterer.file_does_pass("apples/carrots/cauliflowers/oranges"); filterer.file_does_pass("apples/carrots/cauliflowers/oranges");
@ -532,7 +540,7 @@ async fn ignore_folder_incorrectly_with_bare_and_leading_slash() {
#[tokio::test] #[tokio::test]
async fn ignore_folder_incorrectly_with_bare_and_trailing_slash() { async fn ignore_folder_incorrectly_with_bare_and_trailing_slash() {
let filterer = filt(&[], &["prunes/"], &[]).await; let filterer = filt(&[], &["prunes/"], &[], &[], &[]).await;
filterer.file_does_pass("apples"); filterer.file_does_pass("apples");
filterer.file_does_pass("apples/carrots/cauliflowers/oranges"); filterer.file_does_pass("apples/carrots/cauliflowers/oranges");
@ -563,7 +571,7 @@ async fn ignore_folder_incorrectly_with_bare_and_trailing_slash() {
#[tokio::test] #[tokio::test]
async fn ignore_folder_incorrectly_with_only_double_double_glob() { async fn ignore_folder_incorrectly_with_only_double_double_glob() {
let filterer = filt(&[], &["**/prunes/**"], &[]).await; let filterer = filt(&[], &["**/prunes/**"], &[], &[], &[]).await;
filterer.file_does_pass("apples"); filterer.file_does_pass("apples");
filterer.file_does_pass("apples/carrots/cauliflowers/oranges"); filterer.file_does_pass("apples/carrots/cauliflowers/oranges");
@ -594,7 +602,7 @@ async fn ignore_folder_incorrectly_with_only_double_double_glob() {
#[tokio::test] #[tokio::test]
async fn ignore_folder_correctly_with_double_and_double_double_globs() { async fn ignore_folder_correctly_with_double_and_double_double_globs() {
let filterer = filt(&[], &["**/prunes", "**/prunes/**"], &[]).await; let filterer = filt(&[], &["**/prunes", "**/prunes/**"], &[], &[], &[]).await;
filterer.file_does_pass("apples"); filterer.file_does_pass("apples");
filterer.file_does_pass("apples/carrots/cauliflowers/oranges"); filterer.file_does_pass("apples/carrots/cauliflowers/oranges");
@ -620,3 +628,94 @@ async fn ignore_folder_correctly_with_double_and_double_double_globs() {
filterer.dir_doesnt_pass("prunes/carrots/cauliflowers/oranges"); filterer.dir_doesnt_pass("prunes/carrots/cauliflowers/oranges");
filterer.dir_doesnt_pass("prunes/carrots/cauliflowers/artichokes/oranges"); filterer.dir_doesnt_pass("prunes/carrots/cauliflowers/artichokes/oranges");
} }
#[tokio::test]
async fn whitelist_overrides_ignore() {
let filterer = filt(&[], &["**/prunes"], &["/prunes"], &[], &[]).await;
filterer.file_does_pass("apples");
filterer.file_does_pass("/prunes");
filterer.dir_does_pass("apples");
filterer.dir_does_pass("/prunes");
filterer.file_does_pass("raw-prunes");
filterer.dir_does_pass("raw-prunes");
filterer.file_doesnt_pass("apples/prunes");
filterer.file_doesnt_pass("raw/prunes");
filterer.dir_doesnt_pass("apples/prunes");
filterer.dir_doesnt_pass("raw/prunes");
}
#[tokio::test]
async fn whitelist_overrides_ignore_files() {
let mut ignore_file = tempfile::NamedTempFile::new().unwrap();
let _ = ignore_file.write(b"prunes");
let origin = std::fs::canonicalize(".").unwrap();
let whitelist = origin.join("prunes").display().to_string();
let filterer = filt(
&[],
&[],
&[&whitelist],
&[],
&[ignore_file.path().to_path_buf()],
)
.await;
filterer.file_does_pass("apples");
filterer.file_does_pass("prunes");
filterer.dir_does_pass("apples");
filterer.dir_does_pass("prunes");
filterer.file_does_pass("raw-prunes");
filterer.dir_does_pass("raw-prunes");
filterer.file_doesnt_pass("apples/prunes");
filterer.file_doesnt_pass("raw/prunes");
filterer.dir_doesnt_pass("apples/prunes");
filterer.dir_doesnt_pass("raw/prunes");
}
#[tokio::test]
async fn whitelist_overrides_ignore_files_nested() {
let mut ignore_file = tempfile::NamedTempFile::new().unwrap();
let _ = ignore_file.write(b"prunes\n");
let origin = std::fs::canonicalize(".").unwrap();
let whitelist = origin.join("prunes").join("target").display().to_string();
let filterer = filt(
&[],
&[],
&[&whitelist],
&[],
&[ignore_file.path().to_path_buf()],
)
.await;
filterer.file_does_pass("apples");
filterer.file_doesnt_pass("prunes");
filterer.dir_does_pass("apples");
filterer.dir_doesnt_pass("prunes");
filterer.file_does_pass("raw-prunes");
filterer.dir_does_pass("raw-prunes");
filterer.file_doesnt_pass("prunes/apples");
filterer.file_doesnt_pass("prunes/raw");
filterer.dir_doesnt_pass("prunes/apples");
filterer.dir_doesnt_pass("prunes/raw");
filterer.file_doesnt_pass("apples/prunes");
filterer.file_doesnt_pass("raw/prunes");
filterer.dir_doesnt_pass("apples/prunes");
filterer.dir_doesnt_pass("raw/prunes");
filterer.file_does_pass("prunes/target");
filterer.dir_does_pass("prunes/target");
filterer.file_doesnt_pass("prunes/nested/target");
filterer.dir_doesnt_pass("prunes/nested/target");
}

View File

@ -3,6 +3,7 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use ignore_files::IgnoreFile;
use watchexec::{error::RuntimeError, filter::Filterer}; use watchexec::{error::RuntimeError, filter::Filterer};
use watchexec_events::{Event, FileType, Priority, Tag}; use watchexec_events::{Event, FileType, Priority, Tag};
use watchexec_filterer_globset::GlobsetFilterer; use watchexec_filterer_globset::GlobsetFilterer;
@ -102,7 +103,9 @@ fn tracing_init() {
pub async fn globset_filt( pub async fn globset_filt(
filters: &[&str], filters: &[&str],
ignores: &[&str], ignores: &[&str],
whitelists: &[&str],
extensions: &[&str], extensions: &[&str],
ignore_files: &[PathBuf],
) -> GlobsetFilterer { ) -> GlobsetFilterer {
let origin = tokio::fs::canonicalize(".").await.unwrap(); let origin = tokio::fs::canonicalize(".").await.unwrap();
tracing_init(); tracing_init();
@ -110,7 +113,12 @@ pub async fn globset_filt(
origin, origin,
filters.iter().map(|s| ((*s).to_string(), None)), filters.iter().map(|s| ((*s).to_string(), None)),
ignores.iter().map(|s| ((*s).to_string(), None)), ignores.iter().map(|s| ((*s).to_string(), None)),
vec![], whitelists.iter().map(|s| (*s).into()),
ignore_files.iter().map(|path| IgnoreFile {
path: path.clone(),
applies_in: None,
applies_to: None,
}),
extensions.iter().map(OsString::from), extensions.iter().map(OsString::from),
) )
.await .await

View File

@ -2,6 +2,8 @@
## Next (YYYY-MM-DD) ## Next (YYYY-MM-DD)
## v4.0.1 (2024-04-28)
## v4.0.0 (2024-04-20) ## v4.0.0 (2024-04-20)
- Deps: watchexec 4 - Deps: watchexec 4

View File

@ -1,6 +1,6 @@
[package] [package]
name = "watchexec-filterer-ignore" name = "watchexec-filterer-ignore"
version = "4.0.0" version = "4.0.1"
authors = ["Félix Saparelli <felix@passcod.name>"] authors = ["Félix Saparelli <felix@passcod.name>"]
license = "Apache-2.0" license = "Apache-2.0"
@ -22,11 +22,11 @@ normalize-path = "0.2.1"
tracing = "0.1.40" tracing = "0.1.40"
[dependencies.ignore-files] [dependencies.ignore-files]
version = "3.0.0" version = "3.0.1"
path = "../../ignore-files" path = "../../ignore-files"
[dependencies.watchexec] [dependencies.watchexec]
version = "4.0.0" version = "4.1.0"
path = "../../lib" path = "../../lib"
[dependencies.watchexec-events] [dependencies.watchexec-events]

View File

@ -2,6 +2,8 @@
## Next (YYYY-MM-DD) ## Next (YYYY-MM-DD)
## v3.0.1 (2024-04-28)
- Hide fmt::Debug spew from ignore crate, use `full_debug` feature to restore. - Hide fmt::Debug spew from ignore crate, use `full_debug` feature to restore.
## v3.0.0 (2024-04-20) ## v3.0.0 (2024-04-20)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ignore-files" name = "ignore-files"
version = "3.0.0" version = "3.0.1"
authors = ["Félix Saparelli <felix@passcod.name>"] authors = ["Félix Saparelli <felix@passcod.name>"]
license = "Apache-2.0" license = "Apache-2.0"

View File

@ -2,6 +2,8 @@
## Next (YYYY-MM-DD) ## Next (YYYY-MM-DD)
## v4.1.0 (2024-04-28)
- Feature: non-recursive watches with `WatchedPath::non_recursive()` - Feature: non-recursive watches with `WatchedPath::non_recursive()`
- Fix: `config.pathset()` now preserves `WatchedPath` attributes - Fix: `config.pathset()` now preserves `WatchedPath` attributes
- Refactor: move `WatchedPath` to the root of the crate (old path remains as re-export for now) - Refactor: move `WatchedPath` to the root of the crate (old path remains as re-export for now)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "watchexec" name = "watchexec"
version = "4.0.0" version = "4.1.0"
authors = ["Félix Saparelli <felix@passcod.name>", "Matt Green <mattgreenrocks@gmail.com>"] authors = ["Félix Saparelli <felix@passcod.name>", "Matt Green <mattgreenrocks@gmail.com>"]
license = "Apache-2.0" license = "Apache-2.0"
@ -43,7 +43,7 @@ version = "2.0.0"
path = "../supervisor" path = "../supervisor"
[dependencies.ignore-files] [dependencies.ignore-files]
version = "3.0.0" version = "3.0.1"
path = "../ignore-files" path = "../ignore-files"
[dependencies.project-origins] [dependencies.project-origins]

View File

@ -74,8 +74,8 @@ mod watchexec;
#[doc(inline)] #[doc(inline)]
pub use crate::{ pub use crate::{
id::Id, id::Id,
watchexec::{ErrorHook, Watchexec},
watched_path::WatchedPath, watched_path::WatchedPath,
watchexec::{ErrorHook, Watchexec},
}; };
#[doc(no_inline)] #[doc(no_inline)]

View File

@ -1,10 +1,10 @@
.ie \n(.g .ds Aq \(aq .ie \n(.g .ds Aq \(aq
.el .ds Aq ' .el .ds Aq '
.TH watchexec 1 "watchexec 2.0.0" .TH watchexec 1 "watchexec 2.1.2"
.SH NAME .SH NAME
watchexec \- Execute commands when watched files change watchexec \- Execute commands when watched files change
.SH SYNOPSIS .SH SYNOPSIS
\fBwatchexec\fR [\fB\-w\fR|\fB\-\-watch\fR] [\fB\-W\fR|\fB\-\-watch\-non\-recursive\fR] [\fB\-c\fR|\fB\-\-clear\fR] [\fB\-o\fR|\fB\-\-on\-busy\-update\fR] [\fB\-r\fR|\fB\-\-restart\fR] [\fB\-s\fR|\fB\-\-signal\fR] [\fB\-\-stop\-signal\fR] [\fB\-\-stop\-timeout\fR] [\fB\-\-map\-signal\fR] [\fB\-d\fR|\fB\-\-debounce\fR] [\fB\-\-stdin\-quit\fR] [\fB\-\-no\-vcs\-ignore\fR] [\fB\-\-no\-project\-ignore\fR] [\fB\-\-no\-global\-ignore\fR] [\fB\-\-no\-default\-ignore\fR] [\fB\-\-no\-discover\-ignore\fR] [\fB\-\-ignore\-nothing\fR] [\fB\-p\fR|\fB\-\-postpone\fR] [\fB\-\-delay\-run\fR] [\fB\-\-poll\fR] [\fB\-\-shell\fR] [\fB\-n \fR] [\fB\-\-emit\-events\-to\fR] [\fB\-\-only\-emit\-events\fR] [\fB\-E\fR|\fB\-\-env\fR] [\fB\-\-no\-process\-group\fR] [\fB\-\-wrap\-process\fR] [\fB\-N\fR|\fB\-\-notify\fR] [\fB\-\-color\fR] [\fB\-\-timings\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-bell\fR] [\fB\-\-project\-origin\fR] [\fB\-\-workdir\fR] [\fB\-e\fR|\fB\-\-exts\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-filter\-file\fR] [\fB\-j\fR|\fB\-\-filter\-prog\fR] [\fB\-i\fR|\fB\-\-ignore\fR] [\fB\-\-ignore\-file\fR] [\fB\-\-fs\-events\fR] [\fB\-\-no\-meta\fR] [\fB\-\-print\-events\fR] [\fB\-\-manual\fR] [\fB\-\-completions\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-\-log\-file\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICOMMAND\fR] \fBwatchexec\fR [\fB\-w\fR|\fB\-\-watch\fR] [\fB\-W\fR|\fB\-\-watch\-non\-recursive\fR] [\fB\-F\fR|\fB\-\-watch\-file\fR] [\fB\-c\fR|\fB\-\-clear\fR] [\fB\-o\fR|\fB\-\-on\-busy\-update\fR] [\fB\-r\fR|\fB\-\-restart\fR] [\fB\-s\fR|\fB\-\-signal\fR] [\fB\-\-stop\-signal\fR] [\fB\-\-stop\-timeout\fR] [\fB\-\-map\-signal\fR] [\fB\-d\fR|\fB\-\-debounce\fR] [\fB\-\-stdin\-quit\fR] [\fB\-\-no\-vcs\-ignore\fR] [\fB\-\-no\-project\-ignore\fR] [\fB\-\-no\-global\-ignore\fR] [\fB\-\-no\-default\-ignore\fR] [\fB\-\-no\-discover\-ignore\fR] [\fB\-\-ignore\-nothing\fR] [\fB\-p\fR|\fB\-\-postpone\fR] [\fB\-\-delay\-run\fR] [\fB\-\-poll\fR] [\fB\-\-shell\fR] [\fB\-n \fR] [\fB\-\-emit\-events\-to\fR] [\fB\-\-only\-emit\-events\fR] [\fB\-E\fR|\fB\-\-env\fR] [\fB\-\-no\-process\-group\fR] [\fB\-\-wrap\-process\fR] [\fB\-N\fR|\fB\-\-notify\fR] [\fB\-\-color\fR] [\fB\-\-timings\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-bell\fR] [\fB\-\-project\-origin\fR] [\fB\-\-workdir\fR] [\fB\-e\fR|\fB\-\-exts\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-filter\-file\fR] [\fB\-j\fR|\fB\-\-filter\-prog\fR] [\fB\-i\fR|\fB\-\-ignore\fR] [\fB\-\-ignore\-file\fR] [\fB\-\-fs\-events\fR] [\fB\-\-no\-meta\fR] [\fB\-\-print\-events\fR] [\fB\-\-manual\fR] [\fB\-\-completions\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-\-log\-file\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICOMMAND\fR]
.SH DESCRIPTION .SH DESCRIPTION
Execute commands when watched files change. Execute commands when watched files change.
.PP .PP
@ -55,6 +55,15 @@ Unlike \*(Aq\-w\*(Aq, folders watched with this option are not recursed into.
This option can be specified multiple times to watch multiple directories non\-recursively. This option can be specified multiple times to watch multiple directories non\-recursively.
.TP .TP
\fB\-F\fR, \fB\-\-watch\-file\fR=\fIPATH\fR
Watch files and directories from a file
Each line in the file will be interpreted as if given to \*(Aq\-w\*(Aq.
For more complex uses (like watching non\-recursively), use the argfile capability: build a file containing command\-line options and pass it to watchexec with `@path/to/argfile`.
The special value \*(Aq\-\*(Aq will read from STDIN; this in incompatible with \*(Aq\-\-stdin\-quit\*(Aq.
.TP
\fB\-c\fR, \fB\-\-clear\fR=\fIMODE\fR \fB\-c\fR, \fB\-\-clear\fR=\fIMODE\fR
Clear screen before running command Clear screen before running command
@ -582,6 +591,6 @@ Use @argfile as first argument to load arguments from the file \*(Aqargfile\*(Aq
Didn\*(Aqt expect this much output? Use the short \*(Aq\-h\*(Aq flag to get short help. Didn\*(Aqt expect this much output? Use the short \*(Aq\-h\*(Aq flag to get short help.
.SH VERSION .SH VERSION
v2.0.0 v2.1.2
.SH AUTHORS .SH AUTHORS
Félix Saparelli <felix@passcod.name>, Matt Green <mattgreenrocks@gmail.com> Félix Saparelli <felix@passcod.name>, Matt Green <mattgreenrocks@gmail.com>

View File

@ -5,10 +5,11 @@ watchexec - Execute commands when watched files change
# SYNOPSIS # SYNOPSIS
**watchexec** \[**-w**\|**\--watch**\] **watchexec** \[**-w**\|**\--watch**\]
\[**-W**\|**\--watch-non-recursive**\] \[**-c**\|**\--clear**\] \[**-W**\|**\--watch-non-recursive**\] \[**-F**\|**\--watch-file**\]
\[**-o**\|**\--on-busy-update**\] \[**-r**\|**\--restart**\] \[**-c**\|**\--clear**\] \[**-o**\|**\--on-busy-update**\]
\[**-s**\|**\--signal**\] \[**\--stop-signal**\] \[**\--stop-timeout**\] \[**-r**\|**\--restart**\] \[**-s**\|**\--signal**\]
\[**\--map-signal**\] \[**-d**\|**\--debounce**\] \[**\--stdin-quit**\] \[**\--stop-signal**\] \[**\--stop-timeout**\] \[**\--map-signal**\]
\[**-d**\|**\--debounce**\] \[**\--stdin-quit**\]
\[**\--no-vcs-ignore**\] \[**\--no-project-ignore**\] \[**\--no-vcs-ignore**\] \[**\--no-project-ignore**\]
\[**\--no-global-ignore**\] \[**\--no-default-ignore**\] \[**\--no-global-ignore**\] \[**\--no-default-ignore**\]
\[**\--no-discover-ignore**\] \[**\--ignore-nothing**\] \[**\--no-discover-ignore**\] \[**\--ignore-nothing**\]
@ -92,6 +93,19 @@ Unlike -w, folders watched with this option are not recursed into.
This option can be specified multiple times to watch multiple This option can be specified multiple times to watch multiple
directories non-recursively. directories non-recursively.
**-F**, **\--watch-file**=*PATH*
: Watch files and directories from a file
Each line in the file will be interpreted as if given to -w.
For more complex uses (like watching non-recursively), use the argfile
capability: build a file containing command-line options and pass it to
watchexec with \`@path/to/argfile\`.
The special value - will read from STDIN; this in incompatible with
\--stdin-quit.
**-c**, **\--clear**=*MODE* **-c**, **\--clear**=*MODE*
: Clear screen before running command : Clear screen before running command
@ -861,7 +875,7 @@ Didnt expect this much output? Use the short -h flag to get short help.
# VERSION # VERSION
v2.0.0 v2.1.2
# AUTHORS # AUTHORS