From 435b94178309a7deaca021815ee77f6bb53a8473 Mon Sep 17 00:00:00 2001 From: Jeff Repanich Date: Sat, 25 Jul 2026 12:55:43 -0400 Subject: [PATCH] feat: exclude configured documentation paths --- README.md | 4 ++++ src/repository_checks.rs | 41 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c21ff75..968ffd5 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ out of the shared binary: ```toml [docs] required = ["README.md", "docs/README.md"] +exclude_paths = ["ui/node_modules"] forbidden_paths = ["docs/archive/old.md"] forbidden_text = ["placeholder text"] @@ -91,6 +92,9 @@ allowlist = ["src/large_provider.rs"] legacy_allowlist = [] ``` +Use `docs.exclude_paths` for generated or vendored trees that are not +repository-owned Markdown. + `summarize-benchmarks` also accepts `--product-name` and `--report-title` if you want to override the default report branding. ### Stress Reports diff --git a/src/repository_checks.rs b/src/repository_checks.rs index 9c064f7..a9094c0 100644 --- a/src/repository_checks.rs +++ b/src/repository_checks.rs @@ -59,6 +59,8 @@ struct DocsConfig { #[serde(default)] required: Vec, #[serde(default)] + exclude_paths: Vec, + #[serde(default)] forbidden_paths: Vec, #[serde(default)] forbidden_text: Vec, @@ -70,7 +72,15 @@ pub fn validate_docs(args: ValidateDocsArgs) -> Result { let markdown: Vec = WalkDir::new(&root) .into_iter() .filter_entry(|entry| { - entry.file_name() != "target" && !entry.file_name().to_string_lossy().starts_with('.') + let name = entry.file_name().to_string_lossy(); + if entry.depth() > 0 + && (name == "target" + || name.starts_with('.') + || is_excluded(&root, entry.path(), &config.exclude_paths)) + { + return false; + } + true }) .filter_map(Result::ok) .filter(|entry| { @@ -133,6 +143,14 @@ pub fn validate_docs(args: ValidateDocsArgs) -> Result { Ok(1) } +fn is_excluded(root: &Path, path: &Path, excluded: &[String]) -> bool { + let name = relative(root, path); + excluded.iter().any(|prefix| { + let prefix = prefix.trim_end_matches('/'); + name == prefix || name.starts_with(&format!("{prefix}/")) + }) +} + fn relative(root: &Path, path: &Path) -> String { path.strip_prefix(root) .unwrap_or(path) @@ -405,6 +423,27 @@ fn production_lines(text: &str) -> usize { count } +#[cfg(test)] +mod tests { + use super::is_excluded; + use std::path::Path; + + #[test] + fn should_exclude_configured_directory_and_children() { + let root = Path::new("/repo"); + assert!(is_excluded( + root, + Path::new("/repo/ui/node_modules/pkg/readme.md"), + &["ui/node_modules".into()] + )); + assert!(!is_excluded( + root, + Path::new("/repo/ui/docs/readme.md"), + &["ui/node_modules".into()] + )); + } +} + #[derive(Debug, Args, Clone)] pub struct TestWatchdogArgs { #[arg(long)]