Directory walkers pruned .git/vendor/node_modules but not .worktrees/, so a repo scan descended into nested git worktrees (e.g. an orchestrator worktree under cms/.worktrees/). Their Go/TS files surfaced as false positives in the standalone-plugin import check and noise in the any-usage warnings. Add ".worktrees" to the skip set across the implicated and common walkers: proto RBAC proto-scan, standalone-plugin imports, frontend extras, go-lint, sqlc-uuid, presets.json, and plugin segmentation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
178 lines
3.8 KiB
Go
178 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type sqlcUUIDViolation struct {
|
|
file string
|
|
rule string
|
|
detail string
|
|
}
|
|
|
|
type sqlcConfigFile struct {
|
|
SQL []sqlcConfigEntry `yaml:"sql"`
|
|
}
|
|
|
|
type sqlcConfigEntry struct {
|
|
Gen sqlcGenConfig `yaml:"gen"`
|
|
}
|
|
|
|
type sqlcGenConfig struct {
|
|
Go sqlcGoConfig `yaml:"go"`
|
|
}
|
|
|
|
type sqlcGoConfig struct {
|
|
Overrides []sqlcOverride `yaml:"overrides"`
|
|
}
|
|
|
|
type sqlcOverride struct {
|
|
DBType string `yaml:"db_type"`
|
|
Nullable bool `yaml:"nullable"`
|
|
GoType sqlcGoType `yaml:"go_type"`
|
|
}
|
|
|
|
type sqlcGoType struct {
|
|
Scalar string
|
|
Import string
|
|
Type string
|
|
}
|
|
|
|
func (g *sqlcGoType) UnmarshalYAML(node *yaml.Node) error {
|
|
switch node.Kind {
|
|
case yaml.ScalarNode:
|
|
return node.Decode(&g.Scalar)
|
|
case yaml.MappingNode:
|
|
var decoded struct {
|
|
Import string `yaml:"import"`
|
|
Type string `yaml:"type"`
|
|
}
|
|
if err := node.Decode(&decoded); err != nil {
|
|
return err
|
|
}
|
|
g.Import = decoded.Import
|
|
g.Type = decoded.Type
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("unsupported go_type yaml node kind %d", node.Kind)
|
|
}
|
|
}
|
|
|
|
func (g sqlcGoType) matchesGoogleUUID(nullable bool) bool {
|
|
if nullable {
|
|
return g.Scalar == "github.com/google/uuid.NullUUID" ||
|
|
(g.Import == "github.com/google/uuid" && g.Type == "NullUUID")
|
|
}
|
|
return g.Scalar == "github.com/google/uuid.UUID" ||
|
|
(g.Import == "github.com/google/uuid" && g.Type == "UUID")
|
|
}
|
|
|
|
func findSQLCConfigFiles(root string, includeRoot bool) []string {
|
|
var files []string
|
|
|
|
_ = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if info.IsDir() {
|
|
switch info.Name() {
|
|
case ".git", ".worktrees", "vendor", "node_modules", "dist":
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
base := filepath.Base(path)
|
|
if base != "sqlc.yaml" && base != "sqlc.yml" {
|
|
return nil
|
|
}
|
|
|
|
if includeRoot && samePath(path, filepath.Join(root, base)) {
|
|
files = append(files, path)
|
|
return nil
|
|
}
|
|
|
|
rel, relErr := filepath.Rel(root, path)
|
|
if relErr != nil {
|
|
return nil
|
|
}
|
|
if strings.Contains(filepath.ToSlash(rel), "/cmd/") || strings.HasPrefix(filepath.ToSlash(rel), "cmd/") {
|
|
files = append(files, path)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
sort.Strings(files)
|
|
return files
|
|
}
|
|
|
|
func checkSQLCUUIDOverrides(root string, includeRoot bool) []sqlcUUIDViolation {
|
|
var violations []sqlcUUIDViolation
|
|
|
|
for _, path := range findSQLCConfigFiles(root, includeRoot) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
violations = append(violations, sqlcUUIDViolation{
|
|
file: displayLintPath(root, path),
|
|
rule: "sqlc-read",
|
|
detail: fmt.Sprintf("failed to read sqlc config: %v", err),
|
|
})
|
|
continue
|
|
}
|
|
|
|
var cfg sqlcConfigFile
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
violations = append(violations, sqlcUUIDViolation{
|
|
file: displayLintPath(root, path),
|
|
rule: "sqlc-parse",
|
|
detail: fmt.Sprintf("failed to parse sqlc config: %v", err),
|
|
})
|
|
continue
|
|
}
|
|
|
|
hasUUID := false
|
|
hasNullUUID := false
|
|
for _, block := range cfg.SQL {
|
|
for _, override := range block.Gen.Go.Overrides {
|
|
if override.DBType != "uuid" {
|
|
continue
|
|
}
|
|
if override.Nullable {
|
|
if override.GoType.matchesGoogleUUID(true) {
|
|
hasNullUUID = true
|
|
}
|
|
continue
|
|
}
|
|
if override.GoType.matchesGoogleUUID(false) {
|
|
hasUUID = true
|
|
}
|
|
}
|
|
}
|
|
|
|
relPath := displayLintPath(root, path)
|
|
if !hasUUID {
|
|
violations = append(violations, sqlcUUIDViolation{
|
|
file: relPath,
|
|
rule: "sqlc-uuid-type",
|
|
detail: `db_type "uuid" must map to github.com/google/uuid.UUID`,
|
|
})
|
|
}
|
|
if !hasNullUUID {
|
|
violations = append(violations, sqlcUUIDViolation{
|
|
file: relPath,
|
|
rule: "sqlc-null-uuid-type",
|
|
detail: `nullable db_type "uuid" must map to github.com/google/uuid.NullUUID`,
|
|
})
|
|
}
|
|
}
|
|
|
|
return violations
|
|
}
|