2022-06-15 05:25:05 +02:00
|
|
|
use std::path::{Path, PathBuf};
|
2021-12-14 18:32:30 +01:00
|
|
|
|
2022-06-15 05:25:05 +02:00
|
|
|
use ignore_files::{IgnoreFile, IgnoreFilter};
|
|
|
|
use project_origins::ProjectType;
|
2023-11-25 21:33:44 +01:00
|
|
|
use watchexec::{error::RuntimeError, filter::Filterer};
|
|
|
|
use watchexec_events::{
|
|
|
|
filekind::FileEventKind, Event, FileType, Priority, ProcessEnd, Source, Tag,
|
2021-12-14 18:32:30 +01:00
|
|
|
};
|
2022-06-15 05:25:05 +02:00
|
|
|
use watchexec_filterer_ignore::IgnoreFilterer;
|
2023-03-18 09:32:24 +01:00
|
|
|
use watchexec_signals::Signal;
|
2022-06-15 05:25:05 +02:00
|
|
|
|
2022-01-15 03:15:40 +01:00
|
|
|
pub mod ignore {
|
|
|
|
pub use super::ig_file as file;
|
|
|
|
pub use super::ignore_filt as filt;
|
|
|
|
pub use super::Applies;
|
|
|
|
pub use super::PathHarness;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait PathHarness: Filterer {
|
2021-12-14 18:32:30 +01:00
|
|
|
fn check_path(
|
|
|
|
&self,
|
|
|
|
path: PathBuf,
|
|
|
|
file_type: Option<FileType>,
|
2022-01-15 03:15:40 +01:00
|
|
|
) -> std::result::Result<bool, RuntimeError> {
|
|
|
|
let event = Event {
|
|
|
|
tags: vec![Tag::Path { path, file_type }],
|
|
|
|
metadata: Default::default(),
|
|
|
|
};
|
|
|
|
|
2022-06-11 08:43:11 +02:00
|
|
|
self.check_event(&event, Priority::Normal)
|
2022-01-15 03:15:40 +01:00
|
|
|
}
|
2021-12-14 18:32:30 +01:00
|
|
|
|
|
|
|
fn path_pass(&self, path: &str, file_type: Option<FileType>, pass: bool) {
|
2023-01-06 14:53:49 +01:00
|
|
|
let origin = std::fs::canonicalize(".").unwrap();
|
2021-12-14 18:32:30 +01:00
|
|
|
let full_path = if let Some(suf) = path.strip_prefix("/test/") {
|
|
|
|
origin.join(suf)
|
|
|
|
} else if Path::new(path).has_root() {
|
|
|
|
path.into()
|
|
|
|
} else {
|
|
|
|
origin.join(path)
|
|
|
|
};
|
|
|
|
|
|
|
|
tracing::info!(?path, ?file_type, ?pass, "check");
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
self.check_path(full_path, file_type).unwrap(),
|
|
|
|
pass,
|
|
|
|
"{} {:?} (expected {})",
|
|
|
|
match file_type {
|
|
|
|
Some(FileType::File) => "file",
|
|
|
|
Some(FileType::Dir) => "dir",
|
|
|
|
Some(FileType::Symlink) => "symlink",
|
|
|
|
Some(FileType::Other) => "other",
|
|
|
|
None => "path",
|
|
|
|
},
|
|
|
|
path,
|
|
|
|
if pass { "pass" } else { "fail" }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn file_does_pass(&self, path: &str) {
|
|
|
|
self.path_pass(path, Some(FileType::File), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn file_doesnt_pass(&self, path: &str) {
|
|
|
|
self.path_pass(path, Some(FileType::File), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dir_does_pass(&self, path: &str) {
|
|
|
|
self.path_pass(path, Some(FileType::Dir), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dir_doesnt_pass(&self, path: &str) {
|
|
|
|
self.path_pass(path, Some(FileType::Dir), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unk_does_pass(&self, path: &str) {
|
|
|
|
self.path_pass(path, None, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unk_doesnt_pass(&self, path: &str) {
|
|
|
|
self.path_pass(path, None, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-15 03:15:40 +01:00
|
|
|
impl PathHarness for IgnoreFilterer {}
|
2021-12-14 18:32:30 +01:00
|
|
|
|
2021-12-14 19:24:13 +01:00
|
|
|
pub trait TaggedHarness {
|
2022-06-11 08:43:11 +02:00
|
|
|
fn check_tag(&self, tag: Tag, priority: Priority) -> std::result::Result<bool, RuntimeError>;
|
|
|
|
|
|
|
|
fn priority_pass(&self, priority: Priority, pass: bool) {
|
|
|
|
tracing::info!(?priority, ?pass, "check");
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
self.check_tag(Tag::Source(Source::Filesystem), priority)
|
|
|
|
.unwrap(),
|
|
|
|
pass,
|
|
|
|
"{priority:?} (expected {})",
|
|
|
|
if pass { "pass" } else { "fail" }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn priority_does_pass(&self, priority: Priority) {
|
|
|
|
self.priority_pass(priority, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn priority_doesnt_pass(&self, priority: Priority) {
|
|
|
|
self.priority_pass(priority, false);
|
|
|
|
}
|
2021-12-14 19:24:13 +01:00
|
|
|
|
|
|
|
fn tag_pass(&self, tag: Tag, pass: bool) {
|
|
|
|
tracing::info!(?tag, ?pass, "check");
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-06-11 08:43:11 +02:00
|
|
|
self.check_tag(tag.clone(), Priority::Normal).unwrap(),
|
2021-12-14 19:24:13 +01:00
|
|
|
pass,
|
2022-06-11 08:43:11 +02:00
|
|
|
"{tag:?} (expected {})",
|
2021-12-14 19:24:13 +01:00
|
|
|
if pass { "pass" } else { "fail" }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fek_does_pass(&self, fek: FileEventKind) {
|
|
|
|
self.tag_pass(Tag::FileEventKind(fek), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fek_doesnt_pass(&self, fek: FileEventKind) {
|
|
|
|
self.tag_pass(Tag::FileEventKind(fek), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn source_does_pass(&self, source: Source) {
|
|
|
|
self.tag_pass(Tag::Source(source), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn source_doesnt_pass(&self, source: Source) {
|
|
|
|
self.tag_pass(Tag::Source(source), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pid_does_pass(&self, pid: u32) {
|
|
|
|
self.tag_pass(Tag::Process(pid), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pid_doesnt_pass(&self, pid: u32) {
|
|
|
|
self.tag_pass(Tag::Process(pid), false);
|
|
|
|
}
|
|
|
|
|
2023-03-18 09:32:24 +01:00
|
|
|
fn signal_does_pass(&self, sig: Signal) {
|
2021-12-14 19:24:13 +01:00
|
|
|
self.tag_pass(Tag::Signal(sig), true);
|
|
|
|
}
|
|
|
|
|
2023-03-18 09:32:24 +01:00
|
|
|
fn signal_doesnt_pass(&self, sig: Signal) {
|
2021-12-14 19:24:13 +01:00
|
|
|
self.tag_pass(Tag::Signal(sig), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn complete_does_pass(&self, exit: Option<ProcessEnd>) {
|
|
|
|
self.tag_pass(Tag::ProcessCompletion(exit), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn complete_doesnt_pass(&self, exit: Option<ProcessEnd>) {
|
|
|
|
self.tag_pass(Tag::ProcessCompletion(exit), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-15 03:15:40 +01:00
|
|
|
fn tracing_init() {
|
|
|
|
use tracing_subscriber::{
|
|
|
|
fmt::{format::FmtSpan, Subscriber},
|
|
|
|
util::SubscriberInitExt,
|
|
|
|
EnvFilter,
|
|
|
|
};
|
|
|
|
Subscriber::builder()
|
|
|
|
.pretty()
|
2022-01-15 04:40:01 +01:00
|
|
|
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
2022-01-15 03:15:40 +01:00
|
|
|
.with_env_filter(EnvFilter::from_default_env())
|
|
|
|
.finish()
|
|
|
|
.try_init()
|
|
|
|
.ok();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn ignore_filt(origin: &str, ignore_files: &[IgnoreFile]) -> IgnoreFilterer {
|
|
|
|
tracing_init();
|
2023-01-06 14:53:49 +01:00
|
|
|
let origin = tokio::fs::canonicalize(".").await.unwrap().join(origin);
|
2022-06-15 05:25:05 +02:00
|
|
|
IgnoreFilterer(
|
|
|
|
IgnoreFilter::new(origin, ignore_files)
|
2021-12-14 18:32:30 +01:00
|
|
|
.await
|
2022-06-15 05:25:05 +02:00
|
|
|
.expect("making filterer"),
|
|
|
|
)
|
2021-12-23 14:21:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ig_file(name: &str) -> IgnoreFile {
|
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 02:12:59 +01:00
|
|
|
let origin = std::fs::canonicalize(".").unwrap();
|
|
|
|
let path = origin.join("tests").join("ignores").join(name);
|
2021-12-14 18:32:30 +01:00
|
|
|
IgnoreFile {
|
|
|
|
path,
|
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 02:12:59 +01:00
|
|
|
applies_in: Some(origin),
|
2021-12-14 18:32:30 +01:00
|
|
|
applies_to: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Applies {
|
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 02:12:59 +01:00
|
|
|
fn applies_globally(self) -> Self;
|
2021-12-14 18:32:30 +01:00
|
|
|
fn applies_in(self, origin: &str) -> Self;
|
|
|
|
fn applies_to(self, project_type: ProjectType) -> Self;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Applies for IgnoreFile {
|
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 02:12:59 +01:00
|
|
|
fn applies_globally(mut self) -> Self {
|
|
|
|
self.applies_in = None;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2021-12-14 18:32:30 +01:00
|
|
|
fn applies_in(mut self, origin: &str) -> Self {
|
2023-01-06 14:53:49 +01:00
|
|
|
let origin = std::fs::canonicalize(".").unwrap().join(origin);
|
2021-12-14 18:32:30 +01:00
|
|
|
self.applies_in = Some(origin);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn applies_to(mut self, project_type: ProjectType) -> Self {
|
|
|
|
self.applies_to = Some(project_type);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|