feat(identity): rename user-visible identity to voculinux with one-time legacy import

The fork takes its own name everywhere a user can see it: binaries
(voculinux, voculinux-gui), desktop entry, autostart, tray, D-Bus
namespace (com.voculinux.Voculinux, no alias for the old name),
config/data/cache dirs, single-instance lock, IBus engine identity,
tray icons, resource prefixes, VOCULINUX_* environment names, and the
update-checker user agent. The internal Python module stays vocalinux
and the distribution metadata keeps the upstream URLs as provenance.

On first run a previous Vocalinux install's config.json, custom
dictionary, and portal restore token are copied once into the new
locations (new config always wins, no merge, no write-back); models
re-resolve through the pinned-checksum download path. An offline
naming-sweep test proves no old-name strings remain on user-visible
surfaces outside the allowlist.

Refs #18
This commit is contained in:
soubarnak
2026-11-06 01:29:39 +05:30
parent c565e6d946
commit ea7be5f111
251 changed files with 2449 additions and 1991 deletions
+19 -19
View File
@@ -116,10 +116,10 @@ def test_record_audits_payload_and_preserves_export_digests(tmp_path: Path) -> N
(source / "src/vocalinux/version.py").write_text('__version__ = "0.18.5"\n')
root = tmp_path / "install"
distribution(root, "demo", "1.0")
distribution(root, "vocalinux", "0.18.5")
distribution(root, "voculinux", "0.18.5")
CHECK.record(root, source)
manifest = json.loads((root / CHECK.MANIFEST).read_text())
assert manifest["packages"] == {"demo": "1.0", "vocalinux": "0.18.5"}
assert manifest["packages"] == {"demo": "1.0", "voculinux": "0.18.5"}
assert manifest["python"] == list(sys.version_info[:2])
for name, digest in manifest["exports"].items():
assert digest == hashlib.sha256((source / "requirements" / name).read_bytes()).hexdigest()
@@ -154,12 +154,12 @@ def test_launchers_are_relocatable_and_keep_locked_packages_first(
header = (
f"#!/bin/sh\n'''exec' {python} \"$0\" \"$@\"\n' '''\n" if long_shebang else f"#!{python}\n"
)
for name in ("vocalinux", "vocalinux-gui", "pwcpp"):
for name in ("voculinux", "voculinux-gui", "pwcpp"):
path = binary / name
path.write_text(header + "print('entry point')\n")
path.chmod(0o755)
(binary / "python3").symlink_to(sys.executable)
site = distribution(install, "vocalinux", "0.18.5")
site = distribution(install, "voculinux", "0.18.5")
package = site / "vocalinux"
package.mkdir()
(package / "__init__.py").write_text("")
@@ -169,13 +169,13 @@ def test_launchers_are_relocatable_and_keep_locked_packages_first(
platform.mkdir(parents=True)
(platform / "__init__.py").write_text("raise RuntimeError('wrong runtime')\n")
BUILD.relocate_scripts(install, tooling)
for name in ("vocalinux", "vocalinux-gui", "pwcpp"):
for name in ("voculinux", "voculinux-gui", "pwcpp"):
text = (binary / name).read_text()
assert text.startswith("#!/usr/bin/env python3\n")
assert str(tooling) not in text
assert os.access(binary / name, os.X_OK)
result = subprocess.run(
[str(binary / "vocalinux")],
[str(binary / "voculinux")],
env={
**os.environ,
"PATH": f"{binary}{os.pathsep}{os.environ['PATH']}",
@@ -211,7 +211,7 @@ def test_build_orchestration_uses_hashes_and_separates_tooling(
def run(argv: list[str], cwd: Path, env: dict[str, str]) -> None:
calls.append((argv, cwd, env.copy()))
if str(source) == argv[-1]:
for name in ("vocalinux", "vocalinux-gui"):
for name in ("voculinux", "voculinux-gui"):
(install / "bin" / name).write_text(f"#!{argv[0]}\n")
monkeypatch.setattr(BUILD.venv.EnvBuilder, "create", create)
@@ -309,7 +309,7 @@ def test_runtime_pip_flags_reject_corrupt_artifact_offline(tmp_path: Path) -> No
environment = tmp_path / "tooling"
venv.EnvBuilder(with_pip=True).create(environment)
wheel = tmp_path / "demo-1.0-py3-none-any.whl"
real_zipfile = getattr(sys, "_vocalinux_real_zipfile", zipfile)
real_zipfile = getattr(sys, "_voculinux_real_zipfile", zipfile)
with real_zipfile.ZipFile(wheel, "w") as archive:
archive.writestr("demo.py", "VALUE = 1\n")
archive.writestr(
@@ -369,7 +369,7 @@ def test_full_python_payload_build_is_offline_and_keeps_project_metadata(tmp_pat
(source / "src/vocalinux/version.py").write_text('__version__ = "0.18.5"\n')
wheelhouse = tmp_path / "wheels"
wheelhouse.mkdir()
real_zipfile = getattr(sys, "_vocalinux_real_zipfile", zipfile)
real_zipfile = getattr(sys, "_voculinux_real_zipfile", zipfile)
def wheel(name: str, version: str, files: dict[str, str]) -> Path:
path = wheelhouse / f"{name}-{version}-py3-none-any.whl"
@@ -399,13 +399,13 @@ def test_full_python_payload_build_is_offline_and_keeps_project_metadata(tmp_pat
)
runtime = wheel("demo", "1.0", {"demo.py": "VALUE = 1\n"})
app = wheel(
"vocalinux",
"voculinux",
"0.18.5",
{
"vocalinux/__init__.py": "",
"vocalinux/main.py": "def main():\n print('fixture app')\n",
"vocalinux-0.18.5.dist-info/entry_points.txt": "[console_scripts]\nvocalinux = vocalinux.main:main\n"
"[gui_scripts]\nvocalinux-gui = vocalinux.main:main\n",
"voculinux-0.18.5.dist-info/entry_points.txt": "[console_scripts]\nvoculinux = vocalinux.main:main\n"
"[gui_scripts]\nvoculinux-gui = vocalinux.main:main\n",
},
)
for filename, name, version, artifact in (
@@ -421,7 +421,7 @@ def test_full_python_payload_build_is_offline_and_keeps_project_metadata(tmp_pat
metadata = source / "pyproject.toml"
metadata.write_text(
'[build-system]\nrequires = []\nbuild-backend = "backend"\nbackend-path = ["."]\n'
'[project]\nname = "vocalinux"\nversion = "0.18.5"\ndependencies = ["PyGObject"]\n'
'[project]\nname = "voculinux"\nversion = "0.18.5"\ndependencies = ["PyGObject"]\n'
)
(source / "backend.py").write_text(
"import shutil\nfrom pathlib import Path\n"
@@ -434,11 +434,11 @@ def test_full_python_payload_build_is_offline_and_keeps_project_metadata(tmp_pat
install = tmp_path / "install"
BUILD.build(source, install, tmp_path / "work")
assert metadata.read_bytes() == before
CHECK.audit(install, {"demo": "1.0", "vocalinux": "0.18.5"})
CHECK.audit(install, {"demo": "1.0", "voculinux": "0.18.5"})
assert not list((tmp_path / "work").iterdir())
assert not (install / "bin/pip").exists()
result = subprocess.run(
[str(install / "bin/python3"), str(install / "bin/vocalinux")],
[str(install / "bin/python3"), str(install / "bin/voculinux")],
capture_output=True,
text=True,
timeout=5,
@@ -464,7 +464,7 @@ def test_artifact_smoke_refuses_user_install_and_checks_the_supplied_file(
"with open(os.environ['SNAP_TEST_LOG'], 'a') as stream:\n"
" stream.write(json.dumps([Path(sys.argv[0]).name, sys.argv[1:], "
"os.environ.get('PYNPUT_BACKEND')]) + '\\n')\n"
"if sys.argv[1:3] == ['list', 'vocalinux']:\n"
"if sys.argv[1:3] == ['list', 'voculinux']:\n"
" sys.exit(0 if os.environ['SNAP_TEST_EXISTING'] == '1' else 1)\n"
)
path.chmod(0o755)
@@ -490,8 +490,8 @@ def test_artifact_smoke_refuses_user_install_and_checks_the_supplied_file(
else:
assert result.returncode == 0, result.stderr
assert calls[1][:2] == ["sudo", ["snap", "install", "--dangerous", str(artifact)]]
assert calls[2][:2] == ["snap", ["run", "vocalinux", "--version"]]
assert calls[3][1][:4] == ["run", "--shell", "vocalinux", "-c"]
assert calls[2][:2] == ["snap", ["run", "voculinux", "--version"]]
assert calls[3][1][:4] == ["run", "--shell", "voculinux", "-c"]
assert '"$SNAP/snap/command-chain/desktop-launch"' in calls[3][1][4]
assert '"$SNAP/usr/lib/vocalinux/check_runtime.py"' in calls[3][1][4]
assert '"$SNAP/usr/lib/voculinux/check_runtime.py"' in calls[3][1][4]
assert calls[3][2] == "dummy"