"""Ticket #18 + #24: offline naming-sweep — no old-name strings on surfaces. The fork takes its own name everywhere a user can see it. Anything still spelling the old name must be on the allowlist below: - upstream provenance (``VocaHQ/vocalinux`` URLs), - the retained internal Python module name (``src/vocalinux`` paths, ``vocalinux.*`` imports/attributes, ``-m vocalinux.main``), - retained internal class names (``VocalinuxEngine*``, ``VocalinuxDBusService``), - one-time-legacy import sources (old config/dictionary/bus literals), - generated or historical files (``uv.lock``, ``requirements/*.txt``, ``CHANGELOG.md``), - web upstream surfaces (ticket #24): upstream GitHub/raw/nix flake URLs, nix flake API identifiers, and the nixos-page fork-status note that names the upstream flake as installing Vocalinux rather than Voculinux. """ import os import re REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) OLD_NAME = re.compile(r"vocalinux", re.IGNORECASE) SKIP_DIRS = { ".git", "__pycache__", ".venv", "venv", "node_modules", ".next", "build", "dist", "out", } SKIP_SUFFIXES = (".egg-info",) SKIP_FILES = {"uv.lock", "CHANGELOG.md"} SKIP_PREFIXES = ("requirements/",) BINARY_EXTS = ( ".wav", ".mp3", ".ogg", ".png", ".svg", ".ico", ".icns", ".bin", ".onnx", ".pt", ".zip", ".tar", ".gz", ".whl", ".AppImage", ".zsync", ".pyc", ) #: web/ was fully rebranded in ticket #24; only upstream-provenance lines #: (matched in _allowed) may still spell the old name there. WEB_OWNED = {"web/src/app/page.tsx", "web/package.json"} def _allowed(path: str, line: str) -> bool: """Return True when an old-name match is an allowlisted surface.""" low = line.lower() if "vocahq/" in low or "jatinkrmalik/vocalinux" in low: return True if "vocalinux" in low and "voculinux" in low: # Migration-compat shims that name both installs at once. return True if "upstream" in low: # Upstream-provenance mentions that explicitly label the old name # as upstream (fork-status notes, provenance cards). return True if "/{name}/vocalinux" in line: # Upstream repo-name template (retained provenance). return True if path == "snap/local/check_runtime.py" and '"vocalinux"' in line: # Smoke imports resolve the retained import package. return True if re.search(r'vocalinux\.["a-z_]', line): return True if line.strip().rstrip(",").lstrip("/ ") == '"vocalinux"': # Retained module-dir path segment element in tests and tooling # (single-line and os.path.join element forms). return True if 'site / "vocalinux"' in line or '"platform/vocalinux"' in line: # Fixture module trees named after the retained import package. return True if '"vocalinux/' in line: # Fixture module file paths under the retained package name. return True if "/vocalinux/" in line or "src/vocalinux" in line: return True if "-m vocalinux" in line or "import vocalinux" in line: return True if "VocalinuxEngine" in line or "VocalinuxDBusService" in line: return True if "cmdline" in line: return True if " - vocalinux" in line: # Log logger names keep the retained module path. return True if path in ( "src/vocalinux/legacy_import.py", "tests/test_legacy_import.py", ): return True if "LEGACY" in line or "legacy" in line: return True if "~/.config/vocalinux" in line or '".config" / "vocalinux"' in line: # Documented one-time-import source (ticket #18). return True if "from vocalinux" in line: return True if '"src"' in line and '"vocalinux"' in line: # Retained module-dir path segments in tests and tooling. return True if path == "web/src/app/install/nixos/page.tsx": # The Nix flake is upstream's: flake URL, input/output names, module # paths, and programs.* options spell the retained upstream API. # The page carries a fork-status note stating those snippets install # Vocalinux (upstream), not Voculinux. return True if path == "tests/test_rename_sweep.py": return True if path == "pyproject.toml" and re.search(r'"vocalinux[."\'\]]', line): return True if path == "pyproject.toml" and "vocalinux.version" in line: return True if 'PKG_NAME="vocalinux"' in line: # Installer probes the retained import package, not the distribution. return True if path == "packaging/void/srcpkgs/voculinux/template" and "vocalinux-${version}" in line: # Pre-rename release asset root (ticket #27); dropped at the first # post-rename release bump. return True if path == "tests/test_void_app_template.py" and '"vocalinux"' in line: # The app-template policy test asserts the old name is gone. return True if path == "tests/test_void_app_template.py" and "usr/bin/vocalinux" in line: # Pre-rename sdist launchers the template promotes at install time # (ticket #28); dropped at the first post-rename release bump. return True if path == "packaging/void/container-main.sh" and ( "usr/share/applications/vocalinux" in line or 'version("vocalinux")' in line ): # Pre-rename sdist compat (ticket #28): the payload guard names the # old file names it rejects, and the import smoke queries the old # distribution name. Dropped at the first post-rename release bump. return True if '== "vocalinux"' in line or '.startswith("vocalinux' in line: # Retained module identity comparisons in test isolation helpers. return True return False def _iter_files(): for root, dirs, files in os.walk(REPO_ROOT): dirs[:] = [d for d in sorted(dirs) if d not in SKIP_DIRS] for name in sorted(files): if name in SKIP_FILES or name.endswith(BINARY_EXTS): continue full = os.path.join(root, name) rel = os.path.relpath(full, REPO_ROOT) if rel.startswith(SKIP_PREFIXES): continue yield rel, full def test_naming_sweep_no_old_name_outside_allowlist() -> None: """Every old-name hit must be an allowlisted retained surface.""" offenders: list[str] = [] for rel, full in _iter_files(): if any(part.endswith(SKIP_SUFFIXES) for part in rel.split("/")): continue try: with open(full, encoding="utf-8") as handle: text = handle.read() except (UnicodeDecodeError, ValueError): continue for number, line in enumerate(text.splitlines(), 1): if OLD_NAME.search(line) and not _allowed(rel, line): offenders.append(f"{rel}:{number}:{line.strip()[:160]}") assert ( offenders == [] ), f"{len(offenders)} old-name string(s) outside the allowlist:\n" + "\n".join(offenders[:60]) def test_owned_web_files_carry_new_name() -> None: """The two web rename surfaces name the fork (URLs aside).""" for rel in sorted(WEB_OWNED): with open(os.path.join(REPO_ROOT, rel), encoding="utf-8") as handle: text = handle.read() assert "Voculinux" in text or "voculinux" in text, rel