Skip to content

Support glob patterns in the exclude config#901

Open
alexcrocha wants to merge 4 commits into
mainfrom
ar-glob-pattern-config-support
Open

Support glob patterns in the exclude config#901
alexcrocha wants to merge 4 commits into
mainfrom
ar-glob-pattern-config-support

Conversation

@alexcrocha

Copy link
Copy Markdown
Contributor

This PR lets exclude entries be glob patterns (e.g. **/fixtures), not just literal paths. Globs are matched during file discovery.

Excludes are canonicalized to resolve symlinks, but a glob is not a real path and cannot be canonicalized. So canonicalization failure is what tells the two apart: an entry that canonicalizes is concrete and matched literally, one that fails is used as a glob.

@alexcrocha alexcrocha marked this pull request as ready for review July 2, 2026 23:46
@alexcrocha alexcrocha requested a review from a team as a code owner July 2, 2026 23:46
@alexcrocha alexcrocha self-assigned this Jul 2, 2026
Comment thread rust/rubydex/src/listing.rs Outdated
context.touch(&nested);

let mut excluded = HashSet::new();
excluded.insert(context.absolute_path_to("**/fixtures"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this insert a pattern or a path 🤔

And should we have 2 tests exercising both branches of

Ok(canonical) => Pattern::escape(&canonical.to_string_lossy()),
                    Err(_) => path.to_string_lossy().into_owned(),

@alexcrocha alexcrocha Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this insert a pattern or a path 🤔

Yeah, it was ambiguous, the type was lying to us. Exclude entries can be glob patterns so they are Box<str> now instead of PathBuf.

should we have 2 tests exercising both branches

I changed the logic so this is a bit different now, but both branches were covered. The Err branch was the only one needing a test, as the Ok branch was the pre-existing behaviour.

@st0012 st0012 added the enhancement New feature or request label Jul 3, 2026
@alexcrocha alexcrocha force-pushed the ar-glob-pattern-config-support branch 2 times, most recently from 488ee40 to 39db7c5 Compare July 4, 2026 01:41
Exclude entries must be able to hold glob patterns, which are not
filesystem paths
Representing exclusions as patterns allows us to support exclusion
matching with globs without reworking the traversal or the match check.
A glob is recognized by its metacharacters and kept as a pattern, while
concrete paths canonicalize and match literally
@alexcrocha alexcrocha force-pushed the ar-glob-pattern-config-support branch from 39db7c5 to 6e96728 Compare July 4, 2026 01:51
self.workspace_path
.join(&**entry)
.to_string_lossy()
.replace(std::path::MAIN_SEPARATOR, "/")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we substituting the separator here? Note that this PR changes excluded_paths into excluded_glob_patterns. For file paths, backslashes are valid (on Windows), but not for glob patterns (which always use forward slashes).

if entry.contains(['*', '?', '[']) {
Pattern::new(entry).ok()
} else {
let canonical = fs::canonicalize(entry).ok()?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you find that treating all entries as glob patterns was too slow? Do we need the two separate paths?

Also, I think there's potential for this to be slightly confusing. Consider this example:

project_path/
  my_symlink -> some_other_path

Trying to exclude the same directory using a pattern vs a path results in different behaviour:

# rubydex.toml

# This config path gets canonicalized to `some_other_path`, which then
# gets excluded since we also canonicalize the paths being compared
exclude = ["my_symlink"]

# This pattern does not get canonicalized since you cannot do it for a glob
# pattern, which means we compare if `some_other_path` matches `**/my_symlink`.
#
# It does not and so we fail to exclude
exclude = ["**/my_symlink/"]

I would vote for us to do the following:

  1. Stop following symlinks (both in the config and in the listing). If someone created a symlink in their project, I would assume they'd prefer dealing with the symlink they created rather than the destination path
  2. Treat everything as glob patterns

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants