diff --git a/README.md b/README.md index 12f1fafe..eec8daa2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # scafld [![CI](https://github.com/nilstate/scafld/actions/workflows/ci.yml/badge.svg)](https://github.com/nilstate/scafld/actions/workflows/ci.yml) -[![Go](https://img.shields.io/github/go-mod/go-version/nilstate/scafld?label=go)](go.mod) +[![Go Report Card](https://goreportcard.com/badge/github.com/nilstate/scafld/v2)](https://goreportcard.com/report/github.com/nilstate/scafld/v2) [![Go Reference](https://pkg.go.dev/badge/github.com/nilstate/scafld/v2.svg)](https://pkg.go.dev/github.com/nilstate/scafld/v2) [![License](https://img.shields.io/github/license/nilstate/scafld)](LICENSE) diff --git a/internal/core/workspace/snapshot.go b/internal/core/workspace/snapshot.go index 681cd77f..98e68b83 100644 --- a/internal/core/workspace/snapshot.go +++ b/internal/core/workspace/snapshot.go @@ -153,9 +153,15 @@ func NormalizeScope(scope []string) []string { } // PathInScope reports whether path is equal to or under one of the scope prefixes. +// A "." prefix denotes the workspace root and matches every path, mirroring the +// git adapter's scope handling. func PathInScope(path string, scope []string) bool { - candidate := strings.Trim(strings.ReplaceAll(strings.TrimSpace(path), "\\", "/"), "/") + candidate := strings.TrimPrefix(strings.TrimSpace(strings.ReplaceAll(path, "\\", "/")), "./") + candidate = strings.Trim(candidate, "/") for _, prefix := range scope { + if prefix == "." { + return true + } if candidate == prefix || strings.HasPrefix(candidate, prefix+"/") { return true } diff --git a/internal/core/workspace/snapshot_test.go b/internal/core/workspace/snapshot_test.go index 71f88e15..7dbdfb0b 100644 --- a/internal/core/workspace/snapshot_test.go +++ b/internal/core/workspace/snapshot_test.go @@ -39,3 +39,27 @@ func TestFilterAndPartitionUsePathPrefixes(t *testing.T) { t.Fatalf("inside=%+v outside=%+v", inside, outside) } } + +func TestPathInScopeRootScopeMatchesEveryPath(t *testing.T) { + scope := NormalizeScope([]string{"."}) + for _, path := range []string{"cmd/main.go", "internal/core/workspace/snapshot.go", "README.md"} { + if !PathInScope(path, scope) { + t.Fatalf("PathInScope(%q, %q) = false, want true: a \".\" scope denotes the workspace root", path, scope) + } + } +} + +func TestFilterRootScopeKeepsEveryEntry(t *testing.T) { + snapshot := []string{"M cmd/main.go", "A internal/core/x.go"} + filtered := Filter(snapshot, NormalizeScope([]string{"."})) + if len(filtered) != len(snapshot) { + t.Fatalf("Filter(%q, [\".\"]) = %q, want every entry retained", snapshot, filtered) + } +} + +func TestPathInScopeIgnoresLeadingDotSlashOnCandidate(t *testing.T) { + scope := NormalizeScope([]string{"./cmd"}) + if !PathInScope("./cmd/main.go", scope) { + t.Fatalf("PathInScope(\"./cmd/main.go\", %q) = false, want true: candidate normalization must match NormalizeScope", scope) + } +}