#!/usr/bin/env python3 """Rewrite OEEL manifest license and depends list across enterprise-addons.""" from __future__ import annotations import ast import os import re import sys from pathlib import Path ROOT = Path("/root/Documents/trae_projects/one_b_os/enterprise-addons") LOG_PATH = Path("/tmp/manifest_rewrite.log") def collect_module_names(root: Path) -> set[str]: names: set[str] = set() for child in root.iterdir(): if child.is_dir() and (child / "__manifest__.py").is_file(): names.add(child.name) return names def rewrite_manifest( path: Path, own_name: str, oeel_modules: set[str], ) -> tuple[str, bool, bool]: """Return (new_source, license_changed, depends_changed).""" src = path.read_text(encoding="utf-8") # 1) License string swap on the raw source (handles both 'license': 'OEEL-1' # with single or double quotes, with optional trailing comma). We do this # with a regex because some manifests do not survive round-tripping via # ast.unparse safely (odd comment/formatting). license_pattern = re.compile( r"(['\"]license['\"]\s*:\s*['\"])OEEL-1(['\"])" ) new_src, n_license = license_pattern.subn(r"\1LGPL-3\2", src) # 2) depends list filter via AST if possible, else regex fallback. depends_changed = False try: tree = ast.parse(new_src) except SyntaxError: tree = None if tree is not None: mutator = _AstDependsFilter(own_name, oeel_modules) for node in _iter_dicts(tree): changed = mutator.handle(node) if changed: depends_changed = True if depends_changed: new_src = ast.unparse(tree) else: # Fallback regex: find 'depends': [...] block. dep_pattern = re.compile( r"(['\"]depends['\"]\s*:\s*\[)([^\]]*?)(\])", re.DOTALL, ) def strip(m: re.Match[str]) -> str: nonlocal depends_changed head, body, tail = m.group(1), m.group(2), m.group(3) items = re.findall(r"['\"]([^'\"]+)['\"]", body) kept = [ x for x in items if x not in oeel_modules and x != own_name ] removed = len(items) - len(kept) if removed: depends_changed = True body_new = ", ".join(f"'{x}'" for x in kept) return f"{head}{body_new}{tail}" new_src = dep_pattern.sub(strip, new_src) return new_src, n_license > 0, depends_changed def _iter_dicts(tree: ast.AST): """Yield every Dict node in the tree (skip non-Dict roots like Module).""" def walk(n): if isinstance(n, ast.Dict): yield n for child in ast.iter_child_nodes(n): yield from walk(child) else: for child in ast.iter_child_nodes(n): yield from walk(child) yield from walk(tree) class _AstDependsFilter: def __init__(self, own_name: str, oeel_modules: set[str]) -> None: self.own_name = own_name self.oeel = oeel_modules self._changed = False def handle(self, node: ast.Dict) -> bool: for key, val in zip(node.keys, node.values): if not isinstance(key, ast.Constant) or not isinstance( val, (ast.List, ast.Tuple) ): continue if key.value != "depends": continue new_elts = [] removed_any = False for elt in val.elts: if ( isinstance(elt, ast.Constant) and isinstance(elt.value, str) and (elt.value in self.oeel or elt.value == self.own_name) ): removed_any = True continue new_elts.append(elt) if removed_any: # Preserve container type val.elts = new_elts self._changed = True return self._changed def main() -> int: oeel_modules = collect_module_names(ROOT) print(f"OEEL module count: {len(oeel_modules)}") license_changes = 0 depends_changes = 0 errors: list[str] = [] with LOG_PATH.open("w", encoding="utf-8") as log: log.write("module\tfile\tevent\n") for module_dir in sorted(ROOT.iterdir()): if not module_dir.is_dir(): continue manifest = module_dir / "__manifest__.py" if not manifest.is_file(): continue try: new_src, lic_changed, dep_changed = rewrite_manifest( manifest, module_dir.name, oeel_modules ) if lic_changed or dep_changed: manifest.write_text(new_src, encoding="utf-8") log.write( f"{module_dir.name}\tmanifest\t" f"license_changed={lic_changed} " f"depends_stripped={dep_changed}\n" ) license_changes += int(lic_changed) depends_changes += int(dep_changed) except Exception as exc: # noqa: BLE001 errors.append(f"{module_dir.name}: {exc!r}") log.write( f"{module_dir.name}\tmanifest\tERROR: {exc!r}\n" ) print(f"License changes: {license_changes}") print(f"Depends-stripped modules: {depends_changes}") print(f"Errors: {len(errors)}") for e in errors: print(" -", e) print(f"Log written to: {LOG_PATH}") return 1 if errors else 0 if __name__ == "__main__": sys.exit(main())