Ticket xavierk/voculinux#28, needs #27. One documented command (packaging/void/smoke.sh, just void-smoke) lints, builds, installs and smoke-tests every template under packaging/void/srcpkgs in dependency order in the pinned Void container via podman, failing fast naming the package with the harness echoing each stage. The gate caught three latent breaks in the per-package harness and the app template, fixed here: - container-main.sh scoped the libggml scan to python3-pywhispercpp and gave the leaf packages their own import smokes (pynput by presence: no display in the container), failing loudly for packages without a smoke recipe; - voculinux template drops the deprecated wrksrc override (xbps-src renames the single-top-dir archive itself; xlint rejects wrksrc) and builds the PEP 517 wheel (the sdist ships no setup.py); - post_install promotes the pre-rename sdist names (desktop entry content, icons, /usr/bin launchers) to the renamed identity, and the payload smoke rejects pre-rename leftovers in OS-visible paths. Offline policy tests (tests/test_void_smoke_gate.py) pin the gate to the template set, the app-last order, podman-only pinned inputs and per-package failure identity; the #27 policy tests and the rename sweep cover the harness and template contracts.
197 lines
7.3 KiB
Python
197 lines
7.3 KiB
Python
"""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
|