"""Hash authored addon files, excluding generated Workbench/build state. Keep this shared by native evidence archival and release qualification. Unknown resource formats are included automatically, rather than silently omitted. """ import hashlib from pathlib import Path GENERATED_DIRECTORIES = {'.build', '.local', '__pycache__', 'profiles'} def addon_manifest(root): root = Path(root).resolve() addons = root / 'addons' if not addons.is_dir() or addons.is_symlink(): raise ValueError('addons must be a real directory inside the repository') manifest = {} for path in sorted(addons.rglob('*')): relative = path.relative_to(root) if any(part in GENERATED_DIRECTORIES for part in relative.parts[1:]): continue if (len(relative.parts) == 3 and path.name.startswith('resourceDatabase.rdb')) or path.suffix in {'.pak', '.log'}: continue if path.is_symlink(): raise ValueError(f'addon source/resource symlink is not supported: {relative.as_posix()}') if path.is_file(): manifest[relative.as_posix()] = hashlib.sha256(path.read_bytes()).hexdigest() if not manifest: raise ValueError('addon source/resource manifest is empty') return manifest