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:
+22
-22
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build vocalinux_<version>_<arch>.deb and vocalinux-<version>.<arch>.rpm.
|
||||
# Build voculinux_<version>_<arch>.deb and voculinux-<version>.<arch>.rpm.
|
||||
#
|
||||
# Usage: build.sh <path-to-vocalinux-wheel> <version> [output-dir]
|
||||
# Usage: build.sh <path-to-voculinux-wheel> <version> [output-dir]
|
||||
# version release version, e.g. 0.18.0 or 0.19.0-beta
|
||||
#
|
||||
# CI and releases run this inside docker-build.sh's pinned Debian 12 image;
|
||||
@@ -63,18 +63,18 @@ NFPM="$TOOLS/nfpm"
|
||||
PAYLOAD="$TOOLS/payload"
|
||||
DOC="$TOOLS/pkg-doc"
|
||||
install -d \
|
||||
"$PAYLOAD/usr/lib/vocalinux/app" \
|
||||
"$PAYLOAD/usr/lib/vocalinux/vendor" \
|
||||
"$PAYLOAD/usr/lib/voculinux/app" \
|
||||
"$PAYLOAD/usr/lib/voculinux/vendor" \
|
||||
"$PAYLOAD/usr/bin" \
|
||||
"$PAYLOAD/usr/share/applications" \
|
||||
"$PAYLOAD/usr/share/icons/hicolor/scalable/apps" \
|
||||
"$DOC"
|
||||
|
||||
# --- application payload ------------------------------------------------
|
||||
# The wheel is a zip; -m zipfile lays out vocalinux/ and its dist-info
|
||||
# The wheel is a zip; -m zipfile lays out voculinux/ and its dist-info
|
||||
# exactly where pip --target would, without pip's EXTERNALLY-MANAGED refusal
|
||||
# on Debian 12.
|
||||
python3 -m zipfile -e "$WHEEL" "$PAYLOAD/usr/lib/vocalinux/app/"
|
||||
python3 -m zipfile -e "$WHEEL" "$PAYLOAD/usr/lib/voculinux/app/"
|
||||
|
||||
# --- vendored wheels -----------------------------------------------------
|
||||
# Download each supported CPython's pywhispercpp wheel — it has no abi3 tag,
|
||||
@@ -99,7 +99,7 @@ for PYV in $VENDOR_PYS; do
|
||||
--python-version "$PYV" -r "$PINS_VENDOR" -d "$TOOLS/wheels/$PYV" >/dev/null
|
||||
done
|
||||
for VENDOR_WHEEL in "$TOOLS"/wheels/*/*.whl; do
|
||||
python3 -m zipfile -e "$VENDOR_WHEEL" "$PAYLOAD/usr/lib/vocalinux/vendor/"
|
||||
python3 -m zipfile -e "$VENDOR_WHEEL" "$PAYLOAD/usr/lib/voculinux/vendor/"
|
||||
done
|
||||
|
||||
# --- portable libggml-cpu -------------------------------------------------
|
||||
@@ -144,7 +144,7 @@ swapped=0
|
||||
while IFS= read -r vendored; do
|
||||
cp "$BASELINE_CPU" "$vendored"
|
||||
swapped=$((swapped + 1))
|
||||
done < <(find "$PAYLOAD/usr/lib/vocalinux/vendor" -name 'libggml-cpu*.so*')
|
||||
done < <(find "$PAYLOAD/usr/lib/voculinux/vendor" -name 'libggml-cpu*.so*')
|
||||
[ "$swapped" -gt 0 ] || fail "found no vendored libggml-cpu to replace"
|
||||
# The baseline library's own NEEDED entries use the unmangled sonames
|
||||
# (libggml-base.so.0, libgomp.so.1), so alias the vendored copies — the same
|
||||
@@ -157,30 +157,30 @@ while IFS= read -r libs_dir; do
|
||||
vendored_gomp="$(find "$libs_dir" -name 'libgomp*.so*' | head -1)"
|
||||
[ -n "$vendored_gomp" ] || fail "no vendored libgomp in $libs_dir"
|
||||
cp "$vendored_gomp" "$libs_dir/libgomp.so.1"
|
||||
done < <(find "$PAYLOAD/usr/lib/vocalinux/vendor" -type d -name 'pywhispercpp.libs')
|
||||
done < <(find "$PAYLOAD/usr/lib/voculinux/vendor" -type d -name 'pywhispercpp.libs')
|
||||
|
||||
# --- launchers -----------------------------------------------------------
|
||||
# Same environment install.sh's wrapper establishes: the vendored
|
||||
# pywhispercpp.libs on LD_LIBRARY_PATH, user site-packages off so they cannot
|
||||
# shadow the distro modules, and sg input so evdev hotkeys work on Wayland
|
||||
# when the user is already in the input group but the session has not picked
|
||||
# up the membership yet. vocalinux-gui exists because the wheel declares a
|
||||
# up the membership yet. voculinux-gui exists because the wheel declares a
|
||||
# gui-script of that name; on Linux it runs the same entry.
|
||||
write_launcher() {
|
||||
local path="$1"
|
||||
cat > "$path" <<'LAUNCHER_EOF'
|
||||
#!/bin/sh
|
||||
# Launcher for the distro-packaged Vocalinux. The vendored wheels (the
|
||||
# Launcher for the distro-packaged Voculinux. The vendored wheels (the
|
||||
# pywhispercpp/pynput copies Debian and Fedora do not ship) live under
|
||||
# /usr/lib/vocalinux/vendor with the app itself under .../app; the loader in
|
||||
# /usr/lib/voculinux/vendor with the app itself under .../app; the loader in
|
||||
# utils/pywhispercpp_loader.py finds the bundled .so files from sys.path.
|
||||
PYTHONNOUSERSITE=1
|
||||
VOCALINUX_VENDOR="/usr/lib/vocalinux/vendor"
|
||||
if [ -d "$VOCALINUX_VENDOR/pywhispercpp.libs" ]; then
|
||||
LD_LIBRARY_PATH="$VOCALINUX_VENDOR/pywhispercpp.libs${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
VOCULINUX_VENDOR="/usr/lib/voculinux/vendor"
|
||||
if [ -d "$VOCULINUX_VENDOR/pywhispercpp.libs" ]; then
|
||||
LD_LIBRARY_PATH="$VOCULINUX_VENDOR/pywhispercpp.libs${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
export LD_LIBRARY_PATH
|
||||
fi
|
||||
export PYTHONNOUSERSITE PYTHONPATH="/usr/lib/vocalinux/app:$VOCALINUX_VENDOR${PYTHONPATH:+:$PYTHONPATH}"
|
||||
export PYTHONNOUSERSITE PYTHONPATH="/usr/lib/voculinux/app:$VOCULINUX_VENDOR${PYTHONPATH:+:$PYTHONPATH}"
|
||||
if grep -q "^input:.*\b$(whoami)\b" /etc/group 2>/dev/null \
|
||||
&& ! groups | grep -q "\binput\b" && command -v sg >/dev/null 2>&1; then
|
||||
# sg(1) takes a single command string, so each argument is re-quoted for
|
||||
@@ -196,14 +196,14 @@ exec /usr/bin/python3 -m vocalinux.main "$@"
|
||||
LAUNCHER_EOF
|
||||
chmod 755 "$path"
|
||||
}
|
||||
write_launcher "$PAYLOAD/usr/bin/vocalinux"
|
||||
write_launcher "$PAYLOAD/usr/bin/vocalinux-gui"
|
||||
write_launcher "$PAYLOAD/usr/bin/voculinux"
|
||||
write_launcher "$PAYLOAD/usr/bin/voculinux-gui"
|
||||
|
||||
# --- desktop entry, icon, license ----------------------------------------
|
||||
install -Dm644 "$REPO_ROOT/vocalinux.desktop" \
|
||||
"$PAYLOAD/usr/share/applications/vocalinux.desktop"
|
||||
install -Dm644 "$REPO_ROOT/resources/icons/scalable/vocalinux.svg" \
|
||||
"$PAYLOAD/usr/share/icons/hicolor/scalable/apps/vocalinux.svg"
|
||||
install -Dm644 "$REPO_ROOT/voculinux.desktop" \
|
||||
"$PAYLOAD/usr/share/applications/voculinux.desktop"
|
||||
install -Dm644 "$REPO_ROOT/resources/icons/scalable/voculinux.svg" \
|
||||
"$PAYLOAD/usr/share/icons/hicolor/scalable/apps/voculinux.svg"
|
||||
install -Dm644 "$REPO_ROOT/LICENSE" "$DOC/LICENSE"
|
||||
|
||||
# --- package -------------------------------------------------------------
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
# the gi typelibs come from the distribution, and so do every Python
|
||||
# dependency the distros package. Only pywhispercpp and pynput are vendored —
|
||||
# pywhispercpp is not packaged by Debian or Fedora at all, and pynput is
|
||||
# missing from Fedora — both under /usr/lib/vocalinux/vendor/, on sys.path via
|
||||
# the launcher in /usr/bin/vocalinux. Vendoring the pywhispercpp wheel per
|
||||
# missing from Fedora — both under /usr/lib/voculinux/vendor/, on sys.path via
|
||||
# the launcher in /usr/bin/voculinux. Vendoring the pywhispercpp wheel per
|
||||
# supported CPython is what keeps the package free of a bundled interpreter.
|
||||
#
|
||||
# Version: VOCA_VERSION is the tag without its leading v and without a
|
||||
@@ -17,7 +17,7 @@
|
||||
# nfpm maps a prerelease to 0.19.0~beta-1, which orders before the stable
|
||||
# 0.19.0-1 in both formats.
|
||||
|
||||
name: vocalinux
|
||||
name: voculinux
|
||||
arch: ${VOCA_ARCH}
|
||||
platform: linux
|
||||
version: ${VOCA_VERSION}
|
||||
@@ -31,12 +31,12 @@ description: |
|
||||
transcribe speech locally (whisper.cpp by default), and type into the
|
||||
focused window. GTK, PyGObject and the interpreter come from the
|
||||
distribution; pywhispercpp and pynput are vendored under
|
||||
/usr/lib/vocalinux/vendor because neither distro packages them.
|
||||
/usr/lib/voculinux/vendor because neither distro packages them.
|
||||
homepage: https://github.com/VocaHQ/vocalinux
|
||||
license: AGPL-3.0-only
|
||||
|
||||
contents:
|
||||
# The staged payload tree: /usr/lib/vocalinux/{app,vendor}, the launchers in
|
||||
# The staged payload tree: /usr/lib/voculinux/{app,vendor}, the launchers in
|
||||
# /usr/bin, the desktop file and the hicolor icon. build.sh writes it.
|
||||
- src: ./payload/
|
||||
dst: /
|
||||
@@ -45,10 +45,10 @@ contents:
|
||||
# License placement differs between the formats: Debian policy wants the
|
||||
# copyright file under /usr/share/doc, Fedora marks /usr/share/licenses.
|
||||
- src: ./pkg-doc/LICENSE
|
||||
dst: /usr/share/doc/vocalinux/copyright
|
||||
dst: /usr/share/doc/voculinux/copyright
|
||||
packager: deb
|
||||
- src: ./pkg-doc/LICENSE
|
||||
dst: /usr/share/licenses/vocalinux/LICENSE
|
||||
dst: /usr/share/licenses/voculinux/LICENSE
|
||||
packager: rpm
|
||||
|
||||
overrides:
|
||||
|
||||
@@ -52,17 +52,17 @@ echo "== installed: $PKG"
|
||||
# before check_dependencies() touches GTK, so it proves the wrapper, the
|
||||
# PYTHONPATH layout and the entry module's import chain — the dependency
|
||||
# typelibs are exercised by the import smoke below.
|
||||
vocalinux --version
|
||||
vocalinux-gui --version
|
||||
voculinux --version
|
||||
voculinux-gui --version
|
||||
|
||||
# Import smoke in the shape of the AUR gate: the app modules, the vendored
|
||||
# pywhispercpp wheel, and the distro gi/GTK typelibs, all through the same
|
||||
# interpreter and environment the launcher sets up. pynput is only checked
|
||||
# for presence: its __init__ selects a display backend and raises ImportError
|
||||
# with no DISPLAY, which is why the app guards it with PYNPUT_AVAILABLE.
|
||||
PYTHONPATH="/usr/lib/vocalinux/app:/usr/lib/vocalinux/vendor" \
|
||||
PYTHONPATH="/usr/lib/voculinux/app:/usr/lib/voculinux/vendor" \
|
||||
PYTHONNOUSERSITE=1 \
|
||||
LD_LIBRARY_PATH="/usr/lib/vocalinux/vendor/pywhispercpp.libs" \
|
||||
LD_LIBRARY_PATH="/usr/lib/voculinux/vendor/pywhispercpp.libs" \
|
||||
/usr/bin/python3 - <<'PY'
|
||||
import importlib
|
||||
import importlib.util
|
||||
@@ -83,11 +83,11 @@ for module in MODULES:
|
||||
importlib.import_module(module)
|
||||
|
||||
spec = importlib.util.find_spec("pynput")
|
||||
assert spec and spec.origin and spec.origin.startswith("/usr/lib/vocalinux/vendor/"), \
|
||||
assert spec and spec.origin and spec.origin.startswith("/usr/lib/voculinux/vendor/"), \
|
||||
f"vendored pynput not found on sys.path: {spec}"
|
||||
|
||||
tag = f"cpython-{sys.version_info.major}{sys.version_info.minor}-"
|
||||
exts = [p.name for p in Path("/usr/lib/vocalinux/vendor").glob("_pywhispercpp.*.so")]
|
||||
exts = [p.name for p in Path("/usr/lib/voculinux/vendor").glob("_pywhispercpp.*.so")]
|
||||
assert any(tag in name for name in exts), \
|
||||
f"no _pywhispercpp extension for {tag[:-1]} among {exts}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user