summaryrefslogtreecommitdiffstats
path: root/packaging/win/meshbay-node.spec
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-04 09:28:14 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-04 09:28:14 +0200
commit3ce52774760b222d94d78bc0118e9da2662a809f (patch)
tree52a16c7e05080869fbb17b83665b2fb2048b21e2 /packaging/win/meshbay-node.spec
parent220e6e701806213a576ce80fa655cd9cf4a51880 (diff)
downloadmeshbay-3ce52774760b222d94d78bc0118e9da2662a809f.tar.gz
feat: Windows installer (W4) — one per-user NSIS package, client + node
`npm run dist:win` produces MeshBay-Setup-<version>.exe: the Electron client and, beside it under resources/node-runtime/, the frozen meshbay-node daemon (meshbay-common inside it). No hub. Per-user, no elevation — matches the W3 constraint that a logon-triggered scheduled task needs admin. electron-builder / package.json build.win nsis, build/icon.ico, extraResources -> node-runtime/ build.nsis oneClick:false perMachine:false allowElevation:false allowToChangeInstallationDirectory:true dist:win -> packaging/win/build-win.ps1 (mirrors dist -> build-client.sh) packaging/win/ meshbay-node.spec + node-entry.py PyInstaller freeze of meshbay_node.daemon:main. The awkward deps (aiortc, av, aioquic, pydantic_core, uvicorn, watchdog, guessit, blake3, tzdata) are pulled in whole with collect_all — that list is expected to grow when a frozen run raises ModuleNotFoundError. build-node-runtime.ps1 throwaway venv -> pip install -> PyInstaller -> packages/meshbay-client/node-runtime/ (gitignored) build-win.ps1 Node>=22 check, npm ci, Electron bump, sync-ui, node runtime, electron-builder --win nsis bump-electron.mjs the Chromium-CVE "build against latest Electron" policy, out of the PS script (5.1 here-string terminator rules) README.md PyInstaller, not the python-embed zip: the frozen meshbay-node.exe is a genuine relocatable single binary, which is what src/main.js:findNodeBinary spawns (process.resourcesPath/node-runtime/meshbay-node.exe when packaged) and what the W3 autostart launcher points at. The embeddable zip needs pip to make that wrapper and the wrapper bakes in an absolute interpreter path. build/installer.nsh: on uninstall, taskkill meshbay-node.exe and delete the W3 Startup .vbs (it would point wscript at a deleted binary every sign-in). %LOCALAPPDATA%\meshbay\ — node.toml, keystore.enc — is never touched. ffmpeg is not bundled by default (node finds it on PATH); build-win.ps1 -FfmpegDir copies ffmpeg.exe/ffprobe.exe in for a self-contained installer. Verified on the Windows guest: PyInstaller freeze builds first try (node-runtime 147 MB), frozen `meshbay-node status` talks to the live daemon's loopback API; electron-builder --win nsis produces MeshBay-Setup-0.1.0.exe (155 MB), oneClick/perMachine flags applied, node-runtime bundled at the path findNodeBinary expects. test_packaging_win.py (14) pins the config invariants and the NSIS <-> platform.py autostart seam. Node suite 798 pass / 34 skip. Open: Authenticode signing (13.9 — unsigned => SmartScreen), Windows CI (18.3), electron-updater. First clean-machine install + DPAPI + autostart round-trip is a manual check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'packaging/win/meshbay-node.spec')
-rw-r--r--packaging/win/meshbay-node.spec126
1 files changed, 126 insertions, 0 deletions
diff --git a/packaging/win/meshbay-node.spec b/packaging/win/meshbay-node.spec
new file mode 100644
index 0000000..f1f994d
--- /dev/null
+++ b/packaging/win/meshbay-node.spec
@@ -0,0 +1,126 @@
+# PyInstaller spec for the bundled Windows node daemon.
+#
+# Output: dist/meshbay-node/meshbay-node.exe (--onedir, relocatable)
+# Consumed by: packaging/win/build-node-runtime.ps1, which moves the tree to
+# packages/meshbay-client/node-runtime/ for electron-builder to carry as an
+# extraResource.
+#
+# The dependency set (aiortc, av, aioquic, pydantic-core, uvicorn, watchdog,
+# guessit, blake3, ...) has C/Rust extensions and does dynamic imports, so
+# each awkward package is pulled in whole with collect_all rather than left to
+# PyInstaller's static analysis. Add to COLLECT_ALL / HIDDEN / COPY_META when a
+# frozen run raises ModuleNotFoundError — that is the expected way this list grows.
+
+from PyInstaller.utils.hooks import collect_all, copy_metadata
+
+# Packages taken in full: data files + shared libs + every submodule.
+COLLECT_ALL = [
+ "meshbay_node",
+ "meshbay_common",
+ "aiortc",
+ "aioice",
+ "aioquic",
+ "av",
+ "cryptography",
+ "pylibsrtp",
+ "pyee",
+ "google_crc32c",
+ "pydantic",
+ "pydantic_core",
+ "fastapi",
+ "starlette",
+ "uvicorn",
+ "anyio",
+ "sniffio",
+ "h11",
+ "websockets",
+ "httpx",
+ "httpcore",
+ "watchdog",
+ "guessit",
+ "rebulk",
+ "babelfish",
+ "mutagen",
+ "PIL",
+ "blake3",
+ "msgpack",
+ "zstandard",
+ "aiosqlite",
+ "jwt",
+ "dns",
+ "tzdata",
+]
+
+# importlib.metadata lookups that survive freezing only if the dist-info is copied.
+COPY_META = [
+ "meshbay-node",
+ "meshbay-common",
+ "guessit",
+ "rebulk",
+ "babelfish",
+ "uvicorn",
+ "fastapi",
+]
+
+# Imports PyInstaller's static pass misses even with collect_all.
+HIDDEN = [
+ "uvicorn.logging",
+ "uvicorn.loops.auto",
+ "uvicorn.loops.asyncio",
+ "uvicorn.protocols.http.auto",
+ "uvicorn.protocols.http.h11_impl",
+ "uvicorn.protocols.websockets.auto",
+ "uvicorn.protocols.websockets.websockets_impl",
+ "uvicorn.lifespan.on",
+]
+
+datas, binaries, hiddenimports = [], [], list(HIDDEN)
+for pkg in COLLECT_ALL:
+ d, b, h = collect_all(pkg)
+ datas += d
+ binaries += b
+ hiddenimports += h
+for dist in COPY_META:
+ datas += copy_metadata(dist)
+
+a = Analysis(
+ ["node-entry.py"],
+ pathex=[],
+ binaries=binaries,
+ datas=datas,
+ hiddenimports=hiddenimports,
+ hookspath=[],
+ hooksconfig={},
+ runtime_hooks=[],
+ excludes=["tkinter", "pytest", "ruff", "piexif", "IPython"],
+ noarchive=False,
+)
+pyz = PYZ(a.pure)
+
+exe = EXE(
+ pyz,
+ a.scripts,
+ [],
+ exclude_binaries=True,
+ name="meshbay-node",
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=False,
+ console=True,
+ disable_windowed_traceback=False,
+ argv_emulation=False,
+ target_arch=None,
+ codesign_identity=None,
+ entitlements_file=None,
+ icon="../../packages/meshbay-client/build/icon.ico",
+)
+coll = COLLECT(
+ exe,
+ a.binaries,
+ a.datas,
+ strip=False,
+ upx=False,
+ upx_exclude=[],
+ name="meshbay-node",
+)