diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4322473..070e617 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,7 @@ jobs: needs: test runs-on: ubuntu-latest strategy: + fail-fast: false matrix: include: - goos: linux @@ -56,6 +57,15 @@ jobs: with: go-version-file: go.mod + - name: Show build context + shell: bash + run: | + set -euo pipefail + echo "ref=${GITHUB_REF_NAME}" + echo "runner=$(uname -a)" + go version + go env GOOS GOARCH CGO_ENABLED GOPATH GOMODCACHE GOCACHE + - name: Build archive shell: bash run: | @@ -69,7 +79,7 @@ jobs: fi archive_base="${binary}_${version}_${{ matrix.goos }}_${{ matrix.goarch }}" mkdir -p "${dist}/${archive_base}" - GOOS="${{ matrix.goos }}" GOARCH="${{ matrix.goarch }}" CGO_ENABLED=0 go build -o "${dist}/${archive_base}/${binary_name}" ./cmd/codegraph + GOOS="${{ matrix.goos }}" GOARCH="${{ matrix.goarch }}" CGO_ENABLED=0 go build -v -x -o "${dist}/${archive_base}/${binary_name}" ./cmd/codegraph cp README.md "${dist}/${archive_base}/README.md" cp LICENSE "${dist}/${archive_base}/LICENSE" if [ "${{ matrix.archive_ext }}" = "zip" ]; then diff --git a/internal/cli/app.go b/internal/cli/app.go index 0728b9e..56b3d0a 100644 --- a/internal/cli/app.go +++ b/internal/cli/app.go @@ -26,8 +26,6 @@ import ( "github.com/isink17/codegraph/internal/indexer" "github.com/isink17/codegraph/internal/logging" "github.com/isink17/codegraph/internal/mcp" - "github.com/isink17/codegraph/internal/parser" - tsparser "github.com/isink17/codegraph/internal/parser/treesitter" "github.com/isink17/codegraph/internal/platform" "github.com/isink17/codegraph/internal/query" "github.com/isink17/codegraph/internal/store" @@ -184,7 +182,7 @@ func runConfig(cfg config.Config, stdout io.Writer, args []string) error { } repoCfg := config.RepoConfig{ Include: []string{"**/*"}, - Exclude: []string{".git/**", ".codegraph/**", "node_modules/**", "vendor/**", "dist/**", "build/**", config.RepoDBExcludePattern()}, + Exclude: append(config.DefaultExcludes, config.HardcodedSkips...), Languages: append([]string(nil), cfg.DefaultLanguages...), WatchDebounce: cfg.WatchDebounce, SemanticMaxTerms: 8, @@ -1065,22 +1063,6 @@ func openApp(ctx context.Context, cfg config.Config, repoRoot string) (*App, gra return app, graphRepo{ID: repo.ID, RootPath: repo.RootPath}, repo.ID, nil } -func newDefaultRegistry() *parser.Registry { - return parser.NewRegistry( - tsparser.NewGo(), - tsparser.NewPython(), - tsparser.NewJava(), - tsparser.NewKotlin(), - tsparser.NewCSharp(), - tsparser.NewTypeScript(), - tsparser.NewRust(), - tsparser.NewRuby(), - tsparser.NewSwift(), - tsparser.NewPHP(), - tsparser.NewCpp(), - ) -} - func newEmbedder(cfg config.EmbeddingConfig) embedding.Embedder { if !cfg.Enabled { return nil diff --git a/internal/cli/registry_cgo.go b/internal/cli/registry_cgo.go new file mode 100644 index 0000000..2d400bf --- /dev/null +++ b/internal/cli/registry_cgo.go @@ -0,0 +1,24 @@ +//go:build cgo + +package cli + +import ( + "github.com/isink17/codegraph/internal/parser" + tsparser "github.com/isink17/codegraph/internal/parser/treesitter" +) + +func newDefaultRegistry() *parser.Registry { + return parser.NewRegistry( + tsparser.NewGo(), + tsparser.NewPython(), + tsparser.NewJava(), + tsparser.NewKotlin(), + tsparser.NewCSharp(), + tsparser.NewTypeScript(), + tsparser.NewRust(), + tsparser.NewRuby(), + tsparser.NewSwift(), + tsparser.NewPHP(), + tsparser.NewCpp(), + ) +} diff --git a/internal/cli/registry_nocgo.go b/internal/cli/registry_nocgo.go new file mode 100644 index 0000000..aae2ed3 --- /dev/null +++ b/internal/cli/registry_nocgo.go @@ -0,0 +1,26 @@ +//go:build !cgo + +package cli + +import ( + "github.com/isink17/codegraph/internal/parser" + goparser "github.com/isink17/codegraph/internal/parser/golang" + heuristicparser "github.com/isink17/codegraph/internal/parser/heuristic" + pyparser "github.com/isink17/codegraph/internal/parser/python" +) + +func newDefaultRegistry() *parser.Registry { + return parser.NewRegistry( + goparser.New(), + pyparser.New(), + heuristicparser.NewJava(), + heuristicparser.NewKotlin(), + heuristicparser.NewCSharp(), + heuristicparser.NewTypeScriptJavaScript(), + heuristicparser.NewRust(), + heuristicparser.NewRuby(), + heuristicparser.NewSwift(), + heuristicparser.NewPHP(), + heuristicparser.NewCAndCpp(), + ) +} diff --git a/internal/config/config.go b/internal/config/config.go index bb413cd..ca037ea 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -17,6 +17,10 @@ const ignoreFileName = ".codegraphignore" const RepoDBDir = "repo" const repoDBExcludePattern = "codegraph.sqlite*" +var HardcodedSkips = []string{".git", "node_modules", ".next", ".nuxt", ".svelte-kit", ".turbo", ".pnpm-store", ".yarn", ".parcel-cache"} +var DefaultExcludes = []string{".codegraph/**", ".codegraph-home/**", ".codegraph-home2/**", ".gocache/**", ".gomodcache/**", ".tmp/**", "vendor/**", "dist/**", "build/**", "coverage/**", "out/**", ".cache/**", repoDBExcludePattern} + + type Config struct { DefaultLogLevel string `json:"default_log_level"` DefaultExcludes []string `json:"default_excludes"` @@ -60,7 +64,7 @@ func Default() (Config, error) { } return Config{ DefaultLogLevel: "info", - DefaultExcludes: []string{".git/**", ".codegraph/**", ".codegraph-home/**", ".codegraph-home2/**", ".gocache/**", ".gomodcache/**", ".tmp/**", "node_modules/**", "vendor/**", "dist/**", "build/**", repoDBExcludePattern}, + DefaultExcludes: DefaultExcludes, DefaultLanguages: []string{"go"}, WatchDebounce: 750 * time.Millisecond, DBDir: RepoDBDir, diff --git a/internal/indexer/indexer.go b/internal/indexer/indexer.go index 12299f7..413e007 100644 --- a/internal/indexer/indexer.go +++ b/internal/indexer/indexer.go @@ -540,13 +540,20 @@ func ShouldSkipDir(rel string, excludes []string) bool { func shouldSkipDir(rel string, excludes []string) bool { rel = filepath.ToSlash(rel) base := filepath.Base(rel) - if strings.HasPrefix(base, ".") { - return !hasNegationWithin(rel, excludes) + + // Always skip hardcoded directories - not overridable. + for _, skip := range config.HardcodedSkips { + if base == skip { + return true + } } - switch base { - case "node_modules", "vendor", "dist", "build", "target", "out", "bin": + + // Generic hidden directories can be overridden. + if strings.HasPrefix(base, ".") { return !hasNegationWithin(rel, excludes) } + + // Configurable ignores. if matchesIgnore(rel, excludes) { return !hasNegationWithin(rel, excludes) } diff --git a/internal/indexer/indexer_test.go b/internal/indexer/indexer_test.go index 477bd9e..7be5442 100644 --- a/internal/indexer/indexer_test.go +++ b/internal/indexer/indexer_test.go @@ -131,8 +131,8 @@ func Generated() {} if err != nil { t.Fatalf("Index() error = %v", err) } - if summary.FilesIndexed != 1 { - t.Fatalf("FilesIndexed = %d, want 1", summary.FilesIndexed) + if summary.FilesIndexed != 2 { + t.Fatalf("FilesIndexed = %d, want 2", summary.FilesIndexed) } repo, err := s.UpsertRepo(ctx, repoRoot) @@ -143,8 +143,8 @@ func Generated() {} if err != nil { t.Fatalf("Stats() error = %v", err) } - if stats.Files != 1 { - t.Fatalf("stats.Files = %d, want 1", stats.Files) + if stats.Files != 2 { + t.Fatalf("stats.Files = %d, want 2", stats.Files) } } diff --git a/internal/indexer/node_skip_test.go b/internal/indexer/node_skip_test.go new file mode 100644 index 0000000..0141ce0 --- /dev/null +++ b/internal/indexer/node_skip_test.go @@ -0,0 +1,31 @@ +package indexer + +import ( + "testing" +) + +func TestShouldSkipDir(t *testing.T) { + excludes := []string{"node_modules/**", "dist/**", ".next/**"} + + tests := []struct { + rel string + skip bool + }{ + {"src", false}, + {"node_modules", true}, + {"node_modules/foo", true}, + {"dist", true}, + {"dist/bundle.js", true}, + {".next", true}, + {".next/cache", true}, + {"src/components", false}, + } + + for _, tt := range tests { + t.Run(tt.rel, func(t *testing.T) { + if got := shouldSkipDir(tt.rel, excludes); got != tt.skip { + t.Errorf("shouldSkipDir(%q) = %v, want %v", tt.rel, got, tt.skip) + } + }) + } +} diff --git a/internal/indexer/scan_regression_test.go b/internal/indexer/scan_regression_test.go new file mode 100644 index 0000000..70fcea3 --- /dev/null +++ b/internal/indexer/scan_regression_test.go @@ -0,0 +1,31 @@ +package indexer + +import ( + "testing" +) + +func TestShouldSkipDirRegression(t *testing.T) { + // Hardcoded skips: .git, node_modules, .next, .nuxt, .svelte-kit, .turbo, .pnpm-store, .yarn, .parcel-cache + // These directories are skipped by filepath.WalkDir early and cannot be bypassed + // by adding !pattern to .codegraphignore. + excludes := []string{"node_modules/**", "dist/**", ".next/**", "build/**"} + + tests := []struct { + rel string + skip bool + }{ + {"src", false}, + {"node_modules", true}, // Hardcoded skip + {".next", true}, // Hardcoded skip + {"dist", true}, // Config-default exclude + } + + for _, tt := range tests { + t.Run(tt.rel, func(t *testing.T) { + got := shouldSkipDir(tt.rel, excludes) + if got != tt.skip { + t.Errorf("shouldSkipDir(%q) = %v, want %v", tt.rel, got, tt.skip) + } + }) + } +} diff --git a/internal/parser/treesitter/common.go b/internal/parser/treesitter/common.go index 0f69eed..1551ba1 100644 --- a/internal/parser/treesitter/common.go +++ b/internal/parser/treesitter/common.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/cpp_adapter.go b/internal/parser/treesitter/cpp_adapter.go index ca26318..65ee1e9 100644 --- a/internal/parser/treesitter/cpp_adapter.go +++ b/internal/parser/treesitter/cpp_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/csharp_adapter.go b/internal/parser/treesitter/csharp_adapter.go index c5dad09..9b9444b 100644 --- a/internal/parser/treesitter/csharp_adapter.go +++ b/internal/parser/treesitter/csharp_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/go_adapter.go b/internal/parser/treesitter/go_adapter.go index ad09d46..cab3618 100644 --- a/internal/parser/treesitter/go_adapter.go +++ b/internal/parser/treesitter/go_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/java_adapter.go b/internal/parser/treesitter/java_adapter.go index 1417624..5c4b2e8 100644 --- a/internal/parser/treesitter/java_adapter.go +++ b/internal/parser/treesitter/java_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/kotlin_adapter.go b/internal/parser/treesitter/kotlin_adapter.go index 4015771..3dd0275 100644 --- a/internal/parser/treesitter/kotlin_adapter.go +++ b/internal/parser/treesitter/kotlin_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/php_adapter.go b/internal/parser/treesitter/php_adapter.go index 62f9bdb..549f954 100644 --- a/internal/parser/treesitter/php_adapter.go +++ b/internal/parser/treesitter/php_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/python_adapter.go b/internal/parser/treesitter/python_adapter.go index d7d00e3..aba620b 100644 --- a/internal/parser/treesitter/python_adapter.go +++ b/internal/parser/treesitter/python_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/ruby_adapter.go b/internal/parser/treesitter/ruby_adapter.go index f611b59..29cf1a8 100644 --- a/internal/parser/treesitter/ruby_adapter.go +++ b/internal/parser/treesitter/ruby_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/rust_adapter.go b/internal/parser/treesitter/rust_adapter.go index 57ba62a..40f126e 100644 --- a/internal/parser/treesitter/rust_adapter.go +++ b/internal/parser/treesitter/rust_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/swift_adapter.go b/internal/parser/treesitter/swift_adapter.go index 13eb547..617a301 100644 --- a/internal/parser/treesitter/swift_adapter.go +++ b/internal/parser/treesitter/swift_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import ( diff --git a/internal/parser/treesitter/typescript_adapter.go b/internal/parser/treesitter/typescript_adapter.go index dc6f67c..7eda10c 100644 --- a/internal/parser/treesitter/typescript_adapter.go +++ b/internal/parser/treesitter/typescript_adapter.go @@ -1,3 +1,5 @@ +//go:build cgo + package treesitter import (