Spaces:
Sleeping
Sleeping
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # desktop-prod.sh β Build & launch OmniVoice Studio as a "fresh install" | |
| # | |
| # This gives you the EXACT same experience as a user downloading the | |
| # installer (DMG on macOS, AppImage on Linux): | |
| # β’ Full Rust bootstrap (venv creation, uv sync, model setup) | |
| # β’ Splash screen with live logs | |
| # β’ Region selector, version badge, etc. | |
| # | |
| # Usage: | |
| # bun desktop-prod # build debug + wipe + launch | |
| # bun desktop-prod:run # re-launch last build (skip compile) | |
| # bun desktop-prod:upgrade # rebuild, but keep data (test upgrade) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| set -euo pipefail | |
| APP_ID="com.debpalash.omnivoice-studio" | |
| TAURI_DIR="frontend/src-tauri" | |
| APP_NAME="OmniVoice Studio" | |
| # ββ Detect platform βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| OS="$(uname -s)" | |
| case "$OS" in | |
| Darwin) PLATFORM="macos" ;; | |
| Linux) PLATFORM="linux" ;; | |
| *) echo "β Unsupported platform: $OS"; exit 1 ;; | |
| esac | |
| # ββ Platform-specific paths βββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Two directories matter for fresh-install simulation: | |
| # APP_DATA β Tauri's bundle dir (keyed by APP_ID); holds the post-install | |
| # Python venv + webview state. | |
| # BACKEND_DATA β Where backend/core/config.py::get_app_data_dir() writes: | |
| # SQLite db, voice profiles, generation outputs, logs. This is | |
| # NOT under APP_ID β it's a separate hardcoded name. Cleaning | |
| # only APP_DATA leaves all user data behind, defeating the | |
| # fresh-emulation promise. | |
| if [ "$PLATFORM" = "macos" ]; then | |
| APP_DATA="$HOME/Library/Application Support/${APP_ID}" | |
| BACKEND_DATA="$HOME/Library/Application Support/OmniVoice" | |
| TAURI_LOGS="$HOME/Library/Logs/${APP_ID}" | |
| WEBKIT_DATA="$HOME/Library/WebKit/${APP_ID}" | |
| else | |
| # Linux: backend uses ~/.omnivoice (not XDG β see backend/core/config.py). | |
| APP_DATA="${XDG_DATA_HOME:-$HOME/.local/share}/${APP_ID}" | |
| BACKEND_DATA="$HOME/.omnivoice" | |
| TAURI_LOGS="${XDG_DATA_HOME:-$HOME/.local/share}/${APP_ID}/logs" | |
| WEBKIT_DATA="${XDG_DATA_HOME:-$HOME/.local/share}/${APP_ID}/webview" | |
| fi | |
| # HF cache β where downloaded models live | |
| HF_CACHE="${HF_HOME:-$HOME/.cache/huggingface}" | |
| # ββ Flags ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| SKIP_BUILD=false | |
| KEEP_DATA=false | |
| PILL_MODE=false | |
| for arg in "$@"; do | |
| case "$arg" in | |
| --skip-build) SKIP_BUILD=true ;; | |
| --keep-data) KEEP_DATA=true ;; | |
| --pill) PILL_MODE=true ;; | |
| -h|--help) | |
| echo "Usage: $0 [--skip-build] [--keep-data] [--pill]" | |
| echo "" | |
| echo " --skip-build Skip cargo build, use last compiled binary" | |
| echo " --keep-data Don't wipe app data (test upgrade path)" | |
| echo " --pill Launch in dictation-widget mode (no main window)" | |
| exit 0 | |
| ;; | |
| esac | |
| done | |
| # ββ Wipe app data for fresh-install simulation βββββββββββββββββββββββββββββ | |
| if [ "$KEEP_DATA" = false ]; then | |
| echo "π§Ή Cleaning all OmniVoice data for fresh prod emulation..." | |
| echo "" | |
| # 1. App data (Tauri bundle dir: post-install venv + webview state) | |
| if [ -d "${APP_DATA}" ]; then | |
| echo " β App data: ${APP_DATA}" | |
| rm -rf "${APP_DATA}" | |
| else | |
| echo " β App data: (already clean)" | |
| fi | |
| # 1b. Backend data (SQLite db, voice profiles, outputs, logs) | |
| # β separate dir hardcoded in backend/core/config.py, NOT under APP_ID. | |
| if [ -d "${BACKEND_DATA}" ]; then | |
| BD_SIZE=$(du -sh "${BACKEND_DATA}" 2>/dev/null | cut -f1) | |
| echo " β Backend data: ${BACKEND_DATA} (${BD_SIZE})" | |
| rm -rf "${BACKEND_DATA}" | |
| else | |
| echo " β Backend data: (already clean)" | |
| fi | |
| # 2. HF model cache (downloaded .safetensors, tokenizers, etc.) | |
| if [ -d "${HF_CACHE}" ]; then | |
| HF_SIZE=$(du -sh "${HF_CACHE}" 2>/dev/null | cut -f1) | |
| echo " β HF cache: ${HF_CACHE} (${HF_SIZE})" | |
| rm -rf "${HF_CACHE}" | |
| else | |
| echo " β HF cache: (already clean)" | |
| fi | |
| # 3. Tauri log dir | |
| if [ -d "${TAURI_LOGS}" ]; then | |
| echo " β Tauri logs: ${TAURI_LOGS}" | |
| rm -rf "${TAURI_LOGS}" | |
| else | |
| echo " β Tauri logs: (already clean)" | |
| fi | |
| # 4. WebView cache / local storage | |
| if [ -d "${WEBKIT_DATA}" ]; then | |
| echo " β WebKit data: ${WEBKIT_DATA}" | |
| rm -rf "${WEBKIT_DATA}" | |
| else | |
| echo " β WebKit data: (already clean)" | |
| fi | |
| echo "" | |
| echo " β All clean β next launch bootstraps from zero." | |
| else | |
| echo "π¦ Keeping existing app data (upgrade test mode)" | |
| fi | |
| # ββ Build debug binary βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if [ "$SKIP_BUILD" = false ]; then | |
| echo "" | |
| echo "π¨ Building debug bundle (this takes 1-3 min first time)..." | |
| # Remove stale bundle so we never accidentally launch old code | |
| if [ "$PLATFORM" = "macos" ]; then | |
| APP_BUNDLE="${TAURI_DIR}/target/debug/bundle/macos/${APP_NAME}.app" | |
| [ -d "$APP_BUNDLE" ] && rm -rf "$APP_BUNDLE" | |
| fi | |
| # Linux: linuxdeploy uses FUSE to mount itself; if FUSE is unavailable | |
| # (containers, some hardened kernels), set APPIMAGE_EXTRACT_AND_RUN=1 to | |
| # extract-and-run instead. Safe to always set on Linux. | |
| if [ "$PLATFORM" = "linux" ]; then | |
| export APPIMAGE_EXTRACT_AND_RUN=1 | |
| fi | |
| # The build creates the bundle successfully, but then may fail trying | |
| # to sign the updater artifact (no TAURI_SIGNING_PRIVATE_KEY) or to | |
| # run linuxdeploy. The binary itself is fine β tolerate known errors. | |
| BUILD_LOG=$(mktemp) | |
| cd frontend | |
| set +e | |
| bunx tauri build --debug 2>&1 | tee "$BUILD_LOG" | |
| BUILD_EXIT=$? | |
| set -e | |
| cd .. | |
| if [ $BUILD_EXIT -ne 0 ]; then | |
| # Known-harmless failures: | |
| # - Missing TAURI_SIGNING_PRIVATE_KEY (updater signing) | |
| # - "failed to run linuxdeploy" (AppImage bundling β binary still works) | |
| if grep -qi "TAURI_SIGNING_PRIVATE_KEY\|private key\|failed to run linuxdeploy\|failed to bundle" "$BUILD_LOG"; then | |
| echo "β οΈ Non-fatal bundle error β binary is fine (see above for details)." | |
| else | |
| echo "β Build failed with exit code $BUILD_EXIT" | |
| rm -f "$BUILD_LOG" | |
| exit $BUILD_EXIT | |
| fi | |
| fi | |
| rm -f "$BUILD_LOG" | |
| echo "β Build complete." | |
| else | |
| echo "βοΈ Skipping build (--skip-build)" | |
| fi | |
| # ββ Build launch args ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| LAUNCH_ARGS=() | |
| if [ "$PILL_MODE" = true ]; then | |
| LAUNCH_ARGS+=("--pill") | |
| echo "π Launch mode: pill (dictation-only widget, no main window)" | |
| fi | |
| # ββ Find and launch the app ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if [ "$PLATFORM" = "macos" ]; then | |
| APP_BUNDLE="${TAURI_DIR}/target/debug/bundle/macos/${APP_NAME}.app" | |
| BINARY="${TAURI_DIR}/target/debug/omnivoice-studio" | |
| if [ -d "$APP_BUNDLE" ]; then | |
| echo "" | |
| echo "π Launching ${APP_NAME} (.app bundle)..." | |
| echo " Bundle: ${APP_BUNDLE}" | |
| # macOS `open` needs -n to spawn a fresh instance, --args to forward flags. | |
| if [ ${#LAUNCH_ARGS[@]} -gt 0 ]; then | |
| open -n "$APP_BUNDLE" --args "${LAUNCH_ARGS[@]}" | |
| else | |
| open "$APP_BUNDLE" | |
| fi | |
| elif [ -f "$BINARY" ]; then | |
| echo "" | |
| echo "π Launching ${APP_NAME} (raw binary β no .app bundle)..." | |
| echo " Binary: ${BINARY}" | |
| "$BINARY" "${LAUNCH_ARGS[@]}" & | |
| else | |
| echo "β No bundle or binary found. Run without --skip-build first." | |
| exit 1 | |
| fi | |
| else | |
| # Linux: prefer AppImage, fall back to raw binary | |
| APPIMAGE=$(find "${TAURI_DIR}/target/debug/bundle/appimage" -name "*.AppImage" -type f 2>/dev/null | head -1) | |
| BINARY="${TAURI_DIR}/target/debug/omnivoice-studio" | |
| if [ -n "$APPIMAGE" ] && [ -f "$APPIMAGE" ]; then | |
| echo "" | |
| echo "π Launching ${APP_NAME} (AppImage)..." | |
| echo " AppImage: ${APPIMAGE}" | |
| chmod +x "$APPIMAGE" | |
| "$APPIMAGE" "${LAUNCH_ARGS[@]}" & | |
| elif [ -f "$BINARY" ]; then | |
| echo "" | |
| echo "π Launching ${APP_NAME} (raw binary)..." | |
| echo " Binary: ${BINARY}" | |
| "$BINARY" "${LAUNCH_ARGS[@]}" & | |
| else | |
| echo "β No AppImage or binary found. Run without --skip-build first." | |
| exit 1 | |
| fi | |
| fi | |
| echo " App data: ${APP_DATA}" | |
| echo "" | |
| echo "β App launched. Check the splash screen for bootstrap logs." | |
| if [ "$PILL_MODE" = true ]; then | |
| echo " To re-run pill mode without rebuilding: bun desktop-prod:run:pill" | |
| echo " To switch back to studio: bun desktop-prod:run" | |
| else | |
| echo " To re-run without rebuilding: bun desktop-prod:run" | |
| echo " To launch as dictation widget: bun desktop-prod:pill" | |
| fi | |