174 lines
5.6 KiB
Python
174 lines
5.6 KiB
Python
#!/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())
|