#!/usr/bin/env python3 """Reject L1 object specs that redeclare L0-injected system fields.""" import argparse import json import sys from pathlib import Path RESERVED_FIELD_NAMES = { "id", "ownerid", "createdbyid", "lastmodifiedbyid", "createddate", "lastmodifieddate", "systemmodstamp", "isdeleted", } def load_json(path: Path): with path.open(encoding="utf-8") as handle: return json.load(handle) def object_specs_for_component(pack_root: Path, component: dict): if "payload" in component: payload = component["payload"] if isinstance(payload, str): return json.loads(payload) return payload component_path = component.get("path") if not component_path: raise ValueError(f"object_spec component {component.get('businessKey', '')} has no path or payload") return load_json(pack_root / component_path) def diagnostics_for_pack(pack_root: Path): manifest = load_json(pack_root / "pack.json") diagnostics = [] for component in manifest.get("components", []): if component.get("kind") != "object_spec": continue specs = object_specs_for_component(pack_root, component) if not isinstance(specs, list): raise ValueError(f"object_spec component {component.get('businessKey', '')} must be a JSON array") for spec in specs: object_api_name = str(spec.get("apiName", "")).strip() for field in spec.get("fields", []) or []: field_api_name = str(field.get("apiName", "")).strip() if field_api_name.lower() in RESERVED_FIELD_NAMES: diagnostics.append( "component.payload.reserved_field_name: " f"{object_api_name}.{field_api_name} is an L0 system column injected automatically; " "L1 packs must not redeclare it" ) return diagnostics def main(argv=None): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "pack_roots", nargs="+", type=Path, help="Pack directories containing pack.json", ) args = parser.parse_args(argv) diagnostics = [] try: for pack_root in args.pack_roots: diagnostics.extend(diagnostics_for_pack(pack_root)) except (OSError, ValueError, json.JSONDecodeError) as exc: print(f"check_object_specs_reserved_fields: {exc}", file=sys.stderr) return 2 if diagnostics: for diagnostic in diagnostics: print(diagnostic, file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main())