* chore(build): migrate justfile tooling to uv All recipes now run through 'uv run' against .venv, with a shared DEV_EXTRAS variable (dev + vad) because uv syncs the environment exactly and would otherwise uninstall the dev tools between recipes. 'deps' installs dev+vad; new 'deps-all' covers every extra. 'build' switches to 'uv build'; 'version' uses --no-sync. Bump [tool.uv] required-version to >=0.12,<0.13 for uv 0.12.x, and raise the mypy python_version target to 3.12: mypy 1.19 in the lock cannot target 3.9 and numpy 2.x stubs need a 3.12 target to parse (mypy is not run in CI or pre-commit; this only affects 'just typecheck'). * fix(tests): isolate test runs from real XDG state; fix Arch model-path crash Local test runs failed en masse (185 failures) while CI stayed green: tests picked up the developer's real ~/.local/share/vocalinux models via the default XDG paths and switched IBus/keyboard code paths through the desktop session env (XDG_CURRENT_DESKTOP/ XDG_SESSION_TYPE on GNOME/Wayland, issue #478 branches). conftest now points XDG base dirs at a throwaway session-wide directory and clears session variables at import time, mirroring a fresh CI runner (paths like MODELS_DIR freeze at first import during collection, so fixture-time isolation is too late). Also guard the Arch branch of _get_system_model_paths(): it removed /usr/local/share/vocalinux/models unconditionally and crashed with ValueError on Arch hosts when a custom XDG_DATA_DIRS never added that path. Add regression tests, and make the lock-file test assert the actual $XDG_DATA_HOME/vocalinux contract instead of the hardcoded ~/.local/share default. * fix(tests): keep the IBus engine double a plain class Python 3.14's unittest.mock re-instantiates a user subclass when it builds child mocks (klass(name=...)). VocalinuxEngine inherits from whatever IBus.Engine resolves to, and the test module bound that to the MagicMock class itself, so every engine instantiation raised TypeError: VocalinuxEngine.__init__() got an unexpected keyword argument 'name' on 3.14 while passing on 3.13. Replace it with a plain stand-in class that still hands out per-instance MagicMocks for the IBus API surface the engine touches. The same pattern remains in test_ibus_engine.py, test_ibus_engine_utils.py, test_ibus_active_engine_guard.py and test_ibus_xkb_wayland.py; those never instantiate the engine, so they are unaffected for now. * fix(install): build the venv from the system Python install.sh looked up a bare `python3` from PATH. Started from an activated virtualenv — a shell left in uv's .venv after `just deps` — it built venv/ from that interpreter, where distro PyGObject is invisible even with --system-site-packages, and the run aborted with Distro PyGObject (python3-gi / python3-gobject) is not importable in the venv. A mismatched venv/ then survived every retry, because the installer reuses whatever venv it finds. - drop an inherited VIRTUAL_ENV (and its bin from PATH) before any interpreter lookup - select the interpreter explicitly: $SYSTEM_PYTHON (default /usr/bin/python3) or PATH python3, preferring one that can import gi, and create the venv with it instead of `python3` - recreate venv/ when it was built by a different interpreter - name the interpreter in the PyGObject guard's error Documents the two environments (uv's .venv for tooling, venv/ for the installed app) in CONTRIBUTING, the justfile header and AGENTS.md, and adds the mismatch symptom to the INSTALL troubleshooting section. * chore(python): raise the supported floor to 3.11 requires-python promised 3.9 while the CI matrix ran 3.9/3.10/3.11/3.13, the classifiers advertised up to 3.14, mypy targeted 3.12 and install.sh enforced 3.9 — five different answers to "which Python does this support". Settle on 3.11 everywhere: pyproject (requires-python, classifiers, black targets), install.sh's MIN_VERSION, check-system-deps.sh, the distro package map, the CI matrix (now 3.11-3.14, matching the classifiers), the release and nightly workflows, and the docs. mypy now targets the floor instead of 3.12, so type checking covers what we promise; numpy 2.x ships stubs in 3.12 syntax that a 3.11 target cannot parse, so its imports are skipped. (mypy has 71 pre-existing errors either way; no CI job gates on it.) uv.lock and the requirements/* exports are regenerated — dropping 3.9/3.10 removes most of the version-conditional resolution branches. tests/test_python_version_policy.py pins the invariant: install.sh's floor, requires-python, the classifiers and the CI matrix must agree. * ci: run lint and tests from uv.lock The lock file and the hash-pinned exports were not enforced anywhere: CI still built a venv by hand and installed unpinned `PyGObject` plus `-e ".[dev,vad]"`, so the tool versions it checked against had nothing to do with the ones `just lint` uses locally. Both Python jobs now use astral-sh/setup-uv and run through `uv sync --locked` / `uv run --locked`, which also fails the build when uv.lock drifts from pyproject.toml. Partially covers phase 5 of #701 (tests install from the lock, lock freshness gate); trusted publishing, artifact checksums and action SHA pinning are still open there. * ci: lint from a dedicated dependency group The lint job failed on this branch: `uv run --extra dev` syncs the whole project, so it tried to build pyaudio from source on a runner that installs no system dependencies. src/pyaudio/device_api.c:9:10: fatal error: portaudio.h: No such file or directory Move black/isort/flake8 out of the `dev` extra into a `lint` dependency group. CI runs `uv run --locked --only-group lint`, which installs the three pure-Python tools and skips the project entirely — no portaudio or girepository headers needed. `just deps` and DEV_EXTRAS request the group alongside the dev extras, so the local environment still carries test and lint tooling at once and `just lint` matches CI version for version. pre-commit is unaffected: its hooks pin their own tool versions upstream. * fix(install): never configure an engine the venv cannot import vosk became an optional extra in #705, but the installer still writes `"engine": "vosk"` into config.json on the whisper.cpp fallback path even when installing the extra only produced a warning. config.json also survives reinstalls — every writer guards on "file does not exist" — so an engine picked by an earlier attempt outlives the venv that supported it. Either way Vocalinux dies at startup: ModuleNotFoundError: No module named 'vosk' - the fallback now writes the vosk config only when `import vosk` actually succeeds, and otherwise leaves the engine configuration alone - a new verify_configured_engine() runs near the end of the install: if config.json selects vosk and the venv cannot import it, it says so and prints both fixes instead of leaving a broken install behind * feat(models): verify every model download against a pinned digest Pins a sha256/md5 per downloadable speech model in src/vocalinux/utils/model_checksums.txt and verifies against it in both install.sh and the runtime downloaders, before the file is moved into place. Verification fails closed. Covers phase 2.5 of #701; see the PR for details. * docs(agents): rebase AGENTS.md onto the #727 rewrite * fix(install): hash models that are already on disk instead of trusting them * refactor(models): one download path per engine, and fix Whisper's * docs(install): drop distros that cannot meet the 3.11 floor * docs: retire the distros and Python versions the 3.11 floor rules out * fix(install): abort instead of warning when Python is below the floor * fix(install): fall back to the system Python, not the one on PATH * fix(install): match an existing venv by installation, not by X.Y * fix(install): repair a config naming an engine the venv cannot import * fix(models): pin VOSK by the sha256 of bytes we fetched, not upstream's md5 * fix(justfile): request the dev set in every recipe that syncs * test(version): scan the docs and website trees for a stale Python floor * docs: correct the stale model-checksum and distro claims * fix(models): stamp the VOSK tree the app extracts, as the installer does * fix(install): replace an unverified VOSK model only once its successor is ready * docs(install): align the Fedora floor with the rest of the tree * test(install): drop an f-string with no placeholders * test(models): stub the downloader probe the VOSK installer test relies on * docs: drop the long dashes from two model-checksum comments * fix(install): gate on the interpreter, not on the distro release label * fix(justfile): export the lint group into requirements/dev.txt * fix(models): unpack the VOSK tree into staging and stamp it before the swap * fix(whisper): adopt a checkpoint an earlier release stored under the catalog name * docs(settings): state what a stray Whisper copy really means for deletion * fix(models): hash a whisper.cpp model found on disk before it reaches ctypes * docs(contributing): stop telling readers to uninstall their linters * fix(install): stop warning off distros the project documents as supported * docs(distros): file the supported derivatives under supported
257 lines
8.0 KiB
YAML
257 lines
8.0 KiB
YAML
# Package name mapping for different Linux distributions
|
|
# This file documents the package names used by the installer for each distribution
|
|
# It serves as a reference for manual installation and for adding support for new distributions
|
|
|
|
# System packages required for Vocalinux
|
|
system_packages:
|
|
# GTK3 and PyGObject
|
|
gtk3:
|
|
ubuntu: ["python3-gi", "python3-gi-cairo", "gir1.2-gtk-3.0", "gir1.2-gdkpixbuf-2.0"]
|
|
debian: ["python3-gi", "python3-gi-cairo", "gir1.2-gtk-3.0", "gir1.2-gdkpixbuf-2.0", "libcairo2-dev"]
|
|
fedora: ["python3-gobject", "gtk3", "gobject-introspection-devel"]
|
|
arch: ["python-gobject", "gtk3", "gobject-introspection", "python-cairo"]
|
|
# Tumbleweed often uses versioned Python package names such as python313-gobject.
|
|
# NOTE: "python3XY" is a placeholder convention. The XY suffix is resolved at
|
|
# install time by install.sh via suse_python_package_prefix() and
|
|
# suse_python_package_candidates(), which try the active Python minor version
|
|
# first (e.g. python313-gobject) and fall back to the unversioned name.
|
|
opensuse: ["python3XY-gobject", "python3XY-gobject-cairo", "gtk3", "gobject-introspection-devel"]
|
|
gentoo: ["dev-python/pygobject:3", "x11-libs/gtk+:3"]
|
|
alpine: ["py3-gobject3", "py3-cairo", "gtk+3.0"]
|
|
void: ["python3-gobject", "gtk+3"]
|
|
solus: ["python3-gobject", "gtk3"]
|
|
|
|
# AppIndicator / Ayatana AppIndicator
|
|
# Prefer Ayatana: legacy Canonical AppIndicator can load without error but
|
|
# fail to register a StatusNotifierItem on modern KDE Plasma.
|
|
appindicator:
|
|
ubuntu: ["gir1.2-appindicator3-0.1"]
|
|
debian_11: ["gir1.2-ayatanaappindicator3-0.1"]
|
|
debian_12_plus: ["gir1.2-ayatanaappindicator3-0.1"]
|
|
fedora: ["libayatana-appindicator-gtk3"]
|
|
arch: ["libayatana-appindicator"]
|
|
opensuse: ["typelib-1_0-AyatanaAppIndicator3-0_1", "libayatana-appindicator3-1", "typelib-1_0-AppIndicator3-0_1"]
|
|
gentoo: ["dev-libs/libayatana-appindicator"]
|
|
alpine: ["libayatana-appindicator"]
|
|
void: ["libappindicator"]
|
|
solus: ["libappindicator"]
|
|
|
|
# PortAudio
|
|
portaudio:
|
|
ubuntu: ["portaudio19-dev"]
|
|
debian: ["portaudio19-dev"]
|
|
fedora: ["portaudio-devel"]
|
|
arch: ["portaudio"]
|
|
opensuse: ["portaudio-devel"]
|
|
gentoo: ["media-libs/portaudio"]
|
|
alpine: ["portaudio-dev"]
|
|
void: ["portaudio-devel"]
|
|
solus: ["portaudio-devel"]
|
|
|
|
# Python development
|
|
python_dev:
|
|
ubuntu: ["python3-dev", "python3-venv"]
|
|
debian: ["python3-dev", "python3-venv"]
|
|
fedora: ["python3-devel", "python3-virtualenv"]
|
|
arch: ["python"] # Arch includes venv by default
|
|
# Tumbleweed package names follow the active Python minor version, e.g. python313-devel.
|
|
# NOTE: "python3XY" is a placeholder — see gtk3.opensuse comment above for details.
|
|
opensuse: ["python3XY-devel", "python3XY-virtualenv", "python3XY-venv", "python3XY-pip"]
|
|
gentoo: ["dev-lang/python:3.11"]
|
|
alpine: ["py3-virtualenv"]
|
|
void: ["python3-devel"]
|
|
solus: ["python3-virtualenv"]
|
|
|
|
# Package build tools
|
|
build_tools:
|
|
ubuntu: ["pkg-config", "wget", "curl", "unzip"]
|
|
debian: ["pkg-config", "wget", "curl", "unzip"]
|
|
fedora: ["pkg-config", "wget", "curl", "unzip"]
|
|
arch: ["pkgconf", "wget", "curl", "unzip"]
|
|
opensuse: ["pkg-config", "wget", "curl", "unzip"]
|
|
gentoo: ["dev-util/pkgconfig", "net-misc/wget", "net-misc/curl", "app-arch/unzip"]
|
|
alpine: ["pkgconf", "wget", "curl", "unzip"]
|
|
void: ["pkg-config", "wget", "curl", "unzip"]
|
|
solus: ["pkg-config", "wget", "curl", "unzip"]
|
|
|
|
# Text injection tools
|
|
xdotool:
|
|
ubuntu: ["xdotool"]
|
|
debian: ["xdotool"]
|
|
fedora: ["xdotool"]
|
|
arch: ["xdotool"]
|
|
opensuse: ["xdotool"]
|
|
gentoo: ["x11-misc/xdotool"]
|
|
alpine: ["xdotool"]
|
|
void: ["xdotool"]
|
|
solus: ["xdotool"]
|
|
|
|
# Clipboard tools used for layout-independent Wayland paste fallback
|
|
clipboard_tools:
|
|
ubuntu: ["wl-clipboard", "xclip", "xsel"]
|
|
debian: ["wl-clipboard", "xclip", "xsel"]
|
|
fedora: ["wl-clipboard", "xclip", "xsel"]
|
|
arch: ["wl-clipboard", "xclip", "xsel"]
|
|
opensuse: ["wl-clipboard", "xclip", "xsel"]
|
|
gentoo: ["gui-apps/wl-clipboard", "x11-misc/xclip", "x11-misc/xsel"]
|
|
alpine: ["wl-clipboard", "xclip", "xsel"]
|
|
void: ["wl-clipboard", "xclip", "xsel"]
|
|
solus: ["wl-clipboard", "xclip", "xsel"]
|
|
|
|
wtype:
|
|
ubuntu: ["wtype"]
|
|
debian: ["wtype"]
|
|
fedora: ["wtype"]
|
|
arch: ["wtype"]
|
|
opensuse: ["wtype"]
|
|
gentoo: ["x11-misc/wtype"]
|
|
alpine: ["wtype"]
|
|
void: ["wtype"]
|
|
solus: ["wtype"]
|
|
|
|
ydotool:
|
|
ubuntu: ["ydotool"]
|
|
debian: ["ydotool"]
|
|
fedora: ["ydotool"]
|
|
arch: ["ydotool"]
|
|
opensuse: ["ydotool"]
|
|
gentoo: ["app-misc/ydotool"]
|
|
alpine: ["ydotool"]
|
|
void: ["ydotool"]
|
|
solus: ["ydotool"]
|
|
|
|
# Command templates for package managers
|
|
install_commands:
|
|
apt: "sudo apt install -y {packages}"
|
|
apt_update: "sudo apt update"
|
|
|
|
dnf: "sudo dnf install -y {packages}"
|
|
dnf_update: "sudo dnf update -y" # Optional update
|
|
|
|
yum: "sudo yum install -y {packages}"
|
|
|
|
pacman: "sudo pacman -S --noconfirm {packages}"
|
|
pacman_update: "sudo pacman -Syu" # Optional update
|
|
|
|
zypper: "sudo zypper install -y {packages}"
|
|
zypper_update: "sudo zypper refresh" # Optional update
|
|
|
|
emerge: "sudo emerge {packages}"
|
|
|
|
apk: "sudo apk add {packages}"
|
|
apk_update: "sudo apk update" # Optional update
|
|
|
|
xbps: "sudo xbps-install -Sy {packages}"
|
|
xbps_update: "sudo xbps-install -Sy" # Updates sync during install
|
|
|
|
eopkg: "sudo eopkg install -y {packages}"
|
|
eopkg_update: "sudo eopkg upgrade" # Optional update
|
|
|
|
# Distribution family mappings
|
|
# This helps determine which package manager and package names to use
|
|
distribution_families:
|
|
# Ubuntu family
|
|
- id: ubuntu
|
|
family: ubuntu
|
|
package_manager: apt
|
|
variations: [pop, linuxmint, elementary, zorin]
|
|
|
|
# Debian family
|
|
- id: debian
|
|
family: debian
|
|
package_manager: apt
|
|
variations: []
|
|
|
|
# Fedora family
|
|
- id: fedora
|
|
family: fedora
|
|
package_manager: dnf
|
|
variations: [rhel, centos, rocky, almalinux]
|
|
|
|
# Arch family
|
|
- id: arch
|
|
family: arch
|
|
package_manager: pacman
|
|
variations: [manjaro, endeavouros]
|
|
|
|
# openSUSE family
|
|
- id: opensuse
|
|
family: suse
|
|
package_manager: zypper
|
|
variations: []
|
|
|
|
# Gentoo
|
|
- id: gentoo
|
|
family: gentoo
|
|
package_manager: emerge
|
|
variations: []
|
|
|
|
# Alpine
|
|
- id: alpine
|
|
family: alpine
|
|
package_manager: apk
|
|
variations: []
|
|
|
|
# Void Linux
|
|
- id: void
|
|
family: void
|
|
package_manager: xbps
|
|
variations: []
|
|
|
|
# Solus
|
|
- id: solus
|
|
family: solus
|
|
package_manager: eopkg
|
|
variations: []
|
|
|
|
# Mageia
|
|
- id: mageia
|
|
family: mageia
|
|
package_manager: dnf # Prefers dnf, falls back to urpmi
|
|
variations: []
|
|
|
|
# Notes for specific distributions
|
|
distribution_notes:
|
|
debian_11:
|
|
appindicator: "Use gir1.2-ayatanaappindicator3-0.1 instead of gir1.2-appindicator3-0.1"
|
|
|
|
gentoo:
|
|
general: "Gentoo compiles packages from source. Installation will take longer."
|
|
python: "Ensure Python 3.11+ is set as the active version"
|
|
|
|
alpine:
|
|
general: "Alpine uses musl libc. Some Python packages may not have pre-built wheels."
|
|
|
|
nixos:
|
|
general: "NixOS is not supported due to completely different filesystem layout."
|
|
|
|
void:
|
|
general: "Void Linux may use either glibc or musl libc. glibc version recommended."
|
|
|
|
# Architecture-specific library paths
|
|
library_paths:
|
|
typelib:
|
|
# Debian/Ubuntu multiarch paths
|
|
- /usr/lib/x86_64-linux-gnu/girepository-1.0
|
|
- /usr/lib/aarch64-linux-gnu/girepository-1.0
|
|
- /usr/lib/arm-linux-gnueabihf/girepository-1.0
|
|
- /usr/lib/riscv64-linux-gnu/girepository-1.0
|
|
- /usr/lib/powerpc64le-linux-gnu/girepository-1.0
|
|
- /usr/lib/s390x-linux-gnu/girepository-1.0
|
|
|
|
# Fedora/RHEL
|
|
- /usr/lib64/girepository-1.0
|
|
|
|
# Arch, generic
|
|
- /usr/lib/girepository-1.0
|
|
|
|
# Local installations
|
|
- /usr/local/lib/girepository-1.0
|
|
- /usr/local/lib64/girepository-1.0
|
|
|
|
models:
|
|
- /usr/local/share/vocalinux/models
|
|
- /usr/share/vocalinux/models
|
|
- /usr/lib64/vocalinux/models
|
|
- /usr/lib/vocalinux/models
|