diff --git a/docs/DISTRO_COMPATIBILITY.md b/docs/DISTRO_COMPATIBILITY.md index ba9fbbf..1332621 100644 --- a/docs/DISTRO_COMPATIBILITY.md +++ b/docs/DISTRO_COMPATIBILITY.md @@ -104,11 +104,25 @@ sudo pacman -S --needed python-gobject gtk3 gobject-introspection \ ### openSUSE ```bash -sudo zypper install -y python3-gobject python3-gobject-cairo gtk3 \ - gobject-introspection-devel portaudio-devel python3-devel \ - python3-virtualenv pkg-config +PYVER=$(python3 -c 'import sys; print(f"python{sys.version_info.major}{sys.version_info.minor}")') + +sudo zypper install -y \ + "${PYVER}-pip" "${PYVER}-gobject" "${PYVER}-gobject-cairo" \ + "${PYVER}-devel" "${PYVER}-virtualenv" \ + gtk3 typelib-1_0-AyatanaAppIndicator3-0_1 libayatana-appindicator3-1 \ + typelib-1_0-Notify-0_7 libnotify4 \ + gobject-introspection-devel portaudio-devel pkg-config cmake wget curl unzip \ + xdotool wtype + +# Optional: only needed for whisper.cpp Vulkan GPU builds +sudo zypper install -y vulkan-tools vulkan-devel shaderc ``` +**Note:** openSUSE Tumbleweed often uses versioned Python package names such as +`python313-devel`. The `-devel` suffix means development headers for compiling +native dependencies; it does **not** mean beta or unstable packages. If +`${PYVER}-virtualenv` is unavailable on your snapshot, try `${PYVER}-venv`. + ### Gentoo ```bash sudo emerge dev-python/pygobject:3 x11-libs/gtk+:3 dev-libs/libappindicator:3 \ @@ -190,9 +204,12 @@ sudo pacman -S python-gobject gtk3 libappindicator gobject-introspection \ #### openSUSE ```bash -sudo zypper install python3-gobject python3-gobject-cairo gtk3 libappindicator \ - gobject-introspection-devel portaudio-devel python3-devel \ - python3-virtualenv pkg-config xdotool wtype +PYVER=$(python3 -c 'import sys; print(f"python{sys.version_info.major}{sys.version_info.minor}")') + +sudo zypper install "${PYVER}-gobject" "${PYVER}-gobject-cairo" gtk3 \ + typelib-1_0-AyatanaAppIndicator3-0_1 libayatana-appindicator3-1 \ + typelib-1_0-Notify-0_7 libnotify4 \ + portaudio-devel "${PYVER}-devel" "${PYVER}-virtualenv" pkg-config xdotool wtype ``` ### pip Installation Steps diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 54a3ad1..188c5ea 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -78,6 +78,25 @@ pip install vocalinux vocalinux ``` +**openSUSE Tumbleweed:** + +```bash +PYVER=$(python3 -c 'import sys; print(f"python{sys.version_info.major}{sys.version_info.minor}")') + +sudo zypper install -y \ + "${PYVER}-gobject" "${PYVER}-gobject-cairo" gtk3 \ + typelib-1_0-AyatanaAppIndicator3-0_1 libayatana-appindicator3-1 \ + typelib-1_0-Notify-0_7 libnotify4 \ + gobject-introspection-devel portaudio-devel "${PYVER}-devel" \ + "${PYVER}-virtualenv" pkg-config xdotool wtype + +python3 -m venv ~/.local/share/vocalinux-pypi/venv --system-site-packages +source ~/.local/share/vocalinux-pypi/venv/bin/activate +pip install --upgrade pip setuptools wheel +pip install vocalinux +vocalinux +``` + If `vocalinux` starts but reports a missing speech model, open Settings and download a model, or use the recommended installer which downloads the default model during setup. @@ -340,6 +359,26 @@ sudo pacman -S --noconfirm \ wget curl unzip xdotool ``` +**openSUSE Tumbleweed:** +```bash +PYVER=$(python3 -c 'import sys; print(f"python{sys.version_info.major}{sys.version_info.minor}")') + +sudo zypper install -y \ + "${PYVER}-pip" "${PYVER}-gobject" "${PYVER}-gobject-cairo" \ + "${PYVER}-devel" "${PYVER}-virtualenv" \ + gtk3 typelib-1_0-AyatanaAppIndicator3-0_1 libayatana-appindicator3-1 \ + typelib-1_0-Notify-0_7 libnotify4 \ + gobject-introspection-devel portaudio-devel pkg-config cmake \ + wget curl unzip xdotool wtype + +# Optional: only needed for whisper.cpp Vulkan GPU builds +sudo zypper install -y vulkan-tools vulkan-devel shaderc +``` + +On openSUSE, `-devel` packages are development headers for native Python +dependencies. They are not beta or unstable packages. If `${PYVER}-virtualenv` +is unavailable on your snapshot, try `${PYVER}-venv`. + ### 2. Create Virtual Environment ```bash diff --git a/install.sh b/install.sh index 606985e..15c7bbe 100755 --- a/install.sh +++ b/install.sh @@ -100,6 +100,7 @@ RUN_TESTS="no" DEV_MODE="no" VENV_DIR="venv" SKIP_MODELS="no" +SKIP_SYSTEM_DEPS="no" WITH_WHISPER="no" WHISPER_CPU="no" NO_WHISPER_EXPLICIT="no" @@ -151,6 +152,10 @@ while [[ $# -gt 0 ]]; do SKIP_MODELS="yes" shift ;; + --skip-system-deps) + SKIP_SYSTEM_DEPS="yes" + shift + ;; --rebuild-whispercpp) REBUILD_WHISPERCPP="yes" shift @@ -195,6 +200,8 @@ while [[ $# -gt 0 ]]; do echo " --test Run tests after installation" echo " --venv-dir=PATH Specify custom virtual environment directory" echo " --skip-models Skip downloading speech models during installation" + echo " --skip-system-deps" + echo " Skip package-manager dependency installation (advanced)" echo " --rebuild-whispercpp Rebuild/reinstall pywhispercpp even if already installed" echo " --no-rebuild-whispercpp Reuse existing pywhispercpp when present (auto-mode default)" echo " --tag=TAG Install specific release tag (default: latest release)" @@ -1159,6 +1166,106 @@ pacman_package_installed() { pacman -Q "$1" >/dev/null 2>&1 } +suse_python_package_prefix() { + python3 -c 'import sys; print(f"python{sys.version_info.major}{sys.version_info.minor}")' 2>/dev/null || echo "python3" +} + +suse_python_package_candidates() { + local suffix="$1" + local PY_PREFIX + PY_PREFIX=$(suse_python_package_prefix) + + if [[ "$PY_PREFIX" != "python3" ]]; then + echo "${PY_PREFIX}-${suffix} python3-${suffix}" + else + echo "python3-${suffix}" + fi +} + +suse_package_installed() { + rpm -q "$1" >/dev/null 2>&1 +} + +suse_install_first_available() { + local DESCRIPTION="$1" + shift + + local PKG + for PKG in "$@"; do + [ -z "$PKG" ] && continue + + if suse_package_installed "$PKG"; then + print_info "$DESCRIPTION is already installed ($PKG)." + return 0 + fi + + if sudo zypper install -y "$PKG" 2>/dev/null; then + print_success "Installed $DESCRIPTION ($PKG)." + return 0 + fi + + print_info "$DESCRIPTION package '$PKG' not available, trying next option..." + done + + return 1 +} + +suse_appindicator_gi_available() { + python3 - <<'PY' >/dev/null 2>&1 +import importlib +import gi + +for namespace in ("AppIndicator3", "AyatanaAppIndicator3", "AyatanaAppindicator3"): + try: + gi.require_version(namespace, "0.1") + importlib.import_module(f"gi.repository.{namespace}") + raise SystemExit(0) + except (ImportError, ValueError): + pass + +raise SystemExit(1) +PY +} + +suse_install_appindicator_runtime() { + local APPINDICATOR_PACKAGES=( + "typelib-1_0-AyatanaAppIndicator3-0_1" + "typelib-1_0-AppIndicator3-0_1" + "typelib-1_0-AyatanaAppIndicator-0_1" + "libayatana-appindicator3-1" + "libappindicator3-1" + "libappindicator-gtk3" + ) + + if suse_appindicator_gi_available; then + print_info "AppIndicator/Ayatana GI namespace is already available." + return 0 + fi + + local PKG + for PKG in "${APPINDICATOR_PACKAGES[@]}"; do + if suse_package_installed "$PKG"; then + print_info "AppIndicator/Ayatana package is already installed ($PKG); verifying GI namespace..." + elif sudo zypper install -y "$PKG" 2>/dev/null; then + print_success "Installed AppIndicator/Ayatana package ($PKG)." + else + print_info "AppIndicator/Ayatana package '$PKG' not available, trying next option..." + continue + fi + + if suse_appindicator_gi_available; then + print_success "AppIndicator/Ayatana GI namespace is available." + return 0 + fi + done + + return 1 +} + +suse_shader_compiler_available() { + command_exists glslc || command_exists glslangValidator +} + # Function to install system dependencies based on the detected distribution install_system_dependencies() { print_info "Installing system dependencies..." @@ -1185,7 +1292,7 @@ install_system_dependencies() { local APT_PACKAGES_DEBIAN_13_PLUS="$APT_PACKAGES_DEBIAN_BASE libgirepository-2.0-dev gir1.2-ayatanaappindicator3-0.1" local DNF_PACKAGES="python3-pip python3-gobject gtk3 libappindicator-gtk3 ibus-devel gobject-introspection-devel python3-devel portaudio-devel python3-virtualenv pkg-config cmake wget curl unzip vulkan-tools vulkan-loader-devel glslang xclip wl-clipboard" local PACMAN_PACKAGES="python-pip python-gobject gtk3 libappindicator-gtk3 ibus gobject-introspection python-cairo portaudio python-virtualenv pkg-config cmake wget curl unzip base-devel vulkan-tools vulkan-headers glslang xclip wl-clipboard" - local ZYPPER_PACKAGES="python3-pip python3-gobject python3-gobject-cairo gtk3 libappindicator-gtk3 ibus-devel gobject-introspection-devel python3-devel portaudio-devel python3-virtualenv pkg-config cmake wget curl unzip vulkan-tools vulkan-devel glslang xclip wl-clipboard" + local ZYPPER_PACKAGES="gtk3 ibus-devel gobject-introspection-devel portaudio-devel pkg-config cmake wget curl unzip xclip wl-clipboard typelib-1_0-Notify-0_7 libnotify4" # Gentoo uses Portage and different package naming convention local EMERGE_PACKAGES="dev-python/pygobject:3 x11-libs/gtk+:3 dev-libs/libayatana-appindicator media-libs/portaudio dev-lang/python:3.9 pkgconf cmake dev-util/glslang x11-misc/xclip gui-apps/wl-clipboard" # Alpine Linux uses apk and has musl libc @@ -1305,56 +1412,87 @@ install_system_dependencies() { exit 1 fi - zypper_package_installed() { - rpm -q "$1" >/dev/null 2>&1 - } + sudo zypper refresh || true + if [[ "${SELECTED_ENGINE:-whisper_cpp}" == "whisper_cpp" && "${WHISPERCPP_BACKEND:-}" != "cpu" ]]; then + ZYPPER_PACKAGES="$ZYPPER_PACKAGES vulkan-tools vulkan-devel" + fi + + local MISSING_ZYPPER_PACKAGES=() for pkg in $ZYPPER_PACKAGES; do - if ! zypper_package_installed "$pkg"; then - MISSING_PACKAGES="$MISSING_PACKAGES $pkg" + if ! suse_package_installed "$pkg"; then + MISSING_ZYPPER_PACKAGES+=("$pkg") fi done - if [ -n "$MISSING_PACKAGES" ]; then - print_info "Installing missing packages:$MISSING_PACKAGES" - sudo zypper refresh || true - - if echo "$MISSING_PACKAGES" | grep -qw "glslang"; then - if ! sudo zypper install -y glslang 2>/dev/null; then - print_info "glslang not found, trying glslang-devel..." - if ! sudo zypper install -y glslang-devel 2>/dev/null; then - print_warning "glslang not available - Vulkan shader compilation may not work" - print_warning "Install glslang manually for whisper.cpp GPU support" - fi - fi - FILTERED_PACKAGES=$(echo "$MISSING_PACKAGES" | sed 's/glslang//' | xargs) - else - FILTERED_PACKAGES="$MISSING_PACKAGES" - fi - - if echo "$FILTERED_PACKAGES" | grep -qw "libappindicator-gtk3"; then - if ! sudo zypper install -y libappindicator-gtk3 2>/dev/null; then - print_info "libappindicator-gtk3 not available, trying typelib-1_0-AppIndicator3-0_1..." - if ! sudo zypper install -y typelib-1_0-AppIndicator3-0_1 2>/dev/null; then - print_info "Trying libayatana-appindicator3-1..." - if sudo zypper install -y libayatana-appindicator3-1 2>/dev/null; then - print_success "Installed libayatana-appindicator3-1" - else - print_warning "No appindicator package found - system tray may not work" - fi - fi - fi - FILTERED_PACKAGES=$(echo "$FILTERED_PACKAGES" | sed 's/libappindicator-gtk3//' | xargs) - fi - - if [ -n "$FILTERED_PACKAGES" ]; then - sudo zypper install -y $FILTERED_PACKAGES || { - print_error "Failed to install dependencies" - exit 1 - } - fi + if [ "${#MISSING_ZYPPER_PACKAGES[@]}" -gt 0 ]; then + print_info "Installing missing packages: ${MISSING_ZYPPER_PACKAGES[*]}" + sudo zypper install -y "${MISSING_ZYPPER_PACKAGES[@]}" || { + print_error "Failed to install openSUSE base dependencies" + exit 1 + } else - print_info "All required packages are already installed." + print_info "All base openSUSE packages are already installed." + fi + + local PY_PIP_CANDIDATES=() + local PY_GOBJECT_CANDIDATES=() + local PY_GOBJECT_CAIRO_CANDIDATES=() + local PY_DEVEL_CANDIDATES=() + local PY_VIRTUALENV_CANDIDATES=() + local PY_VENV_CANDIDATES=() + + read -r -a PY_PIP_CANDIDATES <<< "$(suse_python_package_candidates "pip")" + read -r -a PY_GOBJECT_CANDIDATES <<< "$(suse_python_package_candidates "gobject")" + read -r -a PY_GOBJECT_CAIRO_CANDIDATES <<< "$(suse_python_package_candidates "gobject-cairo")" + read -r -a PY_DEVEL_CANDIDATES <<< "$(suse_python_package_candidates "devel")" + read -r -a PY_VIRTUALENV_CANDIDATES <<< "$(suse_python_package_candidates "virtualenv")" + read -r -a PY_VENV_CANDIDATES <<< "$(suse_python_package_candidates "venv")" + + print_info "Resolving openSUSE Python packages for $(suse_python_package_prefix)..." + + if ! suse_install_first_available "Python pip" "${PY_PIP_CANDIDATES[@]}"; then + print_error "Failed to install Python pip package (tried: ${PY_PIP_CANDIDATES[*]})" + exit 1 + fi + + if ! suse_install_first_available "PyGObject bindings" "${PY_GOBJECT_CANDIDATES[@]}"; then + print_error "Failed to install PyGObject package (tried: ${PY_GOBJECT_CANDIDATES[*]})" + exit 1 + fi + + if ! suse_install_first_available "PyGObject Cairo bindings" "${PY_GOBJECT_CAIRO_CANDIDATES[@]}"; then + print_error "Failed to install PyGObject Cairo package (tried: ${PY_GOBJECT_CAIRO_CANDIDATES[*]})" + exit 1 + fi + + if ! suse_install_first_available "Python development headers" "${PY_DEVEL_CANDIDATES[@]}"; then + print_error "Failed to install Python development headers (tried: ${PY_DEVEL_CANDIDATES[*]})" + exit 1 + fi + + if ! suse_install_first_available "Python virtualenv/venv" "${PY_VIRTUALENV_CANDIDATES[@]}" "${PY_VENV_CANDIDATES[@]}"; then + print_warning "Python virtualenv/venv package was not found (tried: ${PY_VIRTUALENV_CANDIDATES[*]} ${PY_VENV_CANDIDATES[*]})" + print_warning "Continuing because python3 -m venv may still be available." + fi + + if ! suse_install_appindicator_runtime; then + print_error "Failed to install a working AppIndicator/Ayatana GI runtime on openSUSE." + print_error "Try manually: sudo zypper install typelib-1_0-AyatanaAppIndicator3-0_1 libayatana-appindicator3-1" + exit 1 + fi + + if [[ "${SELECTED_ENGINE:-whisper_cpp}" == "whisper_cpp" && "${WHISPERCPP_BACKEND:-}" != "cpu" ]]; then + if ! suse_shader_compiler_available; then + if ! suse_install_first_available "Vulkan shader compiler" shaderc glslang-devel glslang; then + print_warning "No Vulkan shader compiler found - whisper.cpp Vulkan build may fail" + print_warning "Install shaderc manually for glslc support if you want GPU acceleration." + fi + fi + + if ! suse_shader_compiler_available; then + print_warning "glslc/glslangValidator is still unavailable; CPU fallback will be used if Vulkan build fails." + fi fi ;; @@ -1531,7 +1669,12 @@ install_system_dependencies() { } # Install system dependencies -install_system_dependencies +if [[ "$SKIP_SYSTEM_DEPS" == "yes" ]]; then + print_warning "Skipping system dependency installation (--skip-system-deps specified)." + print_warning "Make sure GTK, PyGObject, AppIndicator/Ayatana, PortAudio, and text input tools are installed." +else + install_system_dependencies +fi # Define XDG directories CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/vocalinux" @@ -1541,6 +1684,11 @@ ICON_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/scalable/apps" # Function to detect and install text input tools install_text_input_tools() { + if [[ "$SKIP_SYSTEM_DEPS" == "yes" ]]; then + print_warning "Skipping text input tool installation (--skip-system-deps specified)." + return 0 + fi + # Detect session type more robustly local SESSION_TYPE="unknown" @@ -1856,8 +2004,11 @@ setup_virtual_environment() { # Use --system-site-packages to access pre-compiled system packages like PyGObject # This avoids build failures with Python 3.13+ where PyGObject may not build from source python3 -m venv --system-site-packages "$VENV_DIR" || { - print_error "Failed to create virtual environment. Please check your Python installation." - exit 1 + print_warning "python3 -m venv failed, trying python3 -m virtualenv..." + python3 -m virtualenv --system-site-packages "$VENV_DIR" || { + print_error "Failed to create virtual environment. Please check your Python installation." + exit 1 + } } # Activate virtual environment @@ -2163,6 +2314,7 @@ install_python_package() { print_info " Ubuntu/Debian: sudo apt install libvulkan-dev vulkan-tools glslc || glslang-tools" print_info " Fedora: sudo dnf install vulkan-loader-devel vulkan-tools glslang" print_info " Arch: sudo pacman -S vulkan-headers vulkan-tools glslang" + print_info " openSUSE: sudo zypper install vulkan-devel vulkan-tools shaderc" elif [[ "$GPU_BACKEND" == "CUDA" ]]; then print_info " To use CUDA GPU acceleration, please install CUDA toolkit:" print_info " Visit: https://developer.nvidia.com/cuda-downloads" diff --git a/scripts/check-system-deps.sh b/scripts/check-system-deps.sh index 705f36d..1c77ef9 100755 --- a/scripts/check-system-deps.sh +++ b/scripts/check-system-deps.sh @@ -59,6 +59,28 @@ check_deps() { MISSING=1 fi + # Check for AppIndicator/Ayatana AppIndicator GI runtime + if python3 - <<'PY' >/dev/null 2>&1 +import importlib +import gi + +for namespace in ("AppIndicator3", "AyatanaAppIndicator3", "AyatanaAppindicator3"): + try: + gi.require_version(namespace, "0.1") + importlib.import_module(f"gi.repository.{namespace}") + raise SystemExit(0) + except (ImportError, ValueError): + pass + +raise SystemExit(1) +PY + then + echo "✓ AppIndicator/Ayatana GI runtime" + else + echo "✗ AppIndicator/Ayatana GI runtime not found" + MISSING=1 + fi + # Check for PortAudio if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists portaudio-2.0 2>/dev/null; then PA_VERSION=$(pkg-config --modversion portaudio-2.0 2>/dev/null) @@ -124,7 +146,9 @@ check_deps() { echo " sudo pacman -S --needed python-gobject gtk3 gobject-introspection portaudio python pkg-config xdotool wtype" elif [[ "$DETECTED_DISTRO" == "opensuse" || "$DETECTED_DISTRO_LIKE" == *"suse"* ]]; then echo "openSUSE:" - echo " sudo zypper install -y python3-gobject python3-gobject-cairo gtk3 gobject-introspection-devel portaudio-devel python3-devel python3-virtualenv pkg-config xdotool wtype" + echo " PYVER=\$(python3 -c 'import sys; print(f\"python{sys.version_info.major}{sys.version_info.minor}\")')" + echo " sudo zypper install -y \"\${PYVER}-gobject\" \"\${PYVER}-gobject-cairo\" gtk3 typelib-1_0-AyatanaAppIndicator3-0_1 libayatana-appindicator3-1 typelib-1_0-Notify-0_7 libnotify4 gobject-introspection-devel portaudio-devel \"\${PYVER}-devel\" \"\${PYVER}-virtualenv\" pkg-config xdotool wtype" + echo " If \"\${PYVER}-virtualenv\" is unavailable, try \"\${PYVER}-venv\"." elif [[ "$DETECTED_DISTRO" == "gentoo" ]]; then echo "Gentoo:" echo " sudo emerge dev-python/pygobject:3 x11-libs/gtk+:3 dev-libs/libappindicator:3 media-libs/portaudio dev-lang/python:3.9 xdotool wtype" diff --git a/scripts/distro-package-map.yaml b/scripts/distro-package-map.yaml index d909b62..ff4bfc9 100644 --- a/scripts/distro-package-map.yaml +++ b/scripts/distro-package-map.yaml @@ -10,7 +10,8 @@ system_packages: 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"] - opensuse: ["python3-gobject", "python3-gobject-cairo", "gtk3", "gobject-introspection-devel"] + # Tumbleweed often uses versioned Python package names such as python313-gobject. + 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"] @@ -23,7 +24,7 @@ system_packages: debian_12_plus: ["gir1.2-ayatanaappindicator3-0.1"] fedora: ["libappindicator-gtk3"] arch: ["libappindicator-gtk3"] - opensuse: ["libappindicator-gtk3"] + opensuse: ["typelib-1_0-AyatanaAppIndicator3-0_1", "libayatana-appindicator3-1", "typelib-1_0-AppIndicator3-0_1"] gentoo: ["dev-libs/libappindicator:3"] alpine: ["libayatana-appindicator"] void: ["libappindicator"] @@ -47,7 +48,8 @@ system_packages: debian: ["python3-dev", "python3-venv"] fedora: ["python3-devel", "python3-virtualenv"] arch: ["python"] # Arch includes venv by default - opensuse: ["python3-devel", "python3-virtualenv"] + # Tumbleweed package names follow the active Python minor version, e.g. python313-devel. + opensuse: ["python3XY-devel", "python3XY-virtualenv", "python3XY-venv", "python3XY-pip"] gentoo: ["dev-lang/python:3.9"] alpine: ["py3-virtualenv"] void: ["python3-devel"] diff --git a/src/vocalinux/main.py b/src/vocalinux/main.py index 06092fd..79d02a8 100644 --- a/src/vocalinux/main.py +++ b/src/vocalinux/main.py @@ -139,6 +139,17 @@ def check_dependencies(): logger.error(" Arch Linux:") logger.error(" sudo pacman -S python-gobject gtk3 libappindicator") logger.error("") + logger.error(" openSUSE Tumbleweed:") + logger.error( + " PYVER=$(python3 -c 'import sys; " + 'print(f"python{sys.version_info.major}{sys.version_info.minor}")\')' + ) + logger.error( + ' sudo zypper install "${PYVER}-gobject" gtk3 ' + "typelib-1_0-AyatanaAppIndicator3-0_1 " + "typelib-1_0-Notify-0_7 libnotify4" + ) + logger.error("") logger.error( "For pipx users: Install system packages BEFORE running 'pipx install vocalinux'" ) diff --git a/tests/test_cross_distro_compatibility.py b/tests/test_cross_distro_compatibility.py index e885eb2..aad855f 100644 --- a/tests/test_cross_distro_compatibility.py +++ b/tests/test_cross_distro_compatibility.py @@ -129,6 +129,36 @@ class TestCrossDistroCompatibility: for path in expected_paths: assert path in install_sh_content, f"Should have fallback path: {path}" + def test_opensuse_dependency_mapping_uses_tumbleweed_package_names(self, install_sh_content): + """Test that openSUSE uses Tumbleweed-compatible dependency resolution.""" + assert "suse_python_package_prefix()" in install_sh_content + assert "suse_python_package_candidates()" in install_sh_content + assert "typelib-1_0-AyatanaAppIndicator3-0_1" in install_sh_content + assert "typelib-1_0-Notify-0_7" in install_sh_content + assert "libnotify4" in install_sh_content + assert "suse_install_appindicator_runtime" in install_sh_content + assert "shaderc glslang-devel glslang" in install_sh_content + + zypper_line = next( + line for line in install_sh_content.splitlines() if "local ZYPPER_PACKAGES=" in line + ) + assert "python3-devel" not in zypper_line + assert "python3-virtualenv" not in zypper_line + assert "libappindicator-gtk3" not in zypper_line + assert "glslang" not in zypper_line + + def test_skip_system_deps_option_is_implemented(self, install_sh_content): + """Test that the documented --skip-system-deps option is parsed and honored.""" + assert "SKIP_SYSTEM_DEPS" in install_sh_content + assert "--skip-system-deps" in install_sh_content + assert "Skipping system dependency installation" in install_sh_content + + def test_noninteractive_tty_fallback_does_not_abort(self, install_sh_content): + """Test that missing controlling TTY falls back to non-interactive mode.""" + assert "{ true < /dev/tty; } 2>/dev/null" in install_sh_content + assert "exec < /dev/tty" in install_sh_content + assert 'NON_INTERACTIVE="yes"' in install_sh_content + def test_ci_workflow_uses_dynamic_detection(self, workflow_content): """Test that CI workflow uses dynamic GI_TYPELIB_PATH detection.""" # Should use pkg-config or dynamic detection