Package Superwhisper with upstream beta tracking (#854)
* Package Superwhisper with upstream beta tracking * Seed fresh Superwhisper shortcuts before daemon startup * Refresh checksum for Superwhisper user setup * Fix Superwhisper panel setup and packaging review findings * Read shell replies when retrying Superwhisper panel setup * Exclude Superwhisper portable menu integration
This commit is contained in:
1 parent
e285432795
commit
7d2c7c50af
11 files changed
+422
No files matched your search
@@ -67,6 +67,7 @@ jobs:
|
||||
./tests/artifact-helpers.sh
|
||||
./tests/limine-mkinitcpio-hook.sh
|
||||
./tests/voxtype-bin-install.sh
|
||||
./tests/superwhisper-bin-install.sh
|
||||
pacman -S --noconfirm --quiet rclone >/dev/null
|
||||
./tests/publish-artifact.sh
|
||||
'
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"source": "local",
|
||||
"release_ring": "fast",
|
||||
"min_release_age": "24h",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "superultrainc/superwhisper-linux-releases",
|
||||
"allow_prerelease": true,
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)-beta\\.(?P<beta>[0-9]+)",
|
||||
"version": "{version}beta{beta}",
|
||||
"variables": {
|
||||
"_version": "{version}-beta.{beta}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
# Maintainer: Omarchy <https://omarchy.org>
|
||||
|
||||
pkgname=superwhisper-bin
|
||||
pkgver=0.1.0beta15
|
||||
_version=0.1.0-beta.15
|
||||
pkgrel=1
|
||||
pkgdesc='Local and cloud voice dictation with Superwhisper'
|
||||
arch=('x86_64')
|
||||
url='https://superwhisper.com'
|
||||
license=('LicenseRef-proprietary' 'MIT')
|
||||
provides=('superwhisper')
|
||||
conflicts=('superwhisper')
|
||||
depends=('glibc' 'libgcc' 'libstdc++' 'libgomp' 'xdg-utils' 'python' 'python-gobject' 'python-cairo' 'gtk4'
|
||||
'gtk4-layer-shell' 'at-spi2-core' 'pipewire-pulse' 'libnotify'
|
||||
'wl-clipboard' 'ffmpeg' 'vulkan-icd-loader' 'librsvg' 'fontconfig'
|
||||
'hicolor-icon-theme' 'foot')
|
||||
optdepends=('fcitx5: insert dictation directly through the input method'
|
||||
'vulkan-intel: local processing on Intel GPUs'
|
||||
'vulkan-radeon: local processing on AMD GPUs')
|
||||
makedepends=('minisign')
|
||||
options=('!debug' '!strip')
|
||||
_release="https://tux.superwhisper.com/x86_64/v$_version/superwhisper-omarchy-x86_64-$_version.tar.zst"
|
||||
source=("superwhisper-$pkgver.tar.zst::$_release"
|
||||
"superwhisper-$pkgver.tar.zst.minisig::$_release.minisig"
|
||||
'linux-release.pub'
|
||||
'superwhisper'
|
||||
'superwhisper.service'
|
||||
'superwhisper.desktop'
|
||||
'superwhisper-url.desktop'
|
||||
'setup-user')
|
||||
noextract=("superwhisper-$pkgver.tar.zst")
|
||||
sha256sums=('99e44cbeb60c44945d2713144f12d0a356b1dfb98ed5702b5468fb6b47ae49c0'
|
||||
'4d933540a9299f5f7835d1f6494ce3facad866c477cdf0e82bf3e9ae5813325d'
|
||||
'0672b5230ed6390f9168eaea009c6ce1e8838f0961a87888d9073d1e1db5267d'
|
||||
'54a0fd574f2b123af24031e910fe0dc47be1d4c055ee5df775f1c681e6201c93'
|
||||
'8b9a2cb81a6ea015d663c9981ba67edafe934eb0e56904fab5030fbbfdd8506b'
|
||||
'c65169c8432223f26b072c8defe9ff65ef82c26be34cd92a9617ed112cb22570'
|
||||
'e67978d239e12a386ec335346b14e10cc7e3bed8b9b1d4bcbed4377f10d4695a'
|
||||
'ffbf2f42e403fca85eb73810742397884fbf949b715ed33cb98f618f33556c16')
|
||||
|
||||
prepare() {
|
||||
minisign -Vm "superwhisper-$pkgver.tar.zst" -x "superwhisper-$pkgver.tar.zst.minisig" -p linux-release.pub
|
||||
bsdtar -xf "superwhisper-$pkgver.tar.zst"
|
||||
local bundle="superwhisper-omarchy-x86_64-$_version"
|
||||
[[ $(< "$bundle/VERSION") == "$_version" ]] || return 1
|
||||
# Qt 6.12 adds a Color type that shadows Omarchy's palette singleton.
|
||||
python - "superwhisper-omarchy-x86_64-$_version/app/assets/omarchy-plugin/superwhisper-panel" <<'PYTHON'
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
|
||||
for path in Path(sys.argv[1]).glob("*.qml"):
|
||||
original = path.read_text()
|
||||
updated = re.sub(r"(?<![\w.])Color\b", "Commons.Color", original)
|
||||
if updated != original:
|
||||
alias = r"(?m)^import qs\.Commons(?: [0-9.]+)? as Commons[ \t]*$"
|
||||
if not re.search(alias, updated):
|
||||
updated, imports = re.subn(r"(?m)^(import qs\.Commons(?: [0-9.]+)?[ \t]*)$", r"\1\nimport qs.Commons as Commons", updated, count=1)
|
||||
if imports != 1:
|
||||
raise SystemExit(f"Cannot qualify palette import in {path}")
|
||||
if len(re.findall(alias, updated)) != 1 or re.search(r"(?<![\w.])Color\b", updated):
|
||||
raise SystemExit(f"Invalid palette references in {path}")
|
||||
path.write_text(updated)
|
||||
PYTHON
|
||||
}
|
||||
|
||||
package() {
|
||||
local bundle="superwhisper-omarchy-x86_64-$_version"
|
||||
install -dm755 "$pkgdir/opt/superwhisper"
|
||||
cp -a --no-preserve=ownership "$bundle/app/." "$pkgdir/opt/superwhisper/"
|
||||
# Omarchy owns its Dictation menu; omit the portable installer's menu writer.
|
||||
rm -f "$pkgdir/opt/superwhisper/scripts/omarchy-menu-rows.py"
|
||||
install -Dm755 superwhisper "$pkgdir/usr/bin/superwhisper"
|
||||
install -Dm644 superwhisper.service "$pkgdir/usr/lib/systemd/user/superwhisper.service"
|
||||
install -Dm644 superwhisper.desktop "$pkgdir/usr/share/applications/superwhisper.desktop"
|
||||
install -Dm644 superwhisper-url.desktop "$pkgdir/usr/share/applications/superwhisper-url.desktop"
|
||||
install -Dm755 setup-user "$pkgdir/usr/share/superwhisper/setup-user"
|
||||
install -Dm644 -t "$pkgdir/usr/share/licenses/$pkgname/" "$bundle"/licenses/*
|
||||
install -Dm644 "$bundle/README.md" "$pkgdir/usr/share/doc/$pkgname/README.md"
|
||||
install -Dm644 "$bundle/app/assets/linux-status/SuperwhisperTUI.ttf" "$pkgdir/usr/share/fonts/superwhisper/SuperwhisperTUI.ttf"
|
||||
for size in 16 32 64 128 256 512 1024; do
|
||||
install -Dm644 "$bundle/app/assets/app-icon/$size.png" "$pkgdir/usr/share/icons/hicolor/${size}x${size}/apps/superwhisper.png"
|
||||
done
|
||||
install -dm755 "$pkgdir/usr/share/fcitx5/addon"
|
||||
sed 's|@LIBRARY@|/opt/superwhisper/lib/fcitx5/libsuperwhisper-fcitx5|' \
|
||||
"$bundle/app/assets/fcitx5/superwhisper.conf.in" > "$pkgdir/usr/share/fcitx5/addon/superwhisper.conf"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Superwhisper
|
||||
|
||||
Packages the vendor's signed portable x86_64 Linux archive under `/opt/superwhisper`, with a launcher, desktop entries, user service, fonts, icons and the fcitx5 text-commit addon. Local models are downloaded through Superwhisper's settings; they are not bundled in the package.
|
||||
|
||||
`sync-upstream` follows beta releases from `superultrainc/superwhisper-linux-releases`, normalizing `0.1.0-beta.15` to the Arch version `0.1.0beta15` while retaining the vendor version in `_version`. The stable feed has not been published yet. The recipe verifies the archive's SHA256 and Minisign signature against the checked-in vendor release key. Packaging never runs the downloaded installer.
|
||||
|
||||
Omarchy's Setup → Defaults → Dictation → Superwhisper invokes `/usr/share/superwhisper/setup-user`, then configures its shortcuts and saves Superwhisper as the default backend. The package does not add Omarchy menu extensions and omits the portable installer’s menu-writing helper. User setup enables the packaged service, links the vendor's panel and agent skill, and retires known portable-install wrappers and unit overrides with backups. Preferences, history and downloaded models are retained. Package updates come from OPR; the portable install's private update timer is disabled.
|
||||
|
||||
Omarchy loads the selected backend's generated `~/.config/superwhisper/shortcuts.lua` bridge itself, so no Superwhisper block is required in the user's Hyprland files. The bridge retains cancellation and correction-based vocabulary learning alongside Omarchy's shared dictation commands.
|
||||
|
||||
The launcher redirects the vendor’s generated Hyprland include to a private, unloaded file for both the daemon and CLI. User setup waits for daemon readiness. The portable updater is disabled and refused by the packaged launcher. The archive is verified before extraction, its version is checked, and the bundled panel uses explicit Omarchy palette references for Qt 6.12.
|
||||
|
||||
Fresh profiles start with Alt+Space toggle, no native hold, and Escape cancellation before the daemon starts, avoiding a conflict with Omarchy’s Right Alt binding. Existing preferences remain unchanged.
|
||||
|
||||
Panel discovery is asynchronous: user setup retries placement, leaves a pending marker if the shell is unavailable, and retries on the next setup without moving an already placed panel. Bundled component license notices are installed under `/usr/share/licenses/superwhisper-bin/`.
|
||||
@@ -0,0 +1,2 @@
|
||||
untrusted comment: minisign public key BC524820AE37B966
|
||||
RWRmuTeuIEhSvCYQejUR+zzMNsfxAn7pr9iOZ2ZJ9U1/zJwfsqvhzATB
|
||||
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
data_home="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||
backup_suffix=".before-opr.$(date +%s%N)"
|
||||
|
||||
# Retire the portable install's wrappers and unit overrides. Keep its versioned
|
||||
# binaries, preferences, history and models so the change is reversible.
|
||||
launcher="$HOME/.local/bin/superwhisper"
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
if [[ -f $launcher && ! -L $launcher ]] && grep -Fq 'superwhisper/app/current/run.sh' "$launcher"; then
|
||||
mv "$launcher" "$launcher$backup_suffix"
|
||||
ln -s /usr/bin/superwhisper "$launcher"
|
||||
fi
|
||||
if [[ ! -e $launcher && ! -L $launcher ]]; then
|
||||
ln -s /usr/bin/superwhisper "$launcher"
|
||||
fi
|
||||
current="$data_home/superwhisper/app/current"
|
||||
if [[ -L $current && $(readlink "$current") != "/opt/superwhisper" ]]; then
|
||||
mv "$current" "$current$backup_suffix"
|
||||
ln -s /opt/superwhisper "$current"
|
||||
elif [[ ! -e $current && ! -L $current ]]; then
|
||||
mkdir -p "${current%/*}"
|
||||
ln -s /opt/superwhisper "$current"
|
||||
fi
|
||||
unit="$config_home/systemd/user/superwhisper.service"
|
||||
if [[ -f $unit ]] && grep -Fq 'ExecStart=%h/.local/bin/superwhisper daemon' "$unit"; then
|
||||
mv "$unit" "$unit$backup_suffix"
|
||||
fi
|
||||
systemctl --user disable --now superwhisper-update.timer 2>/dev/null || true
|
||||
# Seed a fresh profile before the daemon starts. Its upstream Right Alt hold
|
||||
# conflicts with Omarchy's own bindings and prevents sequential CLI changes.
|
||||
preferences="$config_home/superwhisper/preferences.json"
|
||||
if [[ ! -e $preferences ]]; then
|
||||
mkdir -p "${preferences%/*}"
|
||||
(
|
||||
umask 077
|
||||
printf '%s\n' '{"toggleRecordingShortcut":"Alt+Space","pushToTalkShortcut":"","cancelRecordingShortcut":"Escape"}' > "$preferences"
|
||||
)
|
||||
fi
|
||||
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now superwhisper.service
|
||||
systemctl --user restart superwhisper.service
|
||||
|
||||
ready=false
|
||||
for (( attempt = 0; attempt < 50; attempt++ )); do
|
||||
if superwhisper status >/dev/null 2>&1; then
|
||||
ready=true
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
if [[ $ready != "true" ]]; then
|
||||
echo "Superwhisper did not become ready. Check systemctl --user status superwhisper.service." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
xdg-mime default superwhisper-url.desktop x-scheme-handler/superwhisper
|
||||
|
||||
# Link the vendor's panel and agent guide to the packaged payload. They update
|
||||
# with pacman; no copy of the application or private updater is installed.
|
||||
plugins="$config_home/omarchy/plugins"
|
||||
panel_pending="$config_home/superwhisper/omarchy-panel.pending"
|
||||
if [[ ! -e $plugins/superwhisper-panel && ! -L $plugins/superwhisper-panel ]]; then
|
||||
touch "$panel_pending"
|
||||
fi
|
||||
mkdir -p "$plugins" "$HOME/.agents/skills"
|
||||
ln -sfn /opt/superwhisper/assets/omarchy-plugin/superwhisper-panel "$plugins/superwhisper-panel"
|
||||
ln -sfn /opt/superwhisper/assets/agent-skill/superwhisper "$HOME/.agents/skills/superwhisper"
|
||||
for agent in .claude .codex .pi/agent; do
|
||||
if [[ -d $HOME/$agent ]]; then
|
||||
mkdir -p "$HOME/$agent/skills"
|
||||
ln -sfn /opt/superwhisper/assets/agent-skill/superwhisper "$HOME/$agent/skills/superwhisper"
|
||||
fi
|
||||
done
|
||||
omarchy-shell -q shell rescanPlugins
|
||||
if [[ -f $panel_pending ]]; then
|
||||
for (( attempt = 0; attempt < 50; attempt++ )); do
|
||||
placement=$(omarchy-shell shell putBarWidget superwhisper.panel '{"section":"right"}' 2>/dev/null) || placement=""
|
||||
if [[ $placement == "ok" ]]; then
|
||||
rm "$panel_pending"
|
||||
break
|
||||
elif [[ -z $placement ]]; then
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
if [[ -f $panel_pending ]]; then
|
||||
echo "Superwhisper is ready; panel setup will retry next time setup-user runs." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# A running input method discovers the packaged text-commit addon on restart.
|
||||
if systemctl --user -q is-active omarchy-fcitx5.service; then
|
||||
systemctl --user restart omarchy-fcitx5.service
|
||||
fi
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Omarchy loads shortcuts.lua from its selected backend, so the vendor's
|
||||
# generated Hyprland block must live in a file Hyprland does not load.
|
||||
export SUPERWHISPER_HYPR_BINDINGS="${XDG_CONFIG_HOME:-$HOME/.config}/superwhisper/hypr-block.lua"
|
||||
mkdir -p "${SUPERWHISPER_HYPR_BINDINGS%/*}"
|
||||
if [[ ! -e $SUPERWHISPER_HYPR_BINDINGS ]]; then
|
||||
install -m600 /dev/null "$SUPERWHISPER_HYPR_BINDINGS"
|
||||
fi
|
||||
|
||||
if [[ ${1:-} == "update" ]]; then
|
||||
echo "Superwhisper is updated through Omarchy's package repository." >&2
|
||||
exit 1
|
||||
fi
|
||||
exec /opt/superwhisper/run.sh "$@"
|
||||
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Superwhisper License Activation
|
||||
Exec=superwhisper open-url %u
|
||||
Icon=superwhisper
|
||||
Terminal=false
|
||||
NoDisplay=true
|
||||
MimeType=x-scheme-handler/superwhisper;
|
||||
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Superwhisper
|
||||
Comment=Local and cloud voice dictation
|
||||
Exec=foot --app-id=com.superwhisper.settings --title=Superwhisper --font="JetBrainsMono Nerd Font,Superwhisper TUI:size=12" env SW_TUI_LOGO=font superwhisper settings
|
||||
Icon=superwhisper
|
||||
Terminal=false
|
||||
Categories=Utility;Accessibility;
|
||||
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Superwhisper dictation
|
||||
After=graphical-session.target pipewire.service
|
||||
PartOf=graphical-session.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/superwhisper daemon
|
||||
ExecStopPost=/usr/bin/superwhisper playback-restore
|
||||
Restart=on-failure
|
||||
RestartSec=2
|
||||
KillMode=control-group
|
||||
TimeoutStopSec=15
|
||||
UMask=0077
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
Executable
+154
@@ -0,0 +1,154 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
test_tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$test_tmp"' EXIT
|
||||
export HOME="$test_tmp/home" XDG_CONFIG_HOME="$test_tmp/config" XDG_DATA_HOME="$test_tmp/data"
|
||||
export SUPERWHISPER_TEST_LOG="$test_tmp/calls"
|
||||
mkdir -p "$test_tmp/bin" "$HOME/.local/bin" "$XDG_CONFIG_HOME/systemd/user" "$XDG_DATA_HOME/superwhisper/app/old"
|
||||
export PATH="$test_tmp/bin:$PATH"
|
||||
for command in systemctl xdg-mime superwhisper; do
|
||||
cat > "$test_tmp/bin/$command" <<'SH'
|
||||
#!/bin/bash
|
||||
printf '%s %s\n' "${0##*/}" "$*" >> "$SUPERWHISPER_TEST_LOG"
|
||||
[[ $* != *is-active* ]]
|
||||
SH
|
||||
done
|
||||
cat > "$test_tmp/bin/omarchy-shell" <<'SH'
|
||||
#!/bin/bash
|
||||
printf '%s %s\n' "${0##*/}" "$*" >> "$SUPERWHISPER_TEST_LOG"
|
||||
[[ $1 != "-q" ]] || exit 0
|
||||
[[ ${SUPERWHISPER_SHELL_ABSENT:-0} != 1 ]] || exit 1
|
||||
if [[ $* == *putBarWidget* ]]; then
|
||||
count=0
|
||||
[[ ! -f $SUPERWHISPER_PANEL_COUNT ]] || read -r count < "$SUPERWHISPER_PANEL_COUNT"
|
||||
printf '%s\n' "$((count + 1))" > "$SUPERWHISPER_PANEL_COUNT"
|
||||
if (( count < 2 )); then
|
||||
echo 'not ready'
|
||||
else
|
||||
echo ok
|
||||
fi
|
||||
fi
|
||||
SH
|
||||
export SUPERWHISPER_PANEL_COUNT="$test_tmp/panel-count"
|
||||
chmod +x "$test_tmp/bin/"*
|
||||
cat > "$HOME/.local/bin/superwhisper" <<'SH'
|
||||
#!/bin/bash
|
||||
exec "${XDG_DATA_HOME:-$HOME/.local/share}/superwhisper/app/current/run.sh" "$@"
|
||||
SH
|
||||
printf '%s\n' 'ExecStart=%h/.local/bin/superwhisper daemon' > "$XDG_CONFIG_HOME/systemd/user/superwhisper.service"
|
||||
ln -s "$XDG_DATA_HOME/superwhisper/app/old" "$XDG_DATA_HOME/superwhisper/app/current"
|
||||
printf '%s\n' 'retained model' > "$XDG_DATA_HOME/superwhisper/app/old/model"
|
||||
mkdir -p "$XDG_CONFIG_HOME/superwhisper"
|
||||
printf '%s\n' 'retained preferences' > "$XDG_CONFIG_HOME/superwhisper/preferences.json"
|
||||
|
||||
bash "$ROOT/pkgbuilds/superwhisper-bin/setup-user"
|
||||
[[ $(readlink "$HOME/.local/bin/superwhisper") == "/usr/bin/superwhisper" ]]
|
||||
[[ $(readlink "$XDG_DATA_HOME/superwhisper/app/current") == "/opt/superwhisper" ]]
|
||||
[[ ! -e $XDG_CONFIG_HOME/systemd/user/superwhisper.service ]]
|
||||
compgen -G "$XDG_CONFIG_HOME/systemd/user/superwhisper.service.before-opr.*" >/dev/null
|
||||
compgen -G "$HOME/.local/bin/superwhisper.before-opr.*" >/dev/null
|
||||
[[ $(cat "$XDG_DATA_HOME/superwhisper/app/old/model") == "retained model" ]]
|
||||
[[ $(cat "$XDG_CONFIG_HOME/superwhisper/preferences.json") == "retained preferences" ]]
|
||||
grep -Fxq 'systemctl --user disable --now superwhisper-update.timer' "$SUPERWHISPER_TEST_LOG"
|
||||
grep -Fxq 'systemctl --user restart superwhisper.service' "$SUPERWHISPER_TEST_LOG"
|
||||
[[ $(readlink "$XDG_CONFIG_HOME/omarchy/plugins/superwhisper-panel") == "/opt/superwhisper/assets/omarchy-plugin/superwhisper-panel" ]]
|
||||
[[ $(readlink "$HOME/.agents/skills/superwhisper") == "/opt/superwhisper/assets/agent-skill/superwhisper" ]]
|
||||
bash "$ROOT/pkgbuilds/superwhisper-bin/setup-user"
|
||||
(( $(grep -c '^omarchy-shell shell putBarWidget ' "$SUPERWHISPER_TEST_LOG") == 3 ))
|
||||
[[ ! -e $XDG_CONFIG_HOME/superwhisper/omarchy-panel.pending ]]
|
||||
printf '%s\n' 'PASS: Superwhisper setup migrates portable wrappers with backups, preserves data, and keeps the panel placement'
|
||||
|
||||
# A fresh install supplies the user launcher and current link the vendor panel
|
||||
# expects, without installing a private updater or touching Hyprland config.
|
||||
export HOME="$test_tmp/fresh" XDG_CONFIG_HOME="$test_tmp/fresh-config" XDG_DATA_HOME="$test_tmp/fresh-data"
|
||||
bash "$ROOT/pkgbuilds/superwhisper-bin/setup-user"
|
||||
[[ $(readlink "$HOME/.local/bin/superwhisper") == "/usr/bin/superwhisper" ]]
|
||||
[[ $(readlink "$XDG_DATA_HOME/superwhisper/app/current") == "/opt/superwhisper" ]]
|
||||
[[ ! -e $XDG_CONFIG_HOME/hypr ]]
|
||||
python3 - "$XDG_CONFIG_HOME/superwhisper/preferences.json" <<'PY_TEST'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
with open(sys.argv[1]) as profile:
|
||||
preferences = json.load(profile)
|
||||
assert preferences["toggleRecordingShortcut"] == "Alt+Space"
|
||||
assert preferences["pushToTalkShortcut"] == ""
|
||||
assert preferences["cancelRecordingShortcut"] == "Escape"
|
||||
assert os.stat(sys.argv[1]).st_mode & 0o777 == 0o600
|
||||
PY_TEST
|
||||
printf '%s\n' 'PASS: fresh Superwhisper setup uses the package and needs no personal Hyprland config'
|
||||
|
||||
# Missing shell cannot fail backend setup; placement retries next time.
|
||||
export HOME="$test_tmp/deferred" XDG_CONFIG_HOME="$test_tmp/deferred-config" XDG_DATA_HOME="$test_tmp/deferred-data"
|
||||
SUPERWHISPER_SHELL_ABSENT=1 bash "$ROOT/pkgbuilds/superwhisper-bin/setup-user" 2> "$test_tmp/panel-warning"
|
||||
[[ -f $XDG_CONFIG_HOME/superwhisper/omarchy-panel.pending ]]
|
||||
bash "$ROOT/pkgbuilds/superwhisper-bin/setup-user"
|
||||
[[ ! -e $XDG_CONFIG_HOME/superwhisper/omarchy-panel.pending ]]
|
||||
printf '%s\n' 'PASS: asynchronous panel discovery retries and unavailable shell does not abort setup'
|
||||
|
||||
|
||||
# The packaged launcher must put the native include in a file Hyprland never
|
||||
# loads, for the daemon as well as clients. Model the verified vendor contract.
|
||||
cat > "$test_tmp/vendor-run" <<'SH'
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >> "$SUPERWHISPER_TEST_LOG"
|
||||
printf '%s\n' '-- Generated native include' > "$SUPERWHISPER_HYPR_BINDINGS"
|
||||
SH
|
||||
chmod +x "$test_tmp/vendor-run"
|
||||
sed "s|/opt/superwhisper/run.sh|$test_tmp/vendor-run|" "$ROOT/pkgbuilds/superwhisper-bin/superwhisper" > "$test_tmp/launcher"
|
||||
mkdir -p "$XDG_CONFIG_HOME/hypr"
|
||||
printf '%s\n' 'personal bindings' > "$XDG_CONFIG_HOME/hypr/bindings.lua"
|
||||
for action in daemon 'shortcuts apply' 'shortcuts set hold none'; do
|
||||
bash "$test_tmp/launcher" $action
|
||||
done
|
||||
[[ $(cat "$XDG_CONFIG_HOME/hypr/bindings.lua") == "personal bindings" ]]
|
||||
[[ $(cat "$XDG_CONFIG_HOME/superwhisper/hypr-block.lua") == "-- Generated native include" ]]
|
||||
if bash "$test_tmp/launcher" update apply; then
|
||||
echo 'FAIL: the portable updater must not replace a packaged installation' >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' 'PASS: packaged daemon and clients redirect native includes and refuse the private updater'
|
||||
|
||||
# A daemon that never becomes ready must stop user setup with a useful error.
|
||||
cat > "$test_tmp/bin/superwhisper" <<'SH'
|
||||
#!/bin/bash
|
||||
exit 1
|
||||
SH
|
||||
cat > "$test_tmp/bin/sleep" <<'SH'
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
SH
|
||||
chmod +x "$test_tmp/bin/superwhisper" "$test_tmp/bin/sleep"
|
||||
if bash "$ROOT/pkgbuilds/superwhisper-bin/setup-user" 2> "$test_tmp/startup-error"; then
|
||||
echo 'FAIL: user setup must report a daemon that failed to start' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -Fq 'Superwhisper did not become ready' "$test_tmp/startup-error"
|
||||
printf '%s\n' 'PASS: user setup fails clearly when the daemon never becomes ready'
|
||||
|
||||
# Exercise the actual packaging rewrite with vendor import variations.
|
||||
python3 - "$ROOT/pkgbuilds/superwhisper-bin/PKGBUILD" "$test_tmp/palette" <<'PY_TEST'
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
recipe = Path(sys.argv[1]).read_text()
|
||||
patch = recipe.split("<<'PYTHON'\n", 1)[1].split("\nPYTHON\n", 1)[0]
|
||||
folder = Path(sys.argv[2])
|
||||
folder.mkdir()
|
||||
qml = folder / "Panel.qml"
|
||||
for imports in ("import qs.Commons", "import qs.Commons 1.0", "import qs.Commons\nimport qs.Commons as Commons"):
|
||||
qml.write_text(imports + "\nItem { property color ink: Color.foreground }\n")
|
||||
subprocess.run([sys.executable, "-", str(folder)], input=patch, text=True, check=True)
|
||||
result = qml.read_text()
|
||||
assert result.count("import qs.Commons as Commons") == 1
|
||||
assert "Commons.Color.foreground" in result
|
||||
for imports in ("import QtQuick", "import qs.Commons\nimport qs.Commons as Commons\nimport qs.Commons as Commons"):
|
||||
qml.write_text(imports + "\nItem { property color ink: Color.foreground }\n")
|
||||
result = subprocess.run([sys.executable, "-", str(folder)], input=patch, text=True, capture_output=True)
|
||||
assert result.returncode != 0, "invalid imports must fail packaging"
|
||||
PY_TEST
|
||||
printf '%s\n' 'PASS: palette rewrite handles existing/versioned imports and fails on missing or duplicate aliases'
|
||||
Reference in new issue
Block a user