diff --git a/.codacy.yml b/.codacy.yml index b4a3c73..296c973 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -2,8 +2,8 @@ # Codacy configuration for starlet. # # Exclude the vendored, pinned third-party JSON-repair runtime from static -# analysis. lib/json/internal/jsonrepair is a verbatim copy of +# analysis. internal/jsonrepair is a verbatim copy of # kaptinlin/jsonrepair v0.2.2 (MIT); its style/complexity is upstream's to # own, and the copy is kept byte-for-byte so it can be re-vendored cleanly. exclude_paths: - - 'lib/json/internal/jsonrepair/**' + - 'internal/jsonrepair/**' diff --git a/CLAUDE.md b/CLAUDE.md index c1a0101..6414798 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,7 +37,7 @@ A new module must satisfy **all three**, otherwise it goes to `starpkg/*`: 2. **Universally needed.** Broad, domain-neutral utility. Domain modules (sqlite, web, llm, mq, s3…) are starpkg's job no matter how clean they are. 3. **Zero third-party dependencies.** Stdlib-only (or an extension of an existing core module). Any `go.sum` entry is inherited by every downstream — one third-party requirement sends the module to starpkg. -**The vendoring exception** (the `lib/json/internal/jsonrepair` precedent): a frozen, **same-license (MIT)**, **stdlib-only** third-party runtime may be vendored under `lib//internal//` to keep `go.sum` clean, when the capability is judged worth it. Requirements: pin to a specific upstream release (record it), copy runtime `.go` files only (no `_test.go`, no upstream test deps), keep the upstream LICENSE in the directory, add a `doc.go` stating provenance + "do not edit by hand; re-vendor to update", golden-lock the observed behavior in our tests, and exclude the path in `codecov.yml` and `.codacy.yml`. Measure the binary delta in the go1.19 container before committing to it. +**The vendoring exception** (the `internal/jsonrepair` precedent): a frozen, **same-license (MIT)**, **stdlib-only** third-party runtime may be vendored under the top-level `internal//` — all vendored third-party code lives under the single `internal/` so it is easy to find and license-audit in one place — to keep `go.sum` clean, when the capability is judged worth it. Requirements: pin to a specific upstream release (record it), copy runtime `.go` files only (no `_test.go`, no upstream test deps), keep the upstream LICENSE in the directory, add a `doc.go` stating provenance + "do not edit by hand; re-vendor to update", golden-lock the observed behavior in our tests, and exclude the path in `codecov.yml` and `.codacy.yml`. Measure the binary delta in the go1.19 container before committing to it. **License hygiene caps vendoring.** Never vendor differently-licensed source — even permissive (Apache-2.0) — into this MIT repository: the copied files keep their license and the repo becomes mixed-license. For a capability worth a differently-licensed library, use a **module dependency** instead, and only when it passes the evaluation bar: its go.mod must not exceed this repo's Go floor, it should bring zero (or near-zero) transitive dependencies into `go.sum`, the binary delta is measured in the go1.19 container, and its panic surface is audited + hostile-input tested (the `lib/json` jsonschema decision: Apache-2.0, go1.19 exactly, zero requires, +256 KiB measured → module dep, repo stays pure MIT). diff --git a/codecov.yml b/codecov.yml index b2d7220..e2e9782 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,10 +1,10 @@ # Codecov configuration for starlet. # # Exclude the vendored, pinned third-party JSON-repair runtime from coverage. -# lib/json/internal/jsonrepair is a verbatim copy of kaptinlin/jsonrepair +# internal/jsonrepair is a verbatim copy of kaptinlin/jsonrepair # v0.2.2 (MIT), frozen at its go1.18/1.19-compatible release. It is exercised # through the json.repair tests, but mirroring its upstream branch coverage is # not a gate this repository maintains. ignore: - - "lib/json/internal/jsonrepair" - - "lib/json/internal/jsonrepair/**/*" + - "internal/jsonrepair" + - "internal/jsonrepair/**/*" diff --git a/doc_coverage_test.go b/doc_coverage_test.go deleted file mode 100644 index 76ad7c2..0000000 --- a/doc_coverage_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package starlet_test - -import ( - "os" - "path/filepath" - "sort" - "strings" - "testing" - - "github.com/1set/starlet" - "go.starlark.net/starlarkstruct" -) - -// libReadmeDir maps a builtin module name to its lib/ documentation -// directory. The only non-identity case is go_idiomatic -> goidiomatic; -// modules backed by go.starlark.net (math, struct, time) have no lib README -// and are skipped below. -func libReadmeDir(module string) string { - return strings.ReplaceAll(module, "_", "") -} - -// moduleSurface enumerates the script-visible names a module exports, across -// the three registration shapes: a starlarkstruct.Module (its Members), a -// starlarkstruct.Struct (its AttrNames), or a flat StringDict (its keys). -func moduleSurface(t *testing.T, name string) []string { - loader := starlet.GetBuiltinModule(name) - if loader == nil { - return nil - } - sd, err := loader() - if err != nil { - t.Fatalf("load module %q: %v", name, err) - } - var out []string - for k, v := range sd { - switch m := v.(type) { - case *starlarkstruct.Module: - for mk := range m.Members { - out = append(out, mk) - } - case *starlarkstruct.Struct: - out = append(out, m.AttrNames()...) - default: - out = append(out, k) - } - } - sort.Strings(out) - return out -} - -// TestDocCoverage asserts that every script-visible member of every lib/* -// module is documented in that module's README. The matching logic lives in -// tools/doccov/coverage.star and runs through a starlet Machine, so the check -// dogfoods the regex module. -func TestDocCoverage(t *testing.T) { - script, err := os.ReadFile(filepath.Join("tools", "doccov", "coverage.star")) - if err != nil { - t.Fatalf("read coverage script: %v", err) - } - - surface := map[string]interface{}{} - docs := map[string]interface{}{} - var skipped []string - for _, name := range starlet.GetAllBuiltinModuleNames() { - readme, err := os.ReadFile(filepath.Join("lib", libReadmeDir(name), "README.md")) - if err != nil { - skipped = append(skipped, name) // external module without a lib README - continue - } - docs[name] = string(readme) - names := moduleSurface(t, name) - members := make([]interface{}, len(names)) - for i, n := range names { - members[i] = n - } - surface[name] = members - } - - m := starlet.NewWithNames(starlet.StringAnyMap{"surface": surface, "docs": docs}, nil, []string{"regex"}) - m.SetScriptContent(script) - out, err := m.Run() - if err != nil { - t.Fatalf("doc coverage script failed: %v", err) - } - if report, ok := out["report"].(string); ok { - t.Log("\n" + report) - } - sort.Strings(skipped) - t.Logf("skipped (go.starlark.net modules, no lib README): %v", skipped) - - if missing, ok := out["missing"].([]interface{}); ok && len(missing) > 0 { - t.Errorf("%d module member(s) are not documented in their README — see the report above", len(missing)) - } -} diff --git a/lib/json/internal/jsonrepair/LICENSE b/internal/jsonrepair/LICENSE similarity index 100% rename from lib/json/internal/jsonrepair/LICENSE rename to internal/jsonrepair/LICENSE diff --git a/lib/json/internal/jsonrepair/const.go b/internal/jsonrepair/const.go similarity index 100% rename from lib/json/internal/jsonrepair/const.go rename to internal/jsonrepair/const.go diff --git a/lib/json/internal/jsonrepair/doc.go b/internal/jsonrepair/doc.go similarity index 100% rename from lib/json/internal/jsonrepair/doc.go rename to internal/jsonrepair/doc.go diff --git a/lib/json/internal/jsonrepair/errors.go b/internal/jsonrepair/errors.go similarity index 100% rename from lib/json/internal/jsonrepair/errors.go rename to internal/jsonrepair/errors.go diff --git a/lib/json/internal/jsonrepair/jsonrepair.go b/internal/jsonrepair/jsonrepair.go similarity index 100% rename from lib/json/internal/jsonrepair/jsonrepair.go rename to internal/jsonrepair/jsonrepair.go diff --git a/lib/json/internal/jsonrepair/utils.go b/internal/jsonrepair/utils.go similarity index 100% rename from lib/json/internal/jsonrepair/utils.go rename to internal/jsonrepair/utils.go diff --git a/lib/json/repair.go b/lib/json/repair.go index dac5678..cee3061 100644 --- a/lib/json/repair.go +++ b/lib/json/repair.go @@ -6,7 +6,7 @@ import ( "regexp" "strings" - "github.com/1set/starlet/lib/json/internal/jsonrepair" + "github.com/1set/starlet/internal/jsonrepair" "go.starlark.net/starlark" ) diff --git a/module_test.go b/module_test.go index 98da688..c7bbff4 100644 --- a/module_test.go +++ b/module_test.go @@ -4,7 +4,9 @@ import ( "io" "io/fs" "os" + "path/filepath" "reflect" + "sort" "strings" "testing" @@ -1360,3 +1362,87 @@ func TestGetBuiltinModuleNamesWithoutCapabilities(t *testing.T) { } } } + +// --- documentation coverage (folded in from the former doc_coverage_test.go) --- + +// libReadmeDir maps a builtin module name to its lib/ documentation +// directory. The only non-identity case is go_idiomatic -> goidiomatic; +// modules backed by go.starlark.net (math, struct, time) have no lib README +// and are skipped below. +func libReadmeDir(module string) string { + return strings.ReplaceAll(module, "_", "") +} + +// moduleSurface enumerates the script-visible names a module exports, across +// the three registration shapes: a starlarkstruct.Module (its Members), a +// starlarkstruct.Struct (its AttrNames), or a flat StringDict (its keys). +func moduleSurface(t *testing.T, name string) []string { + loader := starlet.GetBuiltinModule(name) + if loader == nil { + return nil + } + sd, err := loader() + if err != nil { + t.Fatalf("load module %q: %v", name, err) + } + var out []string + for k, v := range sd { + switch m := v.(type) { + case *starlarkstruct.Module: + for mk := range m.Members { + out = append(out, mk) + } + case *starlarkstruct.Struct: + out = append(out, m.AttrNames()...) + default: + out = append(out, k) + } + } + sort.Strings(out) + return out +} + +// TestDocCoverage asserts that every script-visible member of every lib/* +// module is documented in that module's README. The matching logic lives in +// tools/doccov/coverage.star and runs through a starlet Machine, so the check +// dogfoods the regex module. +func TestDocCoverage(t *testing.T) { + script, err := os.ReadFile(filepath.Join("tools", "doccov", "coverage.star")) + if err != nil { + t.Fatalf("read coverage script: %v", err) + } + + surface := map[string]interface{}{} + docs := map[string]interface{}{} + var skipped []string + for _, name := range starlet.GetAllBuiltinModuleNames() { + readme, err := os.ReadFile(filepath.Join("lib", libReadmeDir(name), "README.md")) + if err != nil { + skipped = append(skipped, name) // external module without a lib README + continue + } + docs[name] = string(readme) + names := moduleSurface(t, name) + members := make([]interface{}, len(names)) + for i, n := range names { + members[i] = n + } + surface[name] = members + } + + m := starlet.NewWithNames(starlet.StringAnyMap{"surface": surface, "docs": docs}, nil, []string{"regex"}) + m.SetScriptContent(script) + out, err := m.Run() + if err != nil { + t.Fatalf("doc coverage script failed: %v", err) + } + if report, ok := out["report"].(string); ok { + t.Log("\n" + report) + } + sort.Strings(skipped) + t.Logf("skipped (go.starlark.net modules, no lib README): %v", skipped) + + if missing, ok := out["missing"].([]interface{}); ok && len(missing) > 0 { + t.Errorf("%d module member(s) are not documented in their README — see the report above", len(missing)) + } +}