Give each document command one postgres transaction and only let table owners unwrap the scope, so cross-owner writes can roll back together. Co-authored-by: Cursor <cursoragent@cursor.com>
158 lines
3.6 KiB
Go
158 lines
3.6 KiB
Go
package archtest
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
const (
|
|
KindImport = "import"
|
|
KindSQLWrite = "sql_write"
|
|
KindPath = "path"
|
|
KindEvent = "event"
|
|
)
|
|
|
|
type Boundaries struct {
|
|
Imports map[string][]string `yaml:"imports"`
|
|
TableOwners map[string][]string `yaml:"table_owners"`
|
|
PathOwners map[string][]string `yaml:"path_owners"`
|
|
EventProducers map[string][]string `yaml:"event_producers"`
|
|
EventConsumers map[string][]string `yaml:"event_consumers"`
|
|
UnwrapOwners []string `yaml:"unwrap_owners"`
|
|
Exceptions []Exception `yaml:"exceptions"`
|
|
BaselineCounts map[string]int `yaml:"baseline_counts"`
|
|
}
|
|
|
|
type Exception struct {
|
|
Kind string `yaml:"kind"`
|
|
Package string `yaml:"package"`
|
|
File string `yaml:"file"`
|
|
Symbol string `yaml:"symbol"`
|
|
Owner string `yaml:"owner"`
|
|
Reason string `yaml:"reason"`
|
|
Milestone string `yaml:"milestone"`
|
|
Expires string `yaml:"expires"`
|
|
}
|
|
|
|
type Finding struct {
|
|
Kind string
|
|
Package string
|
|
File string
|
|
Symbol string
|
|
Detail string
|
|
}
|
|
|
|
func (f Finding) Key() string {
|
|
return f.Kind + "|" + f.Package + "|" + filepath.ToSlash(f.File) + "|" + f.Symbol
|
|
}
|
|
|
|
func ModuleRoot() (string, error) {
|
|
_, file, _, ok := runtime.Caller(0)
|
|
if !ok {
|
|
return "", fmt.Errorf("archtest: cannot locate caller")
|
|
}
|
|
dir := filepath.Dir(file)
|
|
for {
|
|
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
|
|
return dir, nil
|
|
}
|
|
parent := filepath.Dir(dir)
|
|
if parent == dir {
|
|
return "", fmt.Errorf("archtest: go.mod not found")
|
|
}
|
|
dir = parent
|
|
}
|
|
}
|
|
|
|
func LoadBoundaries(path string) (*Boundaries, error) {
|
|
b, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out Boundaries
|
|
if err := yaml.Unmarshal(b, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
if out.Imports == nil {
|
|
out.Imports = map[string][]string{}
|
|
}
|
|
if out.TableOwners == nil {
|
|
out.TableOwners = map[string][]string{}
|
|
}
|
|
if out.PathOwners == nil {
|
|
out.PathOwners = map[string][]string{}
|
|
}
|
|
if out.EventProducers == nil {
|
|
out.EventProducers = map[string][]string{}
|
|
}
|
|
if out.EventConsumers == nil {
|
|
out.EventConsumers = map[string][]string{}
|
|
}
|
|
if out.UnwrapOwners == nil {
|
|
out.UnwrapOwners = []string{}
|
|
}
|
|
if out.BaselineCounts == nil {
|
|
out.BaselineCounts = map[string]int{}
|
|
}
|
|
return &out, nil
|
|
}
|
|
|
|
func DefaultBoundariesPath(root string) string {
|
|
return filepath.Join(root, ".doc", "system-design", "module-boundaries.yaml")
|
|
}
|
|
|
|
func allowedHas(list []string, needle string) bool {
|
|
for _, p := range list {
|
|
if strings.TrimSpace(p) == needle {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (e Exception) validate(now time.Time) error {
|
|
if strings.TrimSpace(e.Owner) == "" {
|
|
return fmt.Errorf("missing owner")
|
|
}
|
|
if strings.TrimSpace(e.Reason) == "" {
|
|
return fmt.Errorf("missing reason")
|
|
}
|
|
if strings.TrimSpace(e.Milestone) == "" {
|
|
return fmt.Errorf("missing milestone")
|
|
}
|
|
if strings.TrimSpace(e.Expires) == "" {
|
|
return fmt.Errorf("missing expires")
|
|
}
|
|
day, err := time.ParseInLocation("2006-01-02", e.Expires, time.UTC)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid expires %q", e.Expires)
|
|
}
|
|
// expires is inclusive through that calendar day (UTC).
|
|
if !now.UTC().Before(day.Add(24 * time.Hour)) {
|
|
return fmt.Errorf("expired on %s", e.Expires)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e Exception) matches(f Finding) bool {
|
|
if e.Kind != f.Kind {
|
|
return false
|
|
}
|
|
if e.Package != "" && e.Package != f.Package {
|
|
return false
|
|
}
|
|
if e.File != "" && filepath.ToSlash(e.File) != filepath.ToSlash(f.File) {
|
|
return false
|
|
}
|
|
if e.Symbol != "" && e.Symbol != f.Symbol {
|
|
return false
|
|
}
|
|
return true
|
|
}
|