package-win.sh 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. #!/bin/bash
  2. # Build ../dist/DoTouchAI-win/ + a portable zip from the cached Windows
  3. # Electron binary + the cross-platform app sources. Win10/11 x64 target.
  4. #
  5. # What this produces (and what it cannot, on a macOS build host):
  6. # - a runnable portable folder: unzip → run "DoTouchAI.exe"
  7. # - the .exe keeps Electron's default icon (rcedit is a Windows tool we
  8. # cannot run on macOS); the window + tray use assets/icon.ico at runtime
  9. # - unsigned → SmartScreen warns on first launch ("更多信息 → 仍要运行")
  10. # - execution must be verified on a real Windows machine; this host cannot
  11. # run .exe, so the build is structural-only (files present + JS compiles)
  12. set -euo pipefail
  13. cd "$(dirname "$0")/.."
  14. VERSION="$(node -p "require('./package.json').version")"
  15. OUT="../dist"
  16. APP="$OUT/DoTouchAI-win"
  17. ZIP="$OUT/DoTouchAI-${VERSION}-win-x64.zip"
  18. # Locate the cached Windows Electron binary (same version pinned in devDeps).
  19. ELECTRON_ZIP=$(ls "$HOME/Library/Caches/electron/"*/electron-v*-win32-x64.zip 2>/dev/null | head -1)
  20. if [ -z "$ELECTRON_ZIP" ]; then
  21. echo "ERROR: electron win32-x64 zip not in cache. Run: npm i -D electron@42.3.3" >&2
  22. exit 1
  23. fi
  24. # Windows-native sherpa-onnx binary (offline ASR). Fetch on first build, cache.
  25. SHERPA_CACHE="../.sherpa-win-cache"
  26. if [ ! -d "$SHERPA_CACHE/sherpa-onnx-win-x64" ]; then
  27. echo "fetching sherpa-onnx-win-x64 (one-time)…"
  28. rm -rf "$SHERPA_CACHE"
  29. mkdir -p "$SHERPA_CACHE"
  30. ( cd "$SHERPA_CACHE" && npm pack sherpa-onnx-win-x64@1.13.6 \
  31. --pack-destination "$SHERPA_CACHE" --cache "$SHERPA_CACHE/npm-cache" >/dev/null 2>&1 )
  32. tar -xzf "$SHERPA_CACHE"/sherpa-onnx-win-x64-*.tgz -C "$SHERPA_CACHE"
  33. mv "$SHERPA_CACHE/package" "$SHERPA_CACHE/sherpa-onnx-win-x64"
  34. rm -f "$SHERPA_CACHE"/*.tgz
  35. fi
  36. rm -rf "$APP"
  37. mkdir -p "$APP"
  38. # 1. Windows Electron runtime (electron.exe + resources + locales + DLLs).
  39. unzip -q "$ELECTRON_ZIP" -d "$APP"
  40. mv "$APP/electron.exe" "$APP/DoTouchAI.exe"
  41. # 2. Swap our app for Electron's default shell.
  42. rm -f "$APP/resources/default_app.asar"
  43. mkdir -p "$APP/resources/app/assets" "$APP/resources/app/plugins"
  44. cp main.js env.js service.js updater.js plugins.js package.json "$APP/resources/app/"
  45. cp assets/icon.ico "$APP/resources/app/assets/"
  46. node -e "require('fs').writeFileSync('$APP/resources/app/build.json', JSON.stringify({version:'$VERSION', platform:'win32-x64', builtAt:new Date().toLocaleString('zh-CN',{hour12:false})}, null, 2) + '\n')"
  47. # 3. Bundled plugins (voice suite) — Windows native binary variant.
  48. VOICE_SRC="/Users/eastudio/Documents/DSWorkSpace"
  49. DEP_SRC="$HOME/.dsh/profiles/web/node_modules"
  50. BUNDLED="$APP/resources/app/plugins"
  51. cp -R "$VOICE_SRC/dsh-voice-client" "$BUNDLED/dsh-voice-client"
  52. rm -rf "$BUNDLED/dsh-voice-client/node_modules"
  53. cp -R "$VOICE_SRC/dsh-voice-server" "$BUNDLED/dsh-voice-server"
  54. rm -rf "$BUNDLED/dsh-voice-server/node_modules"
  55. cp -R "$DEP_SRC/sherpa-onnx-node" "$BUNDLED/sherpa-onnx-node"
  56. cp -R "$SHERPA_CACHE/sherpa-onnx-win-x64" "$BUNDLED/sherpa-onnx-win-x64" # .node + DLLs
  57. cp -R "$DEP_SRC/ws" "$BUNDLED/ws"
  58. node -e "require('fs').writeFileSync('$BUNDLED/manifest.json', JSON.stringify({
  59. 'dsh-voice-client': { id: 'dsh-voice-client', name: 'dsh-voice-client' },
  60. 'dsh-voice-server': { id: 'dsh-voice-server', name: 'dsh-voice-server/lib/index-v6.js' },
  61. 'sherpa-onnx-node': { dep: true },
  62. 'sherpa-onnx-win-x64': { dep: true },
  63. 'ws': { dep: true }
  64. }, null, 2) + '\n')"
  65. echo "app: $(du -sh "$APP" | cut -f1) — exe + plugins: $(ls "$BUNDLED" | tr '\n' ' ')"
  66. # 4. Portable zip (python3 — no resource-fork noise, cross-platform).
  67. rm -f "$ZIP"
  68. python3 -c "
  69. import zipfile, os
  70. src='$APP'
  71. with zipfile.ZipFile('$ZIP','w',zipfile.ZIP_DEFLATED, compresslevel=6) as z:
  72. for root,dirs,files in os.walk(src):
  73. for f in files:
  74. p=os.path.join(root,f)
  75. arc=os.path.relpath(p, os.path.dirname(src))
  76. z.write(p, arc)
  77. "
  78. echo "built: $ZIP ($(du -sh "$ZIP" | cut -f1))"