From 64032fb99921aa5e1a54ebaa9db97f2181c6c3b4 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 15 Oct 2026 15:16:10 -0700 Subject: [PATCH] fix(snap): drop uinput plug denied by the Snap Store (#960) * fix(snap): drop uinput plug denied by the Snap Store The Snap Store rejected revisions 8-10 over the super-privileged uinput plug (allow-installation), and reviewers denied the grant request on forum.snapcraft.io/t/53528, pointing at the RemoteDesktop portal path the app already prefers. Remove the plug and the uinput-only ydotool so revisions pass automatic review again; Wayland typing continues through the RemoteDesktop portal (already the default backend ahead of ydotool), wtype, IBus Wayland on KDE, and clipboard paste. KDE Plasma loses its correct-case ydotool path on the store snap (KWin's portal mangles keysyms on some layouts, KDE bug 489021); the v0.18.x GitHub .snap still declares uinput for KDE users who sideload it. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(text-injection): stop telling snap users to connect uinput The Store snap no longer declares the plug, so the warning pointed at a command that cannot work. The Snap hints now explain that only the v0.18.x GitHub sideload declares uinput, and that native Wayland typing uses the RemoteDesktop portal or IBus Wayland on KDE. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Jatin K Malik Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 9 ++++----- README.md | 7 +++---- docs/INSTALL.md | 9 ++++----- docs/RELEASE_PROCESS.md | 6 +++--- docs/UPDATE.md | 6 +++--- docs/USER_GUIDE.md | 2 +- snap/snapcraft.yaml | 20 ++++++++++--------- src/vocalinux/text_injection/text_injector.py | 12 +++++++---- tests/test_snap_packaging.py | 17 +++++++++------- web/src/app/changelog/page.tsx | 4 ++-- 10 files changed, 49 insertions(+), 43 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 322fce5..4d2207e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -283,11 +283,10 @@ jobs: Tagged releases also attach `vocalinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap` and try the Snap Store (`edge` and `candidate`). `stable` is promoted from candidate after QA by the `snap-promote.yml` dispatch workflow. - Super-privileged plugs such as `uinput` can hold the Store revision in human - review; the GitHub `.snap` is the sideload in that case: + Wayland typing runs through the RemoteDesktop portal; the snap ships no `uinput` plug. + The GitHub `.snap` is also the sideload while a Store revision lags: ```bash sudo snap install --dangerous ./vocalinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap - sudo snap connect vocalinux:uinput ``` `--dangerous` is required because this file is not a Store revision. It will not refresh from the Store. amd64 only. @@ -983,8 +982,8 @@ jobs: ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} commit_message: 'Release ${{ github.ref_name }}' - # GitHub Release first, Store second. uinput is super-privileged, so the - # Store can queue a human review and refuse edge/candidate; the .snap still + # GitHub Release first, Store second. A super-privileged plug can still + # queue a human review and refuse edge/candidate; the .snap still # has to land on the tag for sideload. grade: stable allows candidate; do # not auto-release to stable — promote after QA via snap-promote.yml (#783). # Secret: SNAPCRAFT_STORE_CREDENTIALS (snapcraft export-login). diff --git a/README.md b/README.md index 979bf3d..96e07ee 100644 --- a/README.md +++ b/README.md @@ -217,16 +217,15 @@ sudo snap install vocalinux --edge sudo snap connect vocalinux:audio-record # if mic is not auto-connected sudo snap connect vocalinux:raw-input # global keyboard shortcuts (evdev) sudo snap connect vocalinux:hardware-observe # list keyboards (/proc/bus/input/devices) -sudo snap connect vocalinux:uinput # native Wayland typing (ydotool) ``` -v0.18.2 includes ydotool and the `uinput` plug. The Store still has to finish review of that plug, so `snap info vocalinux` may still show **v0.16.2** (rev 7) on edge. That revision has no `uinput` plug; `snap connect vocalinux:uinput` fails until a 0.18.0+ revision is listed. +Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on first dictation, nothing to connect. The recipe ships **no `uinput` plug** and no ydotool; the Store denied the super-privileged grant, which held the 0.17.0+ revisions and left **v0.16.2 (rev 7)** on edge. On KDE Plasma, KWin's portal can mistype letter case ([KDE bug 489021](https://bugs.kde.org/show_bug.cgi?id=489021)); set Virtual Keyboard to "IBus Wayland" so dictation types correctly. -Until then, sideload the GitHub file: +The v0.18.x GitHub `.snap` still declares `uinput`, so KDE users who want ydotool can sideload it and connect the plug manually: ```bash sudo snap install --dangerous ./vocalinux_0.18.2_amd64.snap -sudo snap connect vocalinux:uinput +sudo snap connect vocalinux:uinput # only in the v0.18.x sideload; the recipe no longer ships it ``` ### Nix / NixOS (flake) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index da2c711..b960910 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -169,22 +169,21 @@ sudo snap install vocalinux --edge sudo snap connect vocalinux:audio-record # if mic is not auto-connected sudo snap connect vocalinux:raw-input # global keyboard shortcuts (evdev) sudo snap connect vocalinux:hardware-observe # list keyboards (/proc/bus/input/devices) -sudo snap connect vocalinux:uinput # native Wayland typing (ydotool) ``` -**v0.18.2** ships ydotool and the `uinput` plug. That plug is super-privileged, so the Store held 0.18.0 for human review (`allow-installation`). Until a 0.18.0+ revision is listed, `snap info vocalinux` still shows **v0.16.2 (rev 7)** on edge. That revision has no `uinput` plug; `sudo snap connect vocalinux:uinput` fails. +Native Wayland typing uses the RemoteDesktop portal: the compositor asks once on first dictation, nothing to connect. The recipe ships **no `uinput` plug** and no ydotool; the Store denied the super-privileged grant ([the request thread](https://forum.snapcraft.io/t/53528)), which held the 0.17.0+ revisions and left **v0.16.2 (rev 7)** on edge. On KDE Plasma, KWin's portal can mistype letter case ([KDE bug 489021](https://bugs.kde.org/show_bug.cgi?id=489021)); set Virtual Keyboard to "IBus Wayland" so dictation types correctly. -Sideload the GitHub `.snap` (amd64) while the Store is waiting: +The v0.18.x GitHub `.snap` still declares `uinput`, so KDE users who want ydotool can sideload it and connect the plug manually: ```bash sudo snap install --dangerous ./vocalinux_0.18.2_amd64.snap sudo snap connect vocalinux:audio-record sudo snap connect vocalinux:raw-input sudo snap connect vocalinux:hardware-observe -sudo snap connect vocalinux:uinput +sudo snap connect vocalinux:uinput # only in the v0.18.x sideload; the recipe no longer ships it ``` -`--dangerous` is required because this file is not a Store revision. It will not refresh from the Store. After Canonical grants `uinput`, switch to `sudo snap install vocalinux --edge` (or `snap refresh`). +`--dangerous` is required because this file is not a Store revision and will not refresh from the Store; once the Store carries a newer revision, `sudo snap install vocalinux --edge` (or `snap refresh`) replaces it. ## Nix / NixOS diff --git a/docs/RELEASE_PROCESS.md b/docs/RELEASE_PROCESS.md index 510f4c5..d2da311 100644 --- a/docs/RELEASE_PROCESS.md +++ b/docs/RELEASE_PROCESS.md @@ -397,8 +397,8 @@ After pushing the tag, the GitHub Actions workflow will automatically: docs/FLATPAK_REMOTE.md) 8. Deploy the website to vocalinux.com 9. Build the amd64 snap, attach it to the GitHub Release, and try Snap Store - `edge`/`candidate`. Store human review (for example `uinput` allow-installation) - must not block the GitHub `.snap`. `stable` does not auto-release (#783): + `edge`/`candidate`. Store human review (for example a super-privileged + `allow-installation` plug) must not block the GitHub `.snap`. `stable` does not auto-release (#783): after candidate QA, dispatch `snap-promote.yml`, which releases the candidate revision to `latest/stable` 10. Mark as pre-release if version contains alpha/beta/rc @@ -412,7 +412,7 @@ Monitor at: https://github.com/VocaHQ/vocalinux/actions both AppImages, both Flatpaks, the amd64 snap, the `.deb` and `.rpm` for both arches) - the release notes tell users to run `sha256sum -c` against it -- [ ] If the Store held the snap for `uinput` review, confirm the GitHub `.snap` +- [ ] If the Store holds the snap for human review, confirm the GitHub `.snap` is still attached and the notes document `snap install --dangerous` - [ ] After candidate QA (install, tray, mic, model download, typing into a real app), promote the snap to stable: diff --git a/docs/UPDATE.md b/docs/UPDATE.md index b9be644..bf3f822 100644 --- a/docs/UPDATE.md +++ b/docs/UPDATE.md @@ -38,7 +38,7 @@ git pull origin main |--------|---------| | AUR | `yay -S vocalinux` (or your AUR helper) | | AppImage | Download the new file from [Releases](https://github.com/VocaHQ/vocalinux/releases) | -| Snap | Store: `sudo snap refresh vocalinux` (`--edge` until stable is promoted). Until Canonical lists 0.18.2, sideload `vocalinux_0.18.2_amd64.snap` from the GitHub Release (`sudo snap install --dangerous ./vocalinux_0.18.2_amd64.snap`), then `sudo snap connect vocalinux:uinput`. v0.16.2 rev 7 has no such plug. | +| Snap | Store: `sudo snap refresh vocalinux` (`--edge` until stable is promoted). The recipe ships no `uinput` plug (the Store denied the grant); Wayland typing uses the RemoteDesktop portal. The GitHub `vocalinux_0.18.2_amd64.snap` still declares `uinput` for KDE users who want ydotool (`sudo snap install --dangerous`, then `sudo snap connect vocalinux:uinput`). | | Flatpak (release bundle) | Install the new `.flatpak` from Releases; bundles do not auto-update | | PyPI | Reinstall in the same venv after system packages are current | @@ -200,7 +200,7 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.18. | **Searchable open picker** | Language list filters while open (#798) | | **Localized punctuation commands** | it/fr/de/es/pt/nl/pl/ru; English phrases still work (#642) | | **Bare F-keys** | F1–F24 are valid push-to-talk shortcuts (#815) | -| **Snap** | Store listing, ydotool + `uinput` for native Wayland; GitHub `.snap` for sideload while Store review is pending (#519, #823, #822) | +| **Snap** | Store listing, ydotool + `uinput` for native Wayland. The Store later denied the `uinput` grant, so the plug was dropped and Wayland typing moved to the RemoteDesktop portal (#519, #823, #822) | | **Flatpak on the tag** | Workflow attaches `.flatpak` assets and checksums them (#786) | ### Also in v0.17.0 @@ -260,7 +260,7 @@ See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.17. - **CI**: gate AUR PKGBUILD builds on every PR (#772 by @sesav) - **CI / docs**: test the distros the docs promise and fix docs drift (#773 by @sesav) - **Site**: VocaGateway family card is Beta; README logo, badges, and privacy copy (#765 by @jatinkrmalik, #764) -- **Snap**: v0.16.2 `latest/edge` (rev 7) has no `uinput` plug. `sudo snap connect vocalinux:uinput` errors with `snap "vocalinux" has no plug named "uinput"`. Dictation only reaches XWayland apps. A later store snap ships ydotool and the plug; then `snap refresh` and `snap connect vocalinux:uinput` (#823) +- **Snap**: v0.16.2 `latest/edge` (rev 7) has no `uinput` plug. `sudo snap connect vocalinux:uinput` errors with `snap "vocalinux" has no plug named "uinput"`. Dictation only reaches XWayland apps. (v0.17.0+ added the plug, but the Store denied it, so later revisions drop it again and type via the RemoteDesktop portal.) (#823) See the [full changelog](https://github.com/VocaHQ/vocalinux/releases/tag/v0.16.2). diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 23595b7..7bfde58 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -304,7 +304,7 @@ Pin the backend explicitly in `~/.config/vocalinux/config.json`: |---|---| | `auto` | Autodetect (default; autodetection may select IBus) | | `ibus` | IBus input method; on Wayland, bypasses compositor checks and may silently do nothing in native Wayland apps | -| `portal` | RemoteDesktop portal (Wayland; the sandboxed path, asks for permission once, works under Flatpak) | +| `portal` | RemoteDesktop portal (Wayland; the sandboxed path, asks for permission once, works under Flatpak and Snap) | | `wtype` | wtype virtual keyboard (Wayland) | | `ydotool` | ydotool uinput (Wayland; needs `ydotoold`) | | `xdotool` | xdotool (X11). On Wayland it only turns IBus off -- the Wayland tool is still picked automatically | diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 55e3450..911612d 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,7 +1,7 @@ # Vocalinux Snap packaging # # First ship: strict confinement, default whisper.cpp engine path, GTK tray UI, -# mic capture, model download, and text injection (ydotool/uinput, wtype, xdotool). +# mic capture, model download, and text injection (RemoteDesktop portal, wtype, xdotool). # Informed by issue #48 and packaging/flatpak lessons on feat/flatpak-packaging. # # Build (from repo root, with snapcraft + LXD or Multipass): @@ -26,13 +26,16 @@ description: | - System tray control and GTK settings - Offline dictation (models download on first use) - Microphone capture and short feedback tones - - Text injection via ydotool (native Wayland/X11), wtype, or xdotool + - Text injection via the Wayland RemoteDesktop portal, wtype, or xdotool Sandbox notes: - Global hotkeys need: sudo snap connect vocalinux:raw-input and sudo snap connect vocalinux:hardware-observe - - Native Wayland typing needs: sudo snap connect vocalinux:uinput - (v0.17.0 ships the plug; v0.16.2 edge (rev 7) has no uinput plug; refresh first) + - Native Wayland typing uses the RemoteDesktop portal: the compositor + asks once on first dictation. The snap ships no uinput plug and no + ydotool; the Store denied the super-privileged grant. + - KDE Plasma: KWin's portal can mistype letter case (KDE bug 489021); + set "Virtual Keyboard = IBus Wayland" so the IBus path delivers text. - Microphone may need: sudo snap connect vocalinux:audio-record - Config and models live under ~/snap/vocalinux/ @@ -65,10 +68,10 @@ apps: # Needed for /proc/bus/input/devices; not auto-connected: # sudo snap connect vocalinux:hardware-observe - hardware-observe - # ydotool writes to /dev/uinput so dictation reaches native Wayland apps. - # Super-privileged: not auto-connected; user must: - # sudo snap connect vocalinux:uinput - - uinput + # No uinput plug; the RemoteDesktop portal types on Wayland instead. + # The Store denied the super-privileged grant + # (https://forum.snapcraft.io/t/53528), and ydotool is not staged + # because it cannot reach /dev/uinput under confinement. common-id: com.vocalinux.Vocalinux desktop: usr/share/applications/vocalinux.desktop environment: @@ -122,7 +125,6 @@ parts: - pulseaudio-utils # paplay for start/stop UI sounds - xdotool - wtype # virtual-keyboard protocol (GNOME often rejects this) - - ydotool # uinput; reaches native Wayland apps (needs vocalinux:uinput) - x11-utils # xprop for XWayland WM_CLASS - xsel - xclip diff --git a/src/vocalinux/text_injection/text_injector.py b/src/vocalinux/text_injection/text_injector.py index a701f26..f91b647 100644 --- a/src/vocalinux/text_injection/text_injector.py +++ b/src/vocalinux/text_injection/text_injector.py @@ -507,7 +507,9 @@ class TextInjector: start the daemon on demand when the app is granted device access. Host ydotool 0.1.x often works without a daemon; starting one is still safe when ydotoold is installed. The CLI still needs a writable - /dev/uinput (Snap: ``snap connect vocalinux:uinput``). + /dev/uinput (the v0.18.x GitHub sideload Snap declares the plug: + ``snap connect vocalinux:uinput``; the Store snap ships neither plug + nor ydotool). """ if self._is_ydotoold_running(): return True @@ -594,7 +596,8 @@ class TextInjector: """How to grant ydotool access to ``/dev/uinput`` in this install.""" if os.environ.get("SNAP"): return ( - "ydotool cannot open /dev/uinput. For the Snap, run: " + "ydotool cannot open /dev/uinput. The Store snap ships no " + "uinput plug; only the v0.18.x GitHub sideload declares it: " "sudo snap connect vocalinux:uinput" ) return ( @@ -609,8 +612,9 @@ class TextInjector: if not os.environ.get("SNAP"): return logger.warning( - "Snap XWayland fallback only types into X11/XWayland apps. " - "Native Wayland apps need: sudo snap connect vocalinux:uinput" + "Snap XWayland fallback only types into X11/XWayland apps. The " + "Store snap ships no uinput plug; native Wayland typing needs the " + "RemoteDesktop portal or, on KDE, IBus Wayland." ) @staticmethod diff --git a/tests/test_snap_packaging.py b/tests/test_snap_packaging.py index b82095a..09a60e4 100644 --- a/tests/test_snap_packaging.py +++ b/tests/test_snap_packaging.py @@ -26,9 +26,11 @@ def test_snapcraft_recipe_and_gui_assets() -> None: assert "raw-input" in plugs assert "hardware-observe" in plugs assert "audio-record" in plugs - assert "uinput" in plugs + # The Store denied the super-privileged uinput grant; the plug (and the + # uinput-only ydotool) must stay out or every revision is held in review. + assert "uinput" not in plugs stage = doc["parts"]["vocalinux"].get("stage-packages") or [] - assert "ydotool" in stage + assert "ydotool" not in stage recipe_text = SNAPCRAFT_YAML.read_text(encoding="utf-8") assert "sudo snap connect vocalinux:raw-input" in recipe_text @@ -65,8 +67,8 @@ def test_snap_docs_tell_users_to_connect_hardware_observe() -> None: assert "sudo snap connect vocalinux:hardware-observe" in text -def test_snap_docs_warn_that_0162_has_no_uinput_plug() -> None: - """v0.16.2 edge (rev 7) has no uinput plug; the connect command must not stand alone.""" +def test_snap_docs_explain_typing_without_uinput_plug() -> None: + """The Store denied the uinput grant; docs must point at the portal, not a connect.""" readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8") install = (REPO_ROOT / "docs" / "INSTALL.md").read_text(encoding="utf-8") update = (REPO_ROOT / "docs" / "UPDATE.md").read_text(encoding="utf-8") @@ -78,8 +80,8 @@ def test_snap_docs_warn_that_0162_has_no_uinput_plug() -> None: uinput_lines = "\n".join(line for line in text.splitlines() if "uinput" in line.lower()) lowered = uinput_lines.lower() assert "uinput" in lowered - assert "0.16.2" in uinput_lines or "rev 7" in lowered assert "plug" in lowered and "no" in lowered + assert "portal" in lowered def test_snap_strips_pygobject_and_uses_gnome_gi() -> None: @@ -96,7 +98,7 @@ def test_snap_strips_pygobject_and_uses_gnome_gi() -> None: def test_release_attaches_snap_then_tries_the_store() -> None: - """Store review of uinput must not swallow the GitHub .snap (v0.17.0).""" + """A Store human review must not swallow the GitHub .snap (v0.17.0, uinput).""" text = (REPO_ROOT / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8") assert " build-snap:\n" in text assert " attach-snap:\n" in text @@ -137,7 +139,8 @@ def test_release_attaches_snap_then_tries_the_store() -> None: def test_release_notes_document_snap_sideload() -> None: body = (REPO_ROOT / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8") assert "snap install --dangerous" in body - assert "snap connect vocalinux:uinput" in body + assert "RemoteDesktop portal" in body + assert "no `uinput` plug" in body assert "vocalinux_${{ steps.get_version.outputs.VERSION }}_amd64.snap" in body diff --git a/web/src/app/changelog/page.tsx b/web/src/app/changelog/page.tsx index c307ca1..243c27a 100644 --- a/web/src/app/changelog/page.tsx +++ b/web/src/app/changelog/page.tsx @@ -87,7 +87,7 @@ const releases = [ "Recommended model button sets size and specialization together and will not ignore a file already on disk (PR #797, #778)", "Localized punctuation voice commands for it/fr/de/es/pt/nl/pl/ru (PR #642, #640)", "Bare F1–F24 push-to-talk shortcuts (PR #815)", - "Snap packaging with ydotool and uinput for native Wayland; GitHub .snap sideload while Store review of uinput is pending (PR #519, #823, #822)", + "Snap packaging with ydotool and uinput for native Wayland; the Store denied the uinput grant, so later revisions drop the plug and type via the RemoteDesktop portal (PR #519, #823, #822)", "Release workflow attaches Vocalinux--{x86_64,aarch64}.flatpak (PR #786, #784)", "XWayland clipboard paste instead of layout-garbled xdotool type (PR #680, #657)", "Layout-aware Ctrl+V paste; ydotool releases Ctrl after a timed-out paste (PR #788, #675, #658)", @@ -116,7 +116,7 @@ const releases = [ "CI: gate AUR PKGBUILD builds on every PR; test the distros the docs promise and fix docs drift (PR #772, #773)", "Site: VocaGateway family card is Beta; README logo, badges, and privacy copy (PR #765, #764)", "Deps: bump the github-actions group (PR #766)", - "Snap 0.16.2 edge (rev 7) has no uinput plug: snap connect vocalinux:uinput fails, and dictation only reaches XWayland apps. A later store snap ships ydotool; refresh, then connect uinput (PR #823)", + "Snap 0.16.2 edge (rev 7) has no uinput plug: snap connect vocalinux:uinput fails, and dictation only reaches XWayland apps. Later revisions ship the RemoteDesktop portal instead; the Store denied the uinput grant (PR #823)", ], }, {