Files
VoiceStudio/scripts/inject-apprun.sh
T
Palash DebnathandClaude Opus 4.7 c32041289d Phase 1 Wave 3: AppImage launcher + .deb ffprobe + Docker LAN + Gatekeeper probe (closes #54, #56, #76, #80) (#93)
* fix(appimage): conditional WEBKIT_DISABLE_COMPOSITING_MODE launcher (#56)

WebKitGTK 2.44.x and 2.46.x have a compositing-path regression on Wayland
that blanks the AppImage's first paint on Fedora 44 / Ubuntu 24.04. Setting
WEBKIT_DISABLE_COMPOSITING_MODE=1 forces the software fallback that works,
but blindly setting it on healthy WebKit versions (2.48+) regresses those.

This wave adds a conditional AppRun launcher that detects the WebKit
version via pkg-config and only sets the env var on the broken ranges
(plus a fail-safe when pkg-config is absent or the version is unknown).
The launcher is injected into Tauri's AppImage staging dir via a
beforeBundleCommand hook — see .planning/decisions/apprun-strategy.md for
the spike outcome and rationale (Strategy B chosen).

Phase 1 Wave 3 — Plan 01-03 Task 1. Closes #56 frontend half.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(deb): relocate bundled ffprobe out of /usr/bin to avoid conflicts (#76)

Prior versions placed the bundled ffprobe at /usr/bin/ffprobe via Tauri's
externalBin, which overwrites the system ffprobe on Ubuntu 26.04 and
collides with apt-installed media-package ffprobe.

Relocate the .deb-bundled ffprobe to /usr/lib/omnivoice-studio/bin/ffprobe
via bundle.linux.deb.files, plus defensive maintainer scripts:
  - preinst:  ensure target dir exists for upgrade flows
  - postinst: remove legacy /usr/bin/ffprobe ONLY when dpkg confirms our
              package owns it (never touches a user's distro ffprobe)
  - postrm:   clean up the relocated path tree on purge/remove

Rust side (tools.rs::resolve_ffprobe) now probes the new path on Linux,
and backend spawn (backend.rs) carries both FFPROBE_PATH (legacy alias)
and OMNIVOICE_FFPROBE_PATH (canonical) into the backend env. Python side
(ffmpeg_utils.resolve_ffprobe) reads OMNIVOICE_FFPROBE_PATH first, falls
back to FFPROBE_PATH, then to shutil.which("ffprobe").

6 new unit tests cover the env-cascade resolution.

Phase 1 Wave 3 — Plan 01-03 Task 2. Closes #76.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): centralised apiBase resolver for Docker LAN access (#80)

Docker / LAN browser users hit the preview API at the LAN host's IP, not
their local machine — the prior frontend/src/utils/media.js:20 hardcoded
http://localhost:3900, which from a LAN client resolved to the client
machine itself.

Centralise via frontend/src/utils/apiBase.ts:
  1. VITE_OMNIVOICE_API override (Docker compose / dev) always wins.
  2. Tauri webview → http://localhost:3900 (unchanged behaviour).
  3. Plain browser → ${window.location.protocol}//${window.location.hostname}:3900
     (follows the page's origin — closes #80).
  4. SSR / no-window → http://localhost:3900 (safe fallback).

Grep-sweep confirmed media.js:20 was the only hardcode site (Assumption
A4 in 01-RESEARCH.md verified). 6 new vitest cases cover the resolver.

Phase 1 Wave 3 — Plan 01-03 Task 3. Closes #80 frontend half.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(backend): macOS Gatekeeper quarantine probe + INST-01 guard (#54)

Adds backend/core/gatekeeper_detect.py which walks up from sys.executable
to find the .app bundle and runs `xattr -l` to check for the quarantine
extended attribute (com.apple.quarantine). On detection, the lifespan
startup probe logs a structured warning and emits a system_error event
through the existing event bus with error_class="GATEKEEPER_QUARANTINE",
which Wave 2's React ErrorBoundary turns into a docs deeplink.

Detection is informational only — we never auto-run `xattr -cr` (the app
itself is quarantined and cannot fix its own state per Anti-Pattern in
01-RESEARCH.md). Users get a clear pointer to the workaround docs.

GET /system/quarantine-status exposes the structured payload so the
frontend can poll on first load.

INST-01 (setuptools>=75.0 pin from PR #62) gains a PR-time guard in
tests/backend/test_pyproject.py + a user-observable smoke check in
scripts/smoke-test.sh (pkg_resources + whisperx import).

7 gatekeeper tests + 1 pyproject test added — all pass.

Phase 1 Wave 3 — Plan 01-03 Task 4. Closes #54 backend half (Wave 2 owns
the docs page + ErrorBoundary deeplink wiring).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 05:53:15 +05:30

59 lines
2.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Inject our custom AppRun into Tauri's auto-generated AppImage staging dir.
#
# Wired into tauri.conf.json's `build.beforeBundleCommand`, this script runs
# AFTER `cargo build` but BEFORE `appimagetool` packs the .AppDir. We overwrite
# the default AppRun (which Tauri generates without WEBKIT_DISABLE_COMPOSITING_MODE
# handling) with our conditional launcher.
#
# Issue: #56 (AppImage white-screen on Fedora 44 / Ubuntu 24.04)
# Decision: .planning/decisions/apprun-strategy.md
#
# Idempotent + safe on non-Linux: if no AppDir staging exists (e.g. macOS
# build, or `--bundles app` only), the script exits 0 cleanly.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
APPRUN_SRC="$REPO_ROOT/frontend/src-tauri/appimage/AppRun"
if [ ! -f "$APPRUN_SRC" ]; then
echo "inject-apprun: source not found: $APPRUN_SRC" >&2
exit 1
fi
# Tauri's AppImage staging dir: frontend/src-tauri/target/{profile}/bundle/appimage/*.AppDir/
# The .AppDir name follows productName (e.g. "OmniVoice Studio.AppDir").
# Glob across both release and debug profiles in case the caller used --debug.
STAGE_BASE_RELEASE="$REPO_ROOT/frontend/src-tauri/target/release/bundle/appimage"
STAGE_BASE_DEBUG="$REPO_ROOT/frontend/src-tauri/target/debug/bundle/appimage"
found=0
for stage_base in "$STAGE_BASE_RELEASE" "$STAGE_BASE_DEBUG"; do
if [ ! -d "$stage_base" ]; then
continue
fi
# Use a glob loop instead of `find` to avoid surprises with names containing spaces.
shopt -s nullglob
for appdir in "$stage_base"/*.AppDir; do
if [ -d "$appdir" ]; then
echo "inject-apprun: replacing AppRun in $appdir"
cp -f "$APPRUN_SRC" "$appdir/AppRun"
chmod 755 "$appdir/AppRun"
found=1
fi
done
shopt -u nullglob
done
if [ $found -eq 0 ]; then
# Not necessarily an error — beforeBundleCommand runs unconditionally even
# when the active target list does not include appimage. Stay quiet so
# macOS/Windows builds do not see noisy stderr.
echo "inject-apprun: no AppDir staging found (skipping — not an AppImage build)"
fi
exit 0