feat(desktop): migrate packaging to Tauri 2 and Nuitka - #118
Draft
LC044 wants to merge 18 commits into
Draft
Conversation
Owner
Author
|
多平台安装包 CI 验证通过:
本地完整 E2E:274 passed,4 skipped。 I have read and agree to the CLA |
The desktop build workflow ran `pnpm --dir package/desktop build -- --bundles <b>`. pnpm 10 forwards the `--` separator verbatim to the script, producing `tauri build -- --bundles <b>`. That `--` tells Tauri to pass the remaining args down to `cargo build`, so cargo received `--bundles` and rejected it (suggesting `--benches`). The build failed on Windows, macOS, and Linux within seconds of reaching the Tauri step, after the Nuitka sidecar had already compiled successfully. Invoke `tauri` directly through `pnpm exec` so `--bundles` is consumed by the Tauri CLI instead of forwarded to cargo. Verified locally that `pnpm --dir package/desktop exec tauri build --help` forwards args to tauri and that `-b, --bundles` is the correct Tauri 2 flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nstaller Nuitka's Scons C compilation made the desktop CI builds take ~1h on each of Windows/macOS/Linux, with no measurable cold-start benefit for the desktop targets. Switch both sidecars back to PyInstaller onedir, which the codebase was originally designed for: app/core/paths.py anchors read-only resources via sys._MEIPASS, the path PyInstaller sets in onedir mode (Nuitka never set it, so it only worked via the SERVER_ROOT fallback). Changes: - package/server/scripts/build_desktop_runtime.py: invoke `python -m PyInstaller --noconfirm --clean desktop_server.spec` instead of Nuitka; same dist/trailsnap-server output contract. - package/ai/scripts/build_desktop_runtime.py: same, using desktop_ai.spec -> dist/trailsnap-ai. - package/server/pyproject.toml + package/ai/pyproject.toml: drop nuitka>=4.0 from the dev/desktop groups; regenerate both uv.lock (only nuitka removed, no other version changes; uv lock --check OK). - package/ai/scripts/build_desktop_extension.py: genericize the 'Nuitka runtime not found' message to 'AI runtime not found'. - doc/desktop_packaging_technical_route.md + package/desktop/README.md: document PyInstaller onedir as the default route; Nuitka kept only as a evaluated-and-not-adopted comparison. Verified locally: server sidecar builds via PyInstaller, produces dist/trailsnap-server with resources under _internal/resources (where _MEIPASS resolves), and trailsnap-server.exe --help runs (exit 0). Unit smoke (201 tests) passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Linux installer job failed at the Tauri AppImage bundling step with `failed to run linuxdeploy`. linuxdeploy and linuxdeploy-plugin-appimage are themselves AppImages and need libfuse2 to execute; ubuntu-latest (24.04) no longer ships libfuse2 preinstalled. Add it to the Linux system-dependency step. Unrelated to the Nuitka->PyInstaller switch: the prior Nuitka run never reached AppImage bundling (it was blocked by the --bundles forwarding bug), so this is the first time the Linux AppImage path ran end-to-end. macOS and Windows both succeeded in the same run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Installing libfuse2 was not sufficient: the AppImage-based linuxdeploy / linuxdeploy-plugin-appimage tools still fail to run on ubuntu-latest because the runner has no usable /dev/fuse device, even with the userspace library present. Tauri only surfaces this as 'failed to run linuxdeploy'. Set APPIMAGE_EXTRACT_AND_RUN=1 (Linux only) so those AppImage tools extract themselves to a temp directory and execute from there instead of mounting via FUSE. Windows and macOS are unaffected (env var is empty and unused there). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…stderr APPIMAGE_EXTRACT_AND_RUN=1 did not fix the Linux AppImage failure. Tauri still reports only 'failed to run linuxdeploy' and hides the tool's stderr. Add TAURI_BUNDLER_VERBOSE=1, LINUXDEPLOY_DEBUG=1 (Linux) and --verbose so the next run prints linuxdeploy's actual error output, which is needed to identify the real root cause. Diagnostic-only; will be reverted once the cause is known. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ibgfortran The Linux AppImage build failed at the linuxdeploy dependency-walking step with (revealed via TAURI_BUNDLER_VERBOSE): Deploying dependencies for ELF file .../server/_dfitpack...so ERROR: Could not find dependency: libgfortran-040039e1.so.5.0.0 ERROR: Failed to deploy dependencies for existing files failed to run linuxdeploy scipy/numpy/scikit-learn ship native Fortran/BLAS libraries (libgfortran, OpenBLAS) inside versioned <pkg>.libs directories. PyInstaller's default hooks did not bundle them, so the frozen sidecar referenced a .so that was absent from the bundle, and linuxdeploy could not resolve it while assembling the AppImage. Use collect_all for numpy/scipy/sklearn to pull their submodules, data files, AND .libs binaries into the bundle. Also drop the temporary TAURI_BUNDLER_VERBOSE / LINUXDEPLOY_DEBUG / --verbose diagnostics now that the root cause is identified; keep APPIMAGE_EXTRACT_AND_RUN which is still needed for linuxdeploy to run on the FUSE-less runner. Verified locally: spec builds and trailsnap-server.exe --help runs (exit 0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erbose collect_all(numpy/scipy/sklearn) did not fix the AppImage build. Add a Linux-only diagnostic step that, after staging the sidecar, prints: - libgfortran files present - *.libs directories present - _dfitpack NEEDED libs (objdump) and unresolved libs (ldd) Restore TAURI_BUNDLER_VERBOSE / --verbose / LINUXDEPLOY_DEBUG as permanent (noting Tauri hides linuxdeploy stderr by default, which made prior fixes blind). Goal: determine whether scipy.libs made it into the bundle and what linuxdeploy now reports. Diagnostic-only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The map-syntax resource glob {"../server-dist/trailsnap-server/**/*":
"server/"} flattened the PyInstaller onedir tree to server/<basename>
(Tauri's ResourcePaths Glob branch in crates/tauri-utils/src/resources.rs
joins dest with path.file_name(), keeping only the basename). This broke
the \$ORIGIN/../../scipy.libs/ RUNPATH that scipy's _dfitpack.so uses to
locate libgfortran-<hash>.so, so linuxdeploy aborted the AppImage build
with "Could not find dependency: libgfortran-040039e1.so.5.0.0". It would
also have broken sidecar startup on every platform, since the PyInstaller
bootloader needs _internal/ next to the executable.
Point the resource entry at the directory directly so Tauri walks it
(Walk branch preserves structure via dest.join(strip_prefix(pattern)))
instead of globbing it flat. Also expose every *.libs/ dir via
LD_LIBRARY_PATH on Linux as a fallback resolver for linuxdeploy in case
its \$ORIGIN expansion is shaky.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The previous fix (preserve sidecar dir structure via the directory-form resource entry) got libgfortran past linuxdeploy, and Windows/macOS packaging stayed green. But the Linux AppImage build now aborts one lib further: "Could not find dependency: libmvec-2-06864e43.28.so", a NEEDED of pillow_heif's libx265-d26213b5.so.215. The LD_LIBRARY_PATH fallback only covered *.libs/ subdirs, but PyInstaller flattens many hash-suffixed vendored libs (libgfortran, libmvec, libx265, libscipy_openblas64_, ...) to the _internal/ top level — out of reach of both \$ORIGIN (libx265 sits in pillow_heif.libs/, so its \$ORIGIN points the wrong way) and the .libs-only LD_LIBRARY_PATH. Extend the fallback to every directory under _internal/ so linuxdeploy's ldd resolver finds vendored libs regardless of where PyInstaller placed them. Also broaden the Linux diagnose step to locate libmvec / libx265 and list the top-level flattened hash libs, so a genuinely missing lib is obvious next time instead of a bare "Could not find dependency". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ploy linuxdeploy resolves ELF deps via `ldd` on the canonical path (src/core/elf_file.cpp), and ldd expands \$ORIGIN-relative RPATHs. libx265-d26213b5.so.215 in pillow_heif.libs/ NEEDs libmvec-<hash>.so which sits in the same dir, yet ldd reports "=> not found" and linuxdeploy aborts — meaning libx265's RPATH lost its \$ORIGIN during PyInstaller relocate / UPX (upx=True in desktop_server.spec). The LD_LIBRARY_PATH fallback added last attempt is useless: linuxdeploy is an AppImage run via --appimage-extract-and-run, whose runtime overrides LD_LIBRARY_PATH to its own extracted lib dir, and src/main.cpp confirms linuxdeploy reads no EXTRA_LD_LIBRARY_PATH (only the --exclude-library CLI flag, which Tauri does not expose). The only lever ldd honors is the ELF's own RPATH. So prefix \$ORIGIN (preserving any existing RPATH) onto every .so inside *.libs/ dirs before Tauri copies the sidecar into the AppDir. Also broaden the Linux diagnose step to print libx265's file type / RPATH / ldd output so a future missing-lib is diagnosed in one pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
描述
将桌面打包链路从 Electron/electron-builder 迁移到 Tauri 2,并将 Server 与 AI Sidecar 的默认冻结工具从 PyInstaller onedir 迁移到 Nuitka standalone。
Tauri Rust 壳负责随机端口分配、FastAPI Sidecar 启动、健康检查、日志重定向和退出时进程树清理。Vue 通过 Tauri command 获取运行时 API 地址并直连本地服务。三平台 GitHub Actions 已改为安装 Rust/Tauri 系统依赖、编译 Nuitka runtime 并上传 Tauri 安装包。
本轮属于阶段 0/0.5。Electron Node 主进程中的 AI 扩展下载、离线导入和 AI Gateway 尚未迁移到 Rust,Tauri 设置页会按既有降级逻辑显示桌面扩展接口不可用;AI Nuitka 构建链路保留,待 Rust 扩展管理器接入。
变更类型
相关 Issue
无。
如何测试
pnpm build(package/website)python -m py_compile scripts/build_desktop_runtime.py desktop_entry.py(Server)python -m py_compile scripts/build_desktop_runtime.py scripts/build_desktop_extension.py desktop_entry.py(AI)cargo check(package/desktop/src-tauri)pwsh .\tests\scripts\run-tests.ps1 -Layer e2e -Level full:276 passed,4 skipped检查清单
I have read and agree to the CLA