Add deterministic repo scanner

This commit is contained in:
2026-05-19 03:55:50 +02:00
parent 17bd23e79b
commit afd8b3d608
5 changed files with 1547 additions and 1 deletions

View File

@@ -13,6 +13,7 @@ from pathlib import Path
from .loader import declaration_files, load_yaml
from .graph import FabricGraph, build_graph
from .graph_explorer import fabric_graph_explorer_payload
from .scanner import ScanOptions, scan_repo
from .validation import validate_roots
@@ -62,6 +63,17 @@ def build_parser() -> argparse.ArgumentParser:
export.add_argument("paths", nargs="*", type=Path, default=[Path(".")])
export.add_argument("--format", choices=["json", "mermaid", "graph-explorer"], default="json")
scan = sub.add_parser("scan", help="Scan a repo for deterministic discovery candidates.")
scan.add_argument("path", nargs="?", type=Path, default=Path("."))
scan.add_argument("--repo-slug", default=None)
scan.add_argument("--repo-name", default=None)
scan.add_argument("--domain", default=None)
scan.add_argument("--commit", default=None)
scan.add_argument("--profile", default="deterministic")
scan.add_argument("--dry-run", action="store_true", help="Do not write anywhere except an explicit --output file.")
scan.add_argument("--output", type=Path, default=None, help="Write the discovery snapshot JSON to a file.")
scan.add_argument("--json", action="store_true", help="Print the discovery snapshot JSON to stdout.")
registry = sub.add_parser("registry", help="Feed a running Railiance Fabric registry service.")
registry_sub = registry.add_subparsers(dest="registry_command", required=True)
@@ -140,6 +152,9 @@ def main(argv: list[str] | None = None) -> int:
print(graph.to_json())
return 0
if args.command == "scan":
return _scan_repo(args)
if args.command == "registry":
if args.registry_command == "sync":
return _registry_sync(args)
@@ -368,6 +383,42 @@ def _registry_ingest_cyclonedx(args: argparse.Namespace) -> int:
return 0
def _scan_repo(args: argparse.Namespace) -> int:
snapshot = scan_repo(
ScanOptions(
repo_path=args.path,
repo_slug=args.repo_slug,
repo_name=args.repo_name,
domain=args.domain,
commit=args.commit,
profile=args.profile,
deterministic_only=True,
llm_enabled=False,
)
)
payload = json.dumps(snapshot, indent=2, sort_keys=True)
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(payload + "\n", encoding="utf-8")
if args.json:
print(payload)
return 0
candidates = snapshot["candidates"]
mode = "dry-run " if args.dry_run else ""
print(
f"{mode}scan {snapshot['source']['repo_slug']} "
f"({snapshot['source']['commit']}): "
f"{len(candidates['nodes'])} node(s), "
f"{len(candidates['edges'])} edge(s), "
f"{len(candidates['attributes'])} attribute(s), "
f"{len(snapshot['replacement_scopes'])} replacement scope(s)"
)
if args.output:
print(f"wrote {args.output}")
return 0
class RegistryRequestError(Exception):
pass