Skip to content

Directory-argument walk silently skips hidden dot-directories (.buildkite/, .github/, etc.) #947

Description

@dduugg

Description

rubyfmt --check . (and -i ., or bare .) silently skips .rb files that live under a hidden dot-directory, even when that directory is tracked in git and not excluded by any .gitignore or .rubyfmtignore pattern. Passing an explicit file path is unaffected and finds the same file just fine.

This is surprising for repos that keep Ruby glue code under dot-directories — the two most common cases are Buildkite pipeline definitions under .buildkite/*.rb and GitHub Actions helpers under .github/*.rb. A CI step that runs rubyfmt --check . believing it covers the whole repo is actually silently skipping those files.

Repro

$ mkdir -p repo/.hidden repo/visible && cd repo
$ printf 'x    =1\n' > .hidden/bad.rb
$ printf 'x    =1\n' > visible/bad.rb
$ rubyfmt --check .
--- ./visible/bad.rb
+++ ./visible/bad.rb
@@ -1 +1 @@
-x    =1
+x = 1
$ echo $?
5

Only visible/bad.rb is reported. .hidden/bad.rb has the exact same formatting problem but is never mentioned, and the exit code would be 0 if that were the only offending file.

Explicit-path invocation finds it fine:

$ rubyfmt --check .hidden/bad.rb
--- .hidden/bad.rb
+++ .hidden/bad.rb
@@ -1 +1 @@
-x    =1
+x = 1

Root cause

file_walker_builder in src/main.rs constructs an ignore::WalkBuilder for directory arguments but never calls .hidden(false). The ignore crate's WalkBuilder ignores hidden files by default, independently of .gitignore/.rubyfmtignore handling, so any dot-directory is skipped during the walk regardless of what those ignore files say.

Explicit file-path arguments go through a different path (Path::is_file() + direct read()), which never touches the walker, so they aren't affected — that's why this is easy to miss in day-to-day use and only shows up as "CI didn't catch something it should have."

Proposed fix

I have a fix + regression tests ready and will open a PR shortly: un-hide dotfiles in the walker (builder.hidden(false)) and add an explicit filter_entry to keep .git excluded (since un-hiding would otherwise walk into it too, which is neither useful nor free).

Confirmed this doesn't regress .gitignore/.rubyfmtignore handling for hidden or non-hidden paths — those are enforced independently of the hidden-file setting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions