From cacc42076f7c97b9211201e45474f7928beb3a04 Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Tue, 3 Sep 2024 18:21:06 -0700 Subject: [PATCH] feat: add support for .gitignore --- MODULE.bazel | 1 + WORKSPACE | 1 + deps.bzl | 7 +++++ go.mod | 5 +++- go.sum | 2 ++ vendor/modules.txt | 3 ++ walk/BUILD.bazel | 1 + walk/config.go | 74 +++++++++++++++++++++++++++++++++++++++++++++- walk/walk.go | 4 +-- 9 files changed, 94 insertions(+), 4 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 7a099697f..6debf14f4 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -42,6 +42,7 @@ use_repo( "com_github_bazelbuild_buildtools", "com_github_bmatcuk_doublestar_v4", "com_github_fsnotify_fsnotify", + "com_github_go_git_go_git_v5", "com_github_google_go_cmp", "com_github_pmezard_go_difflib", "org_golang_x_mod", diff --git a/WORKSPACE b/WORKSPACE index 9952c4918..b7bb37c40 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -101,6 +101,7 @@ register_unittest_toolchains() # gazelle:repository go_repository name=com_github_client9_misspell importpath=github.com/client9/misspell # gazelle:repository go_repository name=com_github_envoyproxy_go_control_plane importpath=github.com/envoyproxy/go-control-plane # gazelle:repository go_repository name=com_github_envoyproxy_protoc_gen_validate importpath=github.com/envoyproxy/protoc-gen-validate +# gazelle:repository go_repository name=com_github_go_git_go_git_v5 importpath=github.com/go-git/go-git/v5 # gazelle:repository go_repository name=com_github_fsnotify_fsnotify importpath=github.com/fsnotify/fsnotify # gazelle:repository go_repository name=com_github_gogo_protobuf importpath=github.com/gogo/protobuf # gazelle:repository go_repository name=com_github_golang_glog importpath=github.com/golang/glog diff --git a/deps.bzl b/deps.bzl index 5e9da6eb3..2d23768d2 100644 --- a/deps.bzl +++ b/deps.bzl @@ -187,6 +187,13 @@ def gazelle_dependencies( sum = "h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=", version = "v0.1.0", ) + _maybe( + go_repository, + name = "com_github_go_git_go_git_v5", + importpath = "github.com/go-git/go-git/v5", + sum = "h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=", + version = "v5.12.0", + ) _maybe( go_repository, name = "com_github_fsnotify_fsnotify", diff --git a/go.mod b/go.mod index 6a000afb0..d5df9cfb3 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,15 @@ module github.com/bazelbuild/bazel-gazelle -go 1.21 +go 1.21.1 + +toolchain go1.22.4 require ( github.com/bazelbuild/buildtools v0.0.0-20240313121412-66c605173954 github.com/bazelbuild/rules_go v0.46.0 github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/fsnotify/fsnotify v1.7.0 + github.com/go-git/go-git/v5 v5.12.0 github.com/google/go-cmp v0.6.0 github.com/pmezard/go-difflib v1.0.0 golang.org/x/mod v0.16.0 diff --git a/go.sum b/go.sum index 27a14993e..4f0f1460e 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= +github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/vendor/modules.txt b/vendor/modules.txt index f78bdad4a..b4c581d4c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,6 +4,7 @@ github.com/bazelbuild/buildtools/build github.com/bazelbuild/buildtools/tables # github.com/bazelbuild/rules_go v0.46.0 ## explicit; go 1.21.1 +github.com/bazelbuild/rules_go/go/runfiles github.com/bazelbuild/rules_go/go/tools/bazel github.com/bazelbuild/rules_go/go/tools/bazel_testing github.com/bazelbuild/rules_go/go/tools/internal/txtar @@ -13,6 +14,8 @@ github.com/bmatcuk/doublestar/v4 # github.com/fsnotify/fsnotify v1.7.0 ## explicit; go 1.17 github.com/fsnotify/fsnotify +# github.com/go-git/go-git/v5 v5.12.0 +## explicit; go 1.19 # github.com/google/go-cmp v0.6.0 ## explicit; go 1.13 github.com/google/go-cmp/cmp diff --git a/walk/BUILD.bazel b/walk/BUILD.bazel index 29368adb9..72a98daaf 100644 --- a/walk/BUILD.bazel +++ b/walk/BUILD.bazel @@ -13,6 +13,7 @@ go_library( "//flag", "//rule", "@com_github_bmatcuk_doublestar_v4//:doublestar", + "@com_github_go_git_go_git_v5//plumbing/format/gitignore", ], ) diff --git a/walk/config.go b/walk/config.go index 044b59a79..547ee9c8f 100644 --- a/walk/config.go +++ b/walk/config.go @@ -20,6 +20,7 @@ import ( "errors" "flag" "fmt" + "io" "io/fs" "log" "os" @@ -29,6 +30,7 @@ import ( "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/rule" "github.com/bmatcuk/doublestar/v4" + "github.com/go-git/go-git/v5/plumbing/format/gitignore" gzflag "github.com/bazelbuild/bazel-gazelle/flag" ) @@ -41,6 +43,9 @@ type walkConfig struct { excludes []string ignore bool follow []string + + gitignoreEnabled bool + gitignore *gitignoreEntry } const walkName = "_walk" @@ -56,6 +61,17 @@ func (wc *walkConfig) isExcluded(rel, base string) bool { return matchAnyGlob(wc.excludes, path.Join(rel, base)) } +func (wc *walkConfig) isGitIgnored(rel, base string) bool { + // .gitignore + if wc.gitignoreEnabled && wc.gitignore != nil { + if wc.gitignore.isIgnored(strings.Split(path.Join(rel, base), "/")) { + return true + } + } + + return false +} + func (wc *walkConfig) shouldFollow(rel, base string) bool { return matchAnyGlob(wc.follow, path.Join(rel, base)) } @@ -73,7 +89,7 @@ func (*Configurer) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) func (*Configurer) CheckFlags(fs *flag.FlagSet, c *config.Config) error { return nil } func (*Configurer) KnownDirectives() []string { - return []string{"exclude", "follow", "ignore"} + return []string{"gitignore", "exclude", "follow", "ignore"} } func (cr *Configurer) Configure(c *config.Config, rel string, f *rule.File) { @@ -99,11 +115,26 @@ func (cr *Configurer) Configure(c *config.Config, rel string, f *rule.File) { wcCopy.follow = append(wcCopy.follow, path.Join(rel, d.Value)) case "ignore": wcCopy.ignore = true + case "gitignore": + wcCopy.gitignoreEnabled = d.Value == "on" } } } c.Exts[walkName] = wcCopy + + // TODO: read the gitignore file even if gitignoring is not enabled in this dir + // because it might be enabled in a subdir. + if wcCopy.gitignoreEnabled { + // Collect gitignore style ignore files in this directory. + ignoreFilePath := path.Join(c.RepoRoot, rel, ".gitignore") + + // TODO: reuse the fs entry already created by gazelle to avoid os.Open, + // see https://github.com/bazelbuild/bazel-gazelle/pull/1737. + if ignoreReader, ignoreErr := os.Open(ignoreFilePath); ignoreErr == nil { + wcCopy.gitignore = createGitIgnorer(wcCopy.gitignore, rel, ignoreReader) + } + } } type isIgnoredFunc = func(string) bool @@ -151,6 +182,47 @@ func loadBazelIgnore(repoRoot string) (isIgnoredFunc, error) { return isIgnored, nil } +type gitignoreEntry struct { + i *gitignore.Matcher + base string + parent *gitignoreEntry +} + +func createGitIgnorer(parent *gitignoreEntry, rel string, ignoreReader io.Reader) *gitignoreEntry { + domain := []string{} + if rel != "" { + domain = strings.Split(rel, "/") + } + + matcherPatterns := make([]gitignore.Pattern, 0) + + reader := bufio.NewScanner(ignoreReader) + for reader.Scan() { + p := gitignore.ParsePattern(reader.Text(), domain) + matcherPatterns = append(matcherPatterns, p) + } + + ignore := gitignore.NewMatcher(matcherPatterns) + + return &gitignoreEntry{ + i: ignore, + base: rel, + parent: parent, + } +} + +func (g *gitignoreEntry) isIgnored(p []string) bool { + if g.i.Match(p) { + return true + } + + if g.parent == nil { + return false + } + + return g.parent.isIgnored(p) +} + func checkPathMatchPattern(pattern string) error { _, err := doublestar.Match(pattern, "x") return err diff --git a/walk/walk.go b/walk/walk.go index cfc088c98..b848b3904 100644 --- a/walk/walk.go +++ b/walk/walk.go @@ -155,14 +155,14 @@ func visit(c *config.Config, cexts []config.Configurer, isBazelIgnored isIgnored c = configure(cexts, knownDirectives, c, rel, f) wc := getWalkConfig(c) - if wc.isExcluded(rel, ".") { + if wc.isExcluded(rel, ".") || wc.isGitIgnored(rel, ".") { return } var subdirs, regularFiles []string for _, ent := range ents { base := ent.Name() - if isBazelIgnored(path.Join(rel, base)) || wc.isExcluded(rel, base) { + if isBazelIgnored(path.Join(rel, base)) || wc.isExcluded(rel, base) || wc.isGitIgnored(rel, base) { continue } ent := resolveFileInfo(wc, dir, rel, ent)