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
+22 -22
View File
@@ -11,8 +11,8 @@ from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
AUR = REPO_ROOT / "packaging" / "aur"
PKGBUILD = AUR / "vocalinux" / "PKGBUILD"
BIN_DIR = AUR / "vocalinux-bin"
PKGBUILD = AUR / "voculinux" / "PKGBUILD"
BIN_DIR = AUR / "voculinux-bin"
BIN_PKGBUILD = BIN_DIR / "PKGBUILD"
GATE_SH = AUR / "build-test.sh"
WORKFLOWS = REPO_ROOT / ".github" / "workflows"
@@ -190,7 +190,7 @@ def test_the_smoke_walks_the_pinned_deps():
assert "requirements/runtime.txt" in script
assert "--require-hashes" in script
assert "pacman -U" in script and "vocalinux --version" in script
assert "pacman -U" in script and "voculinux --version" in script
# Arch name -> PyPI name for the export the gate installs from.
pypi_names = {"python-pywhispercpp": "pywhispercpp", "python-pynput": "pynput"}
for arch_name, pypi_name in pypi_names.items():
@@ -369,7 +369,7 @@ def test_bin_package_ships_the_release_appimage_per_arch() -> None:
text = _bin_pkgbuild()
assert "arch=('x86_64' 'aarch64')" in text
for arch in ("x86_64", "aarch64"):
asset = f"Vocalinux-${{_tag}}-{arch}.AppImage"
asset = f"Voculinux-${{_tag}}-{arch}.AppImage"
assert (
f'"{asset}::https://github.com/VocaHQ/vocalinux/releases/download/'
f"v${{_tag}}/{asset}" in text
@@ -383,19 +383,19 @@ def test_bin_package_installs_the_appimage_intact_and_runnable() -> None:
text = _bin_pkgbuild()
assert "options=('!strip')" in text
assert "'fuse2'" in text
assert 'install -Dm755 "Vocalinux-${_tag}-${CARCH}.AppImage"' in text
assert 'install -Dm755 "Voculinux-${_tag}-${CARCH}.AppImage"' in text
assert '"/opt/${pkgname}/' in text
assert "ln -s" in text and '"${pkgdir}/usr/bin/vocalinux"' in text
assert "ln -s" in text and '"${pkgdir}/usr/bin/voculinux"' in text
def test_bin_package_conflicts_with_the_source_and_git_packages() -> None:
"""All three packages ship /usr/bin/vocalinux; provides= lets a depends on
'vocalinux' resolve to the -bin install."""
"""All three packages ship /usr/bin/voculinux; provides= lets a depends on
'voculinux' resolve to the -bin install."""
text = _bin_pkgbuild()
assert "provides=('vocalinux')" in text
assert "provides=('voculinux')" in text
conflicts = re.search(r"conflicts=\(([^)]*)\)", text)
assert conflicts, "vocalinux-bin has no conflicts=()"
for name in ("'vocalinux'", "'vocalinux-git'"):
assert conflicts, "voculinux-bin has no conflicts=()"
for name in ("'voculinux'", "'voculinux-git'"):
assert name in conflicts.group(1)
@@ -405,8 +405,8 @@ def test_bin_pkgbuild_bundled_files_match_the_repo_copies() -> None:
outside the bundle). Guarded byte-for-byte so a fix to a canonical file
cannot leave the -bin package shipping the stale one."""
for name, canonical in (
("vocalinux.desktop", REPO_ROOT / "vocalinux.desktop"),
("vocalinux.svg", REPO_ROOT / "resources" / "icons" / "scalable" / "vocalinux.svg"),
("voculinux.desktop", REPO_ROOT / "voculinux.desktop"),
("voculinux.svg", REPO_ROOT / "resources" / "icons" / "scalable" / "voculinux.svg"),
("LICENSE", REPO_ROOT / "LICENSE"),
):
bundled = BIN_DIR / name
@@ -415,10 +415,10 @@ def test_bin_pkgbuild_bundled_files_match_the_repo_copies() -> None:
f"{name} drifted from {canonical.relative_to(REPO_ROOT)};" " sync the bundled copy"
)
assert f"'{name}'" in _bin_pkgbuild(), f"{name} not in source=()"
desktop = (BIN_DIR / "vocalinux.desktop").read_text(encoding="utf-8")
desktop = (BIN_DIR / "voculinux.desktop").read_text(encoding="utf-8")
# The names package() links into /usr/bin and installs under hicolor.
assert re.search(r"^Exec=vocalinux$", desktop, re.M)
assert re.search(r"^Icon=vocalinux$", desktop, re.M)
assert re.search(r"^Exec=voculinux$", desktop, re.M)
assert re.search(r"^Icon=voculinux$", desktop, re.M)
def test_bin_package_asks_the_host_only_for_what_the_bundle_lacks() -> None:
@@ -436,7 +436,7 @@ def test_bin_package_asks_the_host_only_for_what_the_bundle_lacks() -> None:
assert "'wtype'" in depends.group(1)
lines = text.splitlines()
start = next((i for i, line in enumerate(lines) if line.startswith("optdepends=(")), None)
assert start is not None, "vocalinux-bin has no optdepends=()"
assert start is not None, "voculinux-bin has no optdepends=()"
end = next(i for i in range(start + 1, len(lines)) if lines[i].strip() == ")")
optdepends = "\n".join(lines[start + 1 : end])
for tool in ("ibus", "xclip", "wl-clipboard", "ydotool"):
@@ -445,7 +445,7 @@ def test_bin_package_asks_the_host_only_for_what_the_bundle_lacks() -> None:
def test_bin_pkgbuild_tracks_the_release_tag_like_the_source_one() -> None:
"""Same pkgver/_tag split as packaging/aur/vocalinux, real digests for
"""Same pkgver/_tag split as packaging/aur/voculinux, real digests for
the published AppImages, and the release workflow must bump and publish
both — a -bin left on the last tag would serve the previous release's
AppImage."""
@@ -455,14 +455,14 @@ def test_bin_pkgbuild_tracks_the_release_tag_like_the_source_one() -> None:
# Local builds must verify payloads against real sums, not SKIP.
assert "'SKIP'" not in text
release = RELEASE.read_text(encoding="utf-8")
assert "packaging/aur/vocalinux-bin/PKGBUILD" in release, (
assert "packaging/aur/voculinux-bin/PKGBUILD" in release, (
"release.yml bumps the source PKGBUILD only; the -bin package would"
" keep installing the previous release's AppImage"
)
# The publish step's own updpkgsums only refreshes the x86_64 array, so
# release.yml pins both per-arch digests itself right before publishing.
assert re.search(r"for arch in x86_64 aarch64[\s\S]*sha256sums_\$\{arch\}", release), (
"release.yml must pin per-arch AppImage digests for vocalinux-bin — "
"release.yml must pin per-arch AppImage digests for voculinux-bin — "
"the publish action's updpkgsums only covers the runner's arch"
)
# The aarch64 AppImage lands in a later job; the -bin publish must wait
@@ -472,12 +472,12 @@ def test_bin_pkgbuild_tracks_the_release_tag_like_the_source_one() -> None:
), "publish-aur-bin must need build-appimage-arm64 so the aarch64 asset exists"
# The -bin PKGBUILD names local sources; a lone-PKGBUILD publish leaves
# users unable to build. asset_dir mirrors the whole directory to AUR.
assert "asset_dir: packaging/aur/vocalinux-bin" in release
assert "asset_dir: packaging/aur/voculinux-bin" in release
def test_the_gate_builds_the_bin_package_too() -> None:
"""The -bin package needs build validation as much as the source one:
the gate stubs the release AppImage and runs makepkg on it."""
script = GATE_SH.read_text(encoding="utf-8")
assert "vocalinux-bin" in script
assert "voculinux-bin" in script
assert "--skipchecksums" in script