2021-12-23 15:37:51 +01:00
|
|
|
use std::{
|
|
|
|
collections::HashSet,
|
|
|
|
path::{Path, PathBuf},
|
|
|
|
};
|
|
|
|
|
2024-01-01 06:01:14 +01:00
|
|
|
use ignore_files::{IgnoreFile, IgnoreFilesFromOriginArgs};
|
2022-03-27 23:06:20 +02:00
|
|
|
use miette::{miette, IntoDiagnostic, Result};
|
2022-06-15 05:25:05 +02:00
|
|
|
use project_origins::ProjectType;
|
2023-01-06 14:53:49 +01:00
|
|
|
use tokio::fs::canonicalize;
|
2022-09-02 11:12:47 +02:00
|
|
|
use tracing::{debug, info, warn};
|
2022-06-15 05:25:05 +02:00
|
|
|
use watchexec::paths::common_prefix;
|
2021-12-23 15:37:51 +01:00
|
|
|
|
2023-03-05 02:57:34 +01:00
|
|
|
use crate::args::Args;
|
|
|
|
|
2024-04-28 08:33:07 +02:00
|
|
|
pub async fn project_origin(args: &Args) -> Result<PathBuf> {
|
2023-03-05 02:57:34 +01:00
|
|
|
let project_origin = if let Some(origin) = &args.project_origin {
|
2022-01-25 16:19:07 +01:00
|
|
|
debug!(?origin, "project origin override");
|
2023-01-06 14:53:49 +01:00
|
|
|
canonicalize(origin).await.into_diagnostic()?
|
2022-01-25 16:19:07 +01:00
|
|
|
} else {
|
2023-01-06 14:53:49 +01:00
|
|
|
let homedir = match dirs::home_dir() {
|
|
|
|
None => None,
|
|
|
|
Some(dir) => Some(canonicalize(dir).await.into_diagnostic()?),
|
|
|
|
};
|
2022-01-25 16:19:07 +01:00
|
|
|
debug!(?homedir, "home directory");
|
|
|
|
|
2024-04-28 08:33:07 +02:00
|
|
|
let homedir_requested = homedir.as_ref().map_or(false, |home| {
|
|
|
|
args.paths
|
|
|
|
.binary_search_by_key(home, |w| PathBuf::from(w.clone()))
|
|
|
|
.is_ok()
|
|
|
|
});
|
2022-01-25 16:19:07 +01:00
|
|
|
debug!(
|
|
|
|
?homedir_requested,
|
|
|
|
"resolved whether the homedir is explicitly requested"
|
|
|
|
);
|
2022-04-12 03:30:39 +02:00
|
|
|
|
2022-01-25 16:19:07 +01:00
|
|
|
let mut origins = HashSet::new();
|
2024-04-28 08:33:07 +02:00
|
|
|
for path in &args.paths {
|
|
|
|
origins.extend(project_origins::origins(path).await);
|
2022-01-25 16:19:07 +01:00
|
|
|
}
|
2021-12-23 15:37:51 +01:00
|
|
|
|
2022-01-25 16:19:07 +01:00
|
|
|
match (homedir, homedir_requested) {
|
|
|
|
(Some(ref dir), false) if origins.contains(dir) => {
|
|
|
|
debug!("removing homedir from origins");
|
|
|
|
origins.remove(dir);
|
|
|
|
}
|
|
|
|
_ => {}
|
2022-04-12 03:30:39 +02:00
|
|
|
}
|
|
|
|
|
2022-01-25 16:19:07 +01:00
|
|
|
if origins.is_empty() {
|
|
|
|
debug!("no origins, using current directory");
|
2024-04-28 08:33:07 +02:00
|
|
|
origins.insert(args.workdir.clone().unwrap());
|
2022-01-25 16:19:07 +01:00
|
|
|
}
|
2022-03-27 23:06:20 +02:00
|
|
|
|
2022-01-25 16:19:07 +01:00
|
|
|
debug!(?origins, "resolved all project origins");
|
2021-12-23 15:37:51 +01:00
|
|
|
|
2022-01-25 16:19:07 +01:00
|
|
|
// This canonicalize is probably redundant
|
|
|
|
canonicalize(
|
|
|
|
common_prefix(&origins)
|
|
|
|
.ok_or_else(|| miette!("no common prefix, but this should never fail"))?,
|
|
|
|
)
|
2023-01-06 14:53:49 +01:00
|
|
|
.await
|
2022-01-25 16:19:07 +01:00
|
|
|
.into_diagnostic()?
|
|
|
|
};
|
2024-04-28 08:33:07 +02:00
|
|
|
debug!(?project_origin, "resolved common/project origin");
|
2021-12-23 15:37:51 +01:00
|
|
|
|
2024-04-28 08:33:07 +02:00
|
|
|
Ok(project_origin)
|
2021-12-23 15:37:51 +01:00
|
|
|
}
|
|
|
|
|
2022-01-16 04:11:17 +01:00
|
|
|
pub async fn vcs_types(origin: &Path) -> Vec<ProjectType> {
|
2022-06-15 05:25:05 +02:00
|
|
|
let vcs_types = project_origins::types(origin)
|
2021-12-23 15:37:51 +01:00
|
|
|
.await
|
|
|
|
.into_iter()
|
|
|
|
.filter(|pt| pt.is_vcs())
|
|
|
|
.collect::<Vec<_>>();
|
2024-04-28 08:33:07 +02:00
|
|
|
info!(?vcs_types, "effective vcs types");
|
2022-01-16 04:11:17 +01:00
|
|
|
vcs_types
|
|
|
|
}
|
2021-12-23 15:37:51 +01:00
|
|
|
|
2024-04-28 08:33:07 +02:00
|
|
|
pub async fn ignores(args: &Args, vcs_types: &[ProjectType]) -> Result<Vec<IgnoreFile>> {
|
|
|
|
let origin = args.project_origin.clone().unwrap();
|
2021-12-23 15:37:51 +01:00
|
|
|
let mut skip_git_global_excludes = false;
|
2023-08-30 06:01:23 +02:00
|
|
|
|
|
|
|
let mut ignores = if args.no_project_ignore {
|
|
|
|
Vec::new()
|
|
|
|
} else {
|
2024-04-28 08:33:07 +02:00
|
|
|
let ignore_files = args.ignore_files.iter().map(|path| {
|
|
|
|
if path.is_absolute() {
|
|
|
|
path.into()
|
|
|
|
} else {
|
|
|
|
origin.join(path)
|
|
|
|
}
|
|
|
|
});
|
2024-01-04 10:32:47 +01:00
|
|
|
|
|
|
|
let (mut ignores, errors) = ignore_files::from_origin(
|
2024-04-28 08:33:07 +02:00
|
|
|
IgnoreFilesFromOriginArgs::new_unchecked(
|
|
|
|
&origin,
|
|
|
|
args.paths.iter().map(PathBuf::from),
|
|
|
|
ignore_files,
|
|
|
|
)
|
|
|
|
.canonicalise()
|
|
|
|
.await
|
|
|
|
.into_diagnostic()?,
|
2024-01-04 10:32:47 +01:00
|
|
|
)
|
2024-01-01 06:01:14 +01:00
|
|
|
.await;
|
|
|
|
|
2023-08-30 06:01:23 +02:00
|
|
|
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 {
|
2021-12-23 15:37:51 +01:00
|
|
|
applies_to: Some(ProjectType::Git),
|
|
|
|
applies_in: None,
|
|
|
|
..
|
2023-08-30 06:01:23 +02:00
|
|
|
} = ig
|
|
|
|
{
|
|
|
|
warn!("project git config overrides the global excludes");
|
|
|
|
skip_git_global_excludes = true;
|
2021-12-23 15:37:51 +01:00
|
|
|
}
|
2023-08-30 06:01:23 +02:00
|
|
|
})
|
|
|
|
.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
|
|
|
|
};
|
2021-12-23 15:37:51 +01:00
|
|
|
|
2022-02-07 04:41:38 +01:00
|
|
|
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"
|
|
|
|
);
|
2021-12-23 15:37:51 +01:00
|
|
|
|
2023-03-05 02:57:34 +01:00
|
|
|
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 {
|
2021-12-29 07:19:43 +01:00
|
|
|
ignores = ignores
|
|
|
|
.into_iter()
|
|
|
|
.filter(|ig| {
|
|
|
|
!ig.applies_in
|
|
|
|
.as_ref()
|
2024-04-28 08:33:07 +02:00
|
|
|
.map_or(false, |p| p.starts_with(&origin))
|
2021-12-29 07:19:43 +01:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
debug!(
|
|
|
|
?ignores,
|
|
|
|
"filtered ignores to exclude project-local ignores"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-03-05 02:57:34 +01:00
|
|
|
if args.no_global_ignore {
|
2021-12-29 07:19:43 +01:00
|
|
|
ignores = ignores
|
|
|
|
.into_iter()
|
2023-08-30 05:43:57 +02:00
|
|
|
.filter(|ig| ig.applies_in.is_some())
|
2021-12-29 07:19:43 +01:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
debug!(?ignores, "filtered ignores to exclude global ignores");
|
|
|
|
}
|
|
|
|
|
2023-03-05 02:57:34 +01:00
|
|
|
if args.no_vcs_ignore {
|
2021-12-29 07:19:43 +01:00
|
|
|
ignores = ignores
|
|
|
|
.into_iter()
|
2023-08-30 05:43:57 +02:00
|
|
|
.filter(|ig| ig.applies_to.is_none())
|
2021-12-29 07:19:43 +01:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
debug!(?ignores, "filtered ignores to exclude VCS-specific ignores");
|
|
|
|
}
|
|
|
|
|
2022-06-28 15:40:20 +02:00
|
|
|
info!(files=?ignores.iter().map(|ig| ig.path.as_path()).collect::<Vec<_>>(), "found some ignores");
|
2024-01-01 06:01:14 +01:00
|
|
|
Ok(ignores)
|
2021-12-23 15:37:51 +01:00
|
|
|
}
|