* feat(snap): add Snap packaging recipe and store publish strategy Add a strict-confinement snapcraft.yaml aligned with the current app version and Flatpak sandbox lessons (mic, desktop, network models, xdotool/xsel injection). Document human-only Snap Store steps and mark install docs as not published yet. Refs #48 * fix(snap): adopt-info so craftctl can set version from version.py snapcraft pack failed: craftctl set version requires adopt-info on the part. * fix(snap): stop staging GTK/GLib over the gnome platform Prepending snap-staged GI typelibs and lib paths caused a segfault on from gi.repository import Gtk. Use platform AppIndicator/GTK and only stage PortAudio plus xdotool/clipboard helpers. * docs(snap): note platform GTK path to avoid GI segfault * fix(snap): route ALSA through Pulse so Settings does not abort PortAudio enumerates ALSA devices when Settings opens. Missing /usr/share/alsa/alsa.conf inside the snap triggered a PortAudio assert. Ship an asound.conf that uses the pulse plugin, stage libasound2-plugins, and bind ALSA paths via layout. * fix(snap): keep ALSA conf under snap/local for snapcraft Move asound.conf out of snap/alsa so snapcraft stops warning about non-snapcraft paths in the snap directory. * fix(snap): request raw-input for global keyboard shortcuts Strict confinement blocks /proc/bus/input and /dev/input, so Wayland evdev hotkeys never start. Declare the raw-input plug (manual connect) and print snap-specific connect instructions instead of host input-group hints. * fix(hotkeys): discover keyboards via evdev when /proc is blocked Snap raw-input grants /dev/input/event* but still denies /proc/bus/input/devices, so find_keyboard_devices() returned empty and hotkeys never started. Fall back to evdev.list_devices() when /proc cannot be read. * fix(snap): restore UI sounds and prefer wtype for Wayland inject Stage paplay (pulseaudio-utils) and wtype; teach audio_feedback about pw-play/canberra. xdotool-only injection often hits the wrong window on native Wayland even when it reports success. * fix(snap): sync 0.16.2/AGPL metadata and AppImage-safe audio env Align snapcraft fallback version and store metadata with current main (VocaHQ URLs, AGPL-3.0), keep docs pointing at #48 on VocaHQ, pass host_env to pw-play/canberra paths, and document ordered Snap Store human deploy steps. * test(snap): harden audio host_env unit test against suite pollution Patch os.path.exists like sibling audio_feedback tests instead of relying on a real tempfile path that can collide with other suite mocks. * style(snap): black-format new audio and snap packaging tests * docs(snap): treat Store listing as live; edge→candidate→stable refresh Name vocalinux is already on snapcraft.io (edge @ 0.14.0-beta rev 6). Document upload/release without re-register, add snap-store-refresh.sh helper, and point INSTALL/README/DISTRO at the live listing. * fix(snap): move to core24 and close evdev capability FDs core22 only ships Python 3.10, which fails requires-python >=3.11, so the snap base is now core24 (Python 3.12) with the gnome-46 platform and the Ubuntu 24.04 libasound2t64 package. Also close InputDevice after failed capability scans and annotate the proc parser with TextIO. * Snap: align store listing metadata and icon Match snapcraft.yaml summary, description, license, website, and store icon to the live Snap Store listing. * Snap: trim packaging docs, script, and unused SVG Drop store refresh wrapper, long packaging README, duplicate snap/gui SVG, and speculative pw-play/canberra paths; keep recipe and confinement fixes. * ci: publish snap to edge and candidate on release tags * Snap: set grade stable for candidate channel CI * Snap: skip pip PyGObject; use gnome extension GI
61 lines
2.4 KiB
Python
61 lines
2.4 KiB
Python
"""Structural tests for Snap packaging (issue #48)."""
|
|
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
SNAPCRAFT_YAML = REPO_ROOT / "snap" / "snapcraft.yaml"
|
|
DESKTOP_FILE = REPO_ROOT / "snap" / "gui" / "vocalinux.desktop"
|
|
SNAP_PNG = REPO_ROOT / "snap" / "gui" / "vocalinux.png"
|
|
|
|
|
|
def test_snapcraft_recipe_and_gui_assets() -> None:
|
|
assert SNAPCRAFT_YAML.is_file()
|
|
doc = yaml.safe_load(SNAPCRAFT_YAML.read_text(encoding="utf-8"))
|
|
assert doc["name"] == "vocalinux"
|
|
assert doc["base"] == "core24"
|
|
assert doc["summary"] == "Free offline voice dictation for Linux"
|
|
assert doc["license"] == "AGPL-3.0-only"
|
|
assert doc["icon"] == "snap/gui/vocalinux.png"
|
|
assert doc["website"] == "https://vocalinux.com"
|
|
assert doc["confinement"] == "strict"
|
|
assert doc["grade"] == "stable"
|
|
|
|
plugs = set((doc.get("apps") or {}).get("vocalinux", {}).get("plugs") or [])
|
|
assert "raw-input" in plugs
|
|
assert "audio-record" in plugs
|
|
|
|
assert DESKTOP_FILE.is_file()
|
|
assert SNAP_PNG.is_file()
|
|
assert SNAP_PNG.stat().st_size > 0
|
|
|
|
|
|
def test_snap_strips_pygobject_and_uses_gnome_gi() -> None:
|
|
"""Pip must not build PyGObject; GI comes from the gnome extension."""
|
|
text = SNAPCRAFT_YAML.read_text(encoding="utf-8")
|
|
doc = yaml.safe_load(text)
|
|
override_pull = doc["parts"]["vocalinux"]["override-pull"]
|
|
assert "PyGObject" in override_pull
|
|
assert "pyproject.toml" in override_pull
|
|
assert "gnome extension" in override_pull
|
|
stage = doc["parts"]["vocalinux"].get("stage-packages") or []
|
|
assert "python3-gi" not in stage
|
|
assert "python3-gi-cairo" not in stage
|
|
|
|
|
|
def test_release_publishes_snap_to_edge_and_candidate() -> None:
|
|
"""v* tags must ship the snap to the store, not attach it to GitHub."""
|
|
text = (REPO_ROOT / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8")
|
|
assert " publish-snap:\n" in text
|
|
job = text.split(" publish-snap:\n", 1)[1].split("\n deploy-website:", 1)[0]
|
|
assert "needs: build-and-release" in job
|
|
assert "snapcore/action-build@" in job
|
|
assert "snapcore/action-publish@" in job
|
|
assert "release: edge,candidate" in job
|
|
assert "release: stable" not in job
|
|
assert "SNAPCRAFT_STORE_CREDENTIALS is unset; cannot publish the snap" in job
|
|
assert "gh release upload" not in job
|
|
assert "action-gh-release" not in job
|
|
assert "upload-artifact" not in job
|