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
+1 -1
View File
@@ -130,7 +130,7 @@ if [ "$status" -ne 124 ] && [ "$status" -ne 0 ] && [ "$status" -ne 143 ]; then
fail "the app exited with $status instead of running"
fi
grep -q "Initializing Vocalinux" "$LOG" || {
grep -q "Initializing Voculinux" "$LOG" || {
tail -n 30 "$LOG" >&2
fail "startup never reached component initialisation"
}
+25 -25
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Build a relocatable AppImage for Vocalinux, natively for whichever
# Build a relocatable AppImage for Voculinux, natively for whichever
# architecture this script runs on (x86_64 or aarch64) - no cross-compiling.
#
# Usage: build.sh <path-to-wheel> <version> [output-dir]
@@ -26,10 +26,10 @@
# pywhispercpp from source with GGML_VULKAN=1 when Vulkan headers and a
# shader compiler are on the build host. The AppImage uses the host Vulkan
# loader/ICDs at runtime (do not bundle NVIDIA/AMD driver ICDs).
# Set VOCALINUX_APPIMAGE_REQUIRE_VULKAN=1 in CI so a failed rebuild fails
# Set VOCULINUX_APPIMAGE_REQUIRE_VULKAN=1 in CI so a failed rebuild fails
# the job instead of shipping a CPU-only image. Set
# VOCALINUX_APPIMAGE_SKIP_VULKAN=1 to force the CPU wheel.
# A build that produced libggml-vulkan.so is kept in VOCALINUX_APPIMAGE_CACHE
# VOCULINUX_APPIMAGE_SKIP_VULKAN=1 to force the CPU wheel.
# A build that produced libggml-vulkan.so is kept in VOCULINUX_APPIMAGE_CACHE
# and reused while the base image, tool_checksums.txt, and the pywhispercpp
# pin are unchanged. A CPU-only build is not cached.
set -euo pipefail
@@ -113,7 +113,7 @@ checkout_pinned() {
git -C "$dest" checkout -q FETCH_HEAD
}
# Seed typelibs Vocalinux imports directly, plus transitive GIR deps that Gtk
# Seed typelibs Voculinux imports directly, plus transitive GIR deps that Gtk
# and AppIndicator require (xlib, Dbusmenu, …). Without those, GI falls back to
# the builder's baked-in path (/usr/lib/x86_64-linux-gnu/girepository-1.0), which
# does not exist on Fedora/openSUSE/Arch — startup then reports "missing GTK3".
@@ -241,7 +241,7 @@ uv_install() {
"$UV" pip install --python "$PY_BIN" --prefix "$APPDIR/usr" "$@"
}
echo "== Installing Vocalinux + runtime deps into the bundle =="
echo "== Installing Voculinux + runtime deps into the bundle =="
# From the lock, not from whatever PyPI serves today. vad.txt is a superset of
# runtime.txt (a test keeps it that way, so this is one resolution rather than
# two); appimage.txt adds the PyGObject the exports leave out. The wheel is last
@@ -253,15 +253,15 @@ uv_install --require-hashes --no-deps -r "$REPO_ROOT/requirements/appimage.txt"
uv_install --no-deps "$WHEEL"
echo "== Adding desktop entry + icon =="
install -Dm644 "$REPO_ROOT/vocalinux.desktop" "$APPDIR/usr/share/applications/vocalinux.desktop"
install -Dm644 "$REPO_ROOT/voculinux.desktop" "$APPDIR/usr/share/applications/voculinux.desktop"
# AppImage desktop integration expects Exec=AppRun; set WM class for the tray app.
sed -i \
-e 's|^Exec=.*|Exec=AppRun|' \
-e '/^StartupWMClass=/d' \
"$APPDIR/usr/share/applications/vocalinux.desktop"
printf 'StartupWMClass=vocalinux\n' >> "$APPDIR/usr/share/applications/vocalinux.desktop"
install -Dm644 "$REPO_ROOT/resources/icons/scalable/vocalinux.svg" \
"$APPDIR/usr/share/icons/hicolor/scalable/apps/vocalinux.svg"
"$APPDIR/usr/share/applications/voculinux.desktop"
printf 'StartupWMClass=voculinux\n' >> "$APPDIR/usr/share/applications/voculinux.desktop"
install -Dm644 "$REPO_ROOT/resources/icons/scalable/voculinux.svg" \
"$APPDIR/usr/share/icons/hicolor/scalable/apps/voculinux.svg"
copy_typelibs() {
local dest="$1"
@@ -363,7 +363,7 @@ ensure_build_tools() {
# ggml demands glslc (`find_package(Vulkan COMPONENTS glslc REQUIRED)`) and the
# base image packages none, so build it from the pinned shaderc commit — ~4 min
# on a runner, and CI caches it in VOCALINUX_APPIMAGE_CACHE since the commit is
# on a runner, and CI caches it in VOCULINUX_APPIMAGE_CACHE since the commit is
# its only input.
ensure_glslc() {
if command -v glslc >/dev/null 2>&1; then
@@ -372,7 +372,7 @@ ensure_glslc() {
fi
local commit cache_dir src
commit="$(pin_field shaderc 3)"
cache_dir="${VOCALINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/vocalinux-appimage}/glslc-${commit}-${ARCH}"
cache_dir="${VOCULINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/voculinux-appimage}/glslc-${commit}-${ARCH}"
if [ ! -x "$cache_dir/glslc" ]; then
echo "== Building glslc from shaderc ${commit:0:12} (this image packages none) =="
ensure_build_tools
@@ -486,7 +486,7 @@ _compile_pywhispercpp_vulkan() {
if ! has_vulkan_build_deps; then
echo "Vulkan build deps missing (libvulkan-dev plus a C++ compiler)." >&2
if [ "$require_vulkan" = "1" ]; then
echo "VOCALINUX_APPIMAGE_REQUIRE_VULKAN=1: refusing to ship a CPU-only AppImage." >&2
echo "VOCULINUX_APPIMAGE_REQUIRE_VULKAN=1: refusing to ship a CPU-only AppImage." >&2
exit 1
fi
echo "Warning: AppImage will use a CPU-only baseline pywhispercpp build." >&2
@@ -547,13 +547,13 @@ _compile_pywhispercpp_vulkan() {
}
rebuild_pywhispercpp_vulkan() {
local require_vulkan="${VOCALINUX_APPIMAGE_REQUIRE_VULKAN:-0}"
if [ "${VOCALINUX_APPIMAGE_SKIP_VULKAN:-0}" = "1" ]; then
echo "== Skipping pywhispercpp Vulkan rebuild (VOCALINUX_APPIMAGE_SKIP_VULKAN=1) =="
local require_vulkan="${VOCULINUX_APPIMAGE_REQUIRE_VULKAN:-0}"
if [ "${VOCULINUX_APPIMAGE_SKIP_VULKAN:-0}" = "1" ]; then
echo "== Skipping pywhispercpp Vulkan rebuild (VOCULINUX_APPIMAGE_SKIP_VULKAN=1) =="
return 0
fi
# The Vocalinux wheel was installed above and is not part of this cache.
# The Voculinux wheel was installed above and is not part of this cache.
# A hit still replaces the CPU pywhispercpp wheel the lock just installed,
# and it does not run _compile_pywhispercpp_vulkan.
local cache_dir site
@@ -593,8 +593,8 @@ export DEPLOY_GTK_VERSION=3
"$TOOLDIR/linuxdeploy" --appdir "$APPDIR" \
--plugin gtk \
-e "$APPDIR/usr/bin/python3" \
-d "$APPDIR/usr/share/applications/vocalinux.desktop" \
-i "$APPDIR/usr/share/icons/hicolor/scalable/apps/vocalinux.svg"
-d "$APPDIR/usr/share/applications/voculinux.desktop" \
-i "$APPDIR/usr/share/icons/hicolor/scalable/apps/voculinux.svg"
# The plugin can change the bundle after PyGObject was installed. Check the
# package and the version-agnostic Python path that AppRun and the GI smoke use
@@ -689,14 +689,14 @@ GTK_HOOK="$APPDIR/apprun-hooks/linuxdeploy-plugin-gtk.sh"
if [ -f "$GTK_HOOK" ]; then
# Upstream forces GDK_BACKEND=x11 even on Wayland, which breaks hover/focus
# for GTK widgets under Plasma (XWayland). Prefer native Wayland unless the
# user opts into X11 via VOCALINUX_FORCE_X11=1.
# user opts into X11 via VOCULINUX_FORCE_X11=1.
"$APPDIR/usr/bin/python3" - "$GTK_HOOK" <<'PY'
import pathlib, re, sys
path = pathlib.Path(sys.argv[1])
text = path.read_text()
replacement = (
'# Prefer Wayland when available; force X11 only when requested.\n'
'if [ "${VOCALINUX_FORCE_X11:-0}" = "1" ]; then\n'
'if [ "${VOCULINUX_FORCE_X11:-0}" = "1" ]; then\n'
' export GDK_BACKEND=x11\n'
'elif [ -n "${WAYLAND_DISPLAY:-}" ] || [ "${XDG_SESSION_TYPE:-}" = "wayland" ]; then\n'
' export GDK_BACKEND=wayland\n'
@@ -727,13 +727,13 @@ smoke_gi_imports() {
local appdir="$1"
unshare --user --mount --map-root-user bash -c "
set -euo pipefail
mkdir -p /tmp/vocalinux-empty-gi
mkdir -p /tmp/voculinux-empty-gi
for d in /usr/lib/x86_64-linux-gnu/girepository-1.0 \
/usr/lib/aarch64-linux-gnu/girepository-1.0 \
/usr/lib/girepository-1.0 \
/usr/lib64/girepository-1.0; do
if [ -d \"\$d\" ]; then
mount --bind /tmp/vocalinux-empty-gi \"\$d\"
mount --bind /tmp/voculinux-empty-gi \"\$d\"
fi
done
export PYTHONHOME='$appdir/usr'
@@ -797,6 +797,6 @@ else
fi
echo "== Packaging AppImage =="
OUTPUT="$OUTDIR/Vocalinux-${VERSION}-${ARCH}.AppImage"
OUTPUT="$OUTDIR/Voculinux-${VERSION}-${ARCH}.AppImage"
ARCH="$ARCH" "$TOOLDIR/appimagetool" "$APPDIR" "$OUTPUT"
echo "Built $OUTPUT"
+2 -2
View File
@@ -6,7 +6,7 @@
# tool_checksums.txt. This script is the part that runs inside that image; the
# way in is docker-build.sh, which both CI and `just appimage` call:
#
# bash packaging/appimage/docker-build.sh dist/vocalinux-0.16.0-py3-none-any.whl 0.16.0 dist
# bash packaging/appimage/docker-build.sh dist/voculinux-0.16.0-py3-none-any.whl 0.16.0 dist
#
# Only the interpreter and the Python packages are pinned by digest; these apt
# packages are whatever the pinned image resolves to. That is the trade the base
@@ -34,5 +34,5 @@ bash "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/build.sh" "$@"
# results back to whoever started it (docker-build.sh passes its own ids).
if [ -n "${HOST_UID:-}" ]; then
chown -R "${HOST_UID}:${HOST_GID:-$HOST_UID}" "${3:-dist}" \
"${VOCALINUX_APPIMAGE_CACHE:-/cache}" 2>/dev/null || true
"${VOCULINUX_APPIMAGE_CACHE:-/cache}" 2>/dev/null || true
fi
+7 -7
View File
@@ -8,9 +8,9 @@
# the AppImage links against, and building on the host instead means shipping
# whatever that host happens to run.
#
# Honours VOCALINUX_APPIMAGE_REQUIRE_VULKAN / _SKIP_VULKAN like build.sh, and
# keeps glslc and the pywhispercpp Vulkan build in VOCALINUX_APPIMAGE_CACHE
# (~/.cache/vocalinux-appimage by default) so the next build reuses them.
# Honours VOCULINUX_APPIMAGE_REQUIRE_VULKAN / _SKIP_VULKAN like build.sh, and
# keeps glslc and the pywhispercpp Vulkan build in VOCULINUX_APPIMAGE_CACHE
# (~/.cache/voculinux-appimage by default) so the next build reuses them.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/../.." && pwd)"
@@ -20,7 +20,7 @@ if [ -z "$IMAGE" ]; then
exit 1
fi
CACHE="${VOCALINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/vocalinux-appimage}"
CACHE="${VOCULINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/voculinux-appimage}"
mkdir -p "$CACHE"
# The wheel and output dir are given as host paths; the repo is mounted at the
@@ -31,9 +31,9 @@ exec docker run --rm \
--security-opt seccomp=unconfined \
-v "$REPO_ROOT:$REPO_ROOT" -w "$REPO_ROOT" \
-v "$CACHE:/cache" \
-e VOCALINUX_APPIMAGE_CACHE=/cache \
-e VOCALINUX_APPIMAGE_REQUIRE_VULKAN \
-e VOCALINUX_APPIMAGE_SKIP_VULKAN \
-e VOCULINUX_APPIMAGE_CACHE=/cache \
-e VOCULINUX_APPIMAGE_REQUIRE_VULKAN \
-e VOCULINUX_APPIMAGE_SKIP_VULKAN \
-e CMAKE_BUILD_PARALLEL_LEVEL \
-e HOST_UID="$(id -u)" \
-e HOST_GID="$(id -g)" \
+5 -5
View File
@@ -5,16 +5,16 @@
# from it), every pin in tool_checksums.txt, the cmake/ninja pins in
# requirements/appimage-tools.txt, the Vulkan flags in build.sh, and the
# pywhispercpp sdist install.sh names. It does not depend on the rest of the
# Vocalinux sources, so this id ignores the git commit and the Actions run id
# Voculinux sources, so this id ignores the git commit and the Actions run id
# on purpose: putting either in the cache key would rebuild on every push.
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo="$(cd "$here/../.." && pwd)"
pins="${VOCALINUX_PINS:-$repo/packaging/appimage/tool_checksums.txt}"
install_sh="${VOCALINUX_INSTALL_SH:-$repo/install.sh}"
tools="${VOCALINUX_APPIMAGE_TOOLS:-$repo/requirements/appimage-tools.txt}"
build_sh="${VOCALINUX_BUILD_SH:-$repo/packaging/appimage/build.sh}"
pins="${VOCULINUX_PINS:-$repo/packaging/appimage/tool_checksums.txt}"
install_sh="${VOCULINUX_INSTALL_SH:-$repo/install.sh}"
tools="${VOCULINUX_APPIMAGE_TOOLS:-$repo/requirements/appimage-tools.txt}"
build_sh="${VOCULINUX_BUILD_SH:-$repo/packaging/appimage/build.sh}"
base="$(awk '$1=="base-image" {print $3; exit}' "$pins")"
if [ -z "$base" ]; then
+2 -2
View File
@@ -12,7 +12,7 @@ _NATIVE_CACHE_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
pywhispercpp_native_cache_dir() {
local arch="$1"
local root id
root="${VOCALINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/vocalinux-appimage}"
root="${VOCULINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/voculinux-appimage}"
id="$(bash "$_NATIVE_CACHE_HERE/native-cache-id.sh")"
printf '%s\n' "$root/pywhispercpp-vulkan-${arch}-${id}"
}
@@ -26,7 +26,7 @@ pywhispercpp_cache_has_vulkan() {
}
# Copy the pywhispercpp install out of a site-packages directory.
# Leaves the rest of the prefix (Vocalinux, numpy, …) where it is.
# Leaves the rest of the prefix (Voculinux, numpy, …) where it is.
pywhispercpp_cache_publish() {
local site="$1" dest="$2"
local parent staging pattern backup