fix: improve code-size gate output and duplicate detection, fix Windows path in source-display

This commit is contained in:
quotentiroler
2026-02-09 13:18:51 -08:00
parent 65dae9a088
commit 1fad19008e
2 changed files with 57 additions and 29 deletions
+55 -28
View File
@@ -40,33 +40,20 @@ SKIP_SHORT_PATTERNS = {
} }
SKIP_SHORT_SUFFIXES = ('-cli.ts',) SKIP_SHORT_SUFFIXES = ('-cli.ts',)
# Function names to skip in duplicate detection (common utilities, test helpers) # Function names to skip in duplicate detection.
# Only list names so generic they're expected to appear independently in many modules.
# Do NOT use prefix-based skipping — it hides real duplication (e.g. formatDuration,
# stripPrefix, parseConfig are specific enough to flag).
SKIP_DUPLICATE_FUNCTIONS = { SKIP_DUPLICATE_FUNCTIONS = {
# Common utility names # Lifecycle / framework plumbing
'main', 'init', 'setup', 'teardown', 'cleanup', 'dispose', 'destroy', 'main', 'init', 'setup', 'teardown', 'cleanup', 'dispose', 'destroy',
'open', 'close', 'connect', 'disconnect', 'execute', 'run', 'start', 'stop', 'open', 'close', 'connect', 'disconnect', 'execute', 'run', 'start', 'stop',
'render', 'update', 'refresh', 'reset', 'clear', 'flush', 'render', 'update', 'refresh', 'reset', 'clear', 'flush',
# Too-short / too-generic identifiers
'text', 'json', 'pad', 'mask', 'digest', 'confirm', 'intro', 'outro',
'exists', 'send', 'receive', 'listen', 'log', 'warn', 'error', 'info',
'help', 'version', 'config', 'configure', 'describe', 'test', 'action',
} }
SKIP_DUPLICATE_PREFIXES = (
# Transformers
'normalize', 'parse', 'validate', 'serialize', 'deserialize',
'convert', 'transform', 'extract', 'encode', 'decode',
# Predicates
'is', 'has', 'can', 'should', 'will',
# Constructors/factories
'create', 'make', 'build', 'generate', 'new',
# Accessors
'get', 'set', 'read', 'write', 'load', 'save', 'fetch',
# Handlers
'handle', 'on', 'emit',
# Modifiers
'add', 'remove', 'delete', 'update', 'insert', 'append',
# Other common
'to', 'from', 'with', 'apply', 'process', 'resolve', 'ensure', 'check',
'filter', 'map', 'reduce', 'merge', 'split', 'join', 'find', 'search',
'register', 'unregister', 'subscribe', 'unsubscribe',
)
SKIP_DUPLICATE_FILE_PATTERNS = ('.test.ts', '.test.tsx', '.spec.ts') SKIP_DUPLICATE_FILE_PATTERNS = ('.test.ts', '.test.tsx', '.spec.ts')
# Known packages in the monorepo # Known packages in the monorepo
@@ -150,12 +137,33 @@ def find_duplicate_functions(files: List[Tuple[Path, int]], root_dir: Path) -> D
# Skip known common function names # Skip known common function names
if func in SKIP_DUPLICATE_FUNCTIONS: if func in SKIP_DUPLICATE_FUNCTIONS:
continue continue
if any(func.startswith(prefix) for prefix in SKIP_DUPLICATE_PREFIXES):
continue
function_locations[func].append(file_path) function_locations[func].append(file_path)
# Filter to only duplicates # Filter to only duplicates, ignoring cross-extension duplicates.
return {name: paths for name, paths in function_locations.items() if len(paths) > 1} # Extensions are independent packages — the same function name in
# extensions/telegram and extensions/discord is expected, not duplication.
result: Dict[str, List[Path]] = {}
for name, paths in function_locations.items():
if len(paths) < 2:
continue
# If ALL instances are in different extensions, skip
ext_dirs = set()
non_ext = False
for p in paths:
try:
rel = p.relative_to(root_dir)
parts = rel.parts
if len(parts) >= 2 and parts[0] == 'extensions':
ext_dirs.add(parts[1])
else:
non_ext = True
except ValueError:
non_ext = True
# Skip if every instance lives in a different extension (no core overlap)
if not non_ext and len(ext_dirs) == len(paths):
continue
result[name] = paths
return result
def validate_git_ref(root_dir: Path, ref: str) -> bool: def validate_git_ref(root_dir: Path, ref: str) -> bool:
@@ -285,8 +293,6 @@ def find_duplicate_regressions(
for func in functions: for func in functions:
if func in SKIP_DUPLICATE_FUNCTIONS: if func in SKIP_DUPLICATE_FUNCTIONS:
continue continue
if any(func.startswith(prefix) for prefix in SKIP_DUPLICATE_PREFIXES):
continue
base_function_locations[func].append(file_path) base_function_locations[func].append(file_path)
base_dupes = {name for name, paths in base_function_locations.items() if len(paths) > 1} base_dupes = {name for name, paths in base_function_locations.items() if len(paths) > 1}
@@ -432,7 +438,28 @@ def main():
print() print()
if args.strict and violations: if args.strict and violations:
# Print actionable summary so contributors know what to do
print("" * 60)
print("❌ Code size check failed\n")
if crossed:
print(f" {len(crossed)} file(s) grew past the {args.threshold}-line limit.")
if grew:
print(f" {len(grew)} file(s) already over {args.threshold} lines got larger.")
print()
print(" How to fix:")
print(" • Split large files into smaller, focused modules")
print(" • Extract helpers, types, or constants into separate files")
print(" • See AGENTS.md for guidelines (~500-700 LOC target)")
print()
print(f" This check compares your PR against {args.compare_to}.")
print(f" Only code files are checked ({', '.join(sorted(e for e in CODE_EXTENSIONS))}).")
print(" Docs, tests names, and config files are not affected.")
print("" * 60)
sys.exit(1) sys.exit(1)
elif args.strict:
print("" * 60)
print("✅ Code size check passed — no files exceed thresholds.")
print("" * 60)
return return
+2 -1
View File
@@ -23,7 +23,8 @@ function tryRelative(root: string, filePath: string): string | null {
if (path.isAbsolute(rel)) { if (path.isAbsolute(rel)) {
return null; return null;
} }
return rel; // Normalize to forward slashes for display (path.relative uses backslashes on Windows)
return rel.replaceAll("\\", "/");
} }
export function resolvePluginSourceRoots(params: { workspaceDir?: string }): PluginSourceRoots { export function resolvePluginSourceRoots(params: { workspaceDir?: string }): PluginSourceRoots {