-
Notifications
You must be signed in to change notification settings - Fork 38
rules_buf generates invalid targets from hidden directories #133
Description
Bug
The getProtoImportPaths function in gazelle/buf/generate.go uses fs.WalkDir to discover .proto files under the module root, but does not skip hidden directories (names starting with .). This causes it to discover proto files in directories like .git/, .claude/, or other tool-managed hidden directories, generating buf_breaking_test targets that reference nonexistent packages and break bazel build //....
Gazelle's own directory walker skips hidden directories by default, so the buf extension's behavior is inconsistent with the rest of Gazelle.
Repro
- Have a
buf.yamlv2 config withmodules: [{path: "."}]and# gazelle:buf_breaking_againstset to module mode - Have any hidden directory under the workspace root that contains
.protofiles (e.g..claude/worktrees/from Claude Code, or any git worktree under a dotdir) - Run
bazel run //:gazelle
The generated buf_breaking_test in the root BUILD.bazel will include targets like:
buf_breaking_test(
name = "buf_breaking",
targets = [
# ... legitimate targets ...
"@buf_deps//.claude/worktrees/some-worktree/apps/my-app/proto:proto_proto",
# ... hundreds more from each worktree ...
],
)These @buf_deps// targets don't exist (the @buf_deps workspace mirror correctly skips dotdirs), so bazel build //... fails.
Root cause
func getProtoImportPaths(config *Config, moduleRoot string) []string {
var targets []string
fs.WalkDir(
os.DirFS(moduleRoot),
".",
func(path string, dirEntry fs.DirEntry, err error) error {
if dirEntry.IsDir() {
return nil // enters ALL directories, including hidden ones
}
// ...
},
)
return targets
}When dirEntry.IsDir() is true, the function returns nil (continue walking) instead of returning fs.SkipDir for hidden directories.
Additionally, the function does not respect the includes field from buf.yaml v2 module config — it walks the entire module root regardless. The excludes field cannot be used as a workaround because buf v2 validates that exclude paths must be within include paths.
Suggested fix
func(path string, dirEntry fs.DirEntry, err error) error {
if dirEntry.IsDir() {
// Skip hidden directories (e.g. .git, .claude) to match
// Gazelle's own directory-walking behavior.
if path != "." && strings.HasPrefix(dirEntry.Name(), ".") {
return fs.SkipDir
}
return nil
}A more complete fix would also filter by includes when present in the v2 module config, but skipping hidden directories addresses the most common case and matches Gazelle's conventions.
Workaround
We're currently applying this as a patch via archive_override in MODULE.bazel:
archive_override(
module_name = "rules_buf",
patch_strip = 1,
patches = ["//third-party/patches:rules_buf_skip_hidden_dirs.patch"],
# ...
)Environment
- rules_buf 0.5.2
- Bazel 7.x with bzlmod
buf.yamlv2 with module mode breaking checks