diff --git a/scripts/__pycache__/obfuscate_release.cpython-314.pyc b/scripts/__pycache__/obfuscate_release.cpython-314.pyc index 0051a92..6ee1bb9 100644 Binary files a/scripts/__pycache__/obfuscate_release.cpython-314.pyc and b/scripts/__pycache__/obfuscate_release.cpython-314.pyc differ diff --git a/scripts/obfuscate_release.py b/scripts/obfuscate_release.py index 0c4df20..95c222c 100644 --- a/scripts/obfuscate_release.py +++ b/scripts/obfuscate_release.py @@ -210,11 +210,39 @@ def collect_asset_groups(asset_root: Path) -> dict[tuple[Path, str, str], list[P return groups -def pick_source_file(paths: list[Path], base: str, ext: str) -> Path: - plain = paths[0].parent / f"{base}{ext}" +def pick_source_file(paths: list[Path], base: str, ext: str) -> Path | None: + if not paths: + return None + + parent = paths[0].parent + plain = parent / f"{base}{ext}" + ordered: list[Path] = [] if plain in paths: - return plain - return min(paths, key=lambda p: len(p.name)) + ordered.append(plain) + for candidate in sorted(paths, key=lambda p: len(p.name)): + if candidate not in ordered: + ordered.append(candidate) + + for candidate in ordered: + try: + content = candidate.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError): + continue + if is_valid_source_content(content): + return candidate + return None + + +def cleanup_invalid_siblings(paths: list[Path], source: Path) -> None: + for path in paths: + if path == source or not path.exists(): + continue + try: + content = path.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError): + content = "" + if not is_valid_source_content(content): + path.unlink() def run_cmd(command: list[str], cwd: Path) -> None: @@ -232,7 +260,10 @@ def run_cmd(command: list[str], cwd: Path) -> None: def process_js(path: Path, cwd: Path) -> None: original = path.read_text(encoding="utf-8") if not is_valid_source_content(original): - raise ValueError(f"invalid or corrupted JS source: {path}") + raise ValueError( + f"invalid or corrupted JS source: {path} " + f"(restore e.g. 'git checkout dev -- {path.as_posix()}')" + ) if shutil.which("npx"): tmpdir = Path(tempfile.mkdtemp()) @@ -360,6 +391,8 @@ def build_hash_mapping(public_root: Path) -> dict[str, str]: groups = collect_asset_groups(asset_root) for (parent, base, ext), paths in groups.items(): source = pick_source_file(paths, base, ext) + if source is None: + continue digest = hash_file(source) target = parent / f"{base}.{digest}{ext}" rel_new = target.relative_to(public_root).as_posix() @@ -397,8 +430,17 @@ def main() -> int: asset_root = public_root / "assets" if asset_root.exists(): groups = collect_asset_groups(asset_root) - for (_parent, base, ext), paths in sorted(groups.items()): + for (parent, base, ext), paths in sorted(groups.items()): source = pick_source_file(paths, base, ext) + if source is None: + rel = (parent / f"{base}{ext}").relative_to(public_root) + print( + f"ERROR: No valid source for {rel}. " + f"Restore from dev, e.g.: git checkout dev -- {rel}", + file=sys.stderr, + ) + return 1 + cleanup_invalid_siblings(paths, source) if ext == ".js": process_js(source, repo_root) else: