docs: document Windows build and packaging
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
**English** · [中文](README.zh-CN.md)
|
||||
|
||||
A desktop SSH session manager for Linux — connection tree, tabbed terminals,
|
||||
A desktop SSH session manager for Windows and Linux — connection tree, tabbed terminals,
|
||||
tunnels, local shell and serial.
|
||||
|
||||
Think Xshell's session management with tmux's multiplexing, local-first: your
|
||||
@@ -28,7 +28,24 @@ Early. Everything below reflects what actually works today.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Build toolchain (for native modules)
|
||||
### Windows
|
||||
|
||||
Node.js 22.12 or newer is required. The dependencies ship Windows x64/ARM64
|
||||
prebuilds for `better-sqlite3` and `node-pty`, so Visual Studio is normally not
|
||||
required. If `npm install` still falls back to `node-gyp`, use the verified
|
||||
no-compile installation path:
|
||||
|
||||
```powershell
|
||||
npm ci --ignore-scripts
|
||||
node node_modules/electron/install.js
|
||||
```
|
||||
|
||||
With this path, `ssh2` uses its JavaScript crypto implementation. Functionality
|
||||
is unchanged, but the optional native acceleration is absent. To compile every
|
||||
optional native module, install Python 3 and Visual Studio Build Tools with the
|
||||
Desktop development with C++ workload.
|
||||
|
||||
### Linux build toolchain (for native modules)
|
||||
|
||||
```sh
|
||||
sudo apt install build-essential python3 pkg-config libudev-dev
|
||||
@@ -39,7 +56,8 @@ M8, but installing it up front avoids a confusing build failure later.
|
||||
|
||||
### Runtime keyring
|
||||
|
||||
Credential storage depends on an OS keyring daemon. Verify one is running:
|
||||
Windows uses DPAPI and needs no additional keyring service. On Linux,
|
||||
credential storage depends on an OS keyring daemon. Verify one is running:
|
||||
|
||||
```sh
|
||||
ps -e -o comm= | grep -E 'gnome-keyring|kwalletd'
|
||||
@@ -82,7 +100,8 @@ Other scripts:
|
||||
npm test # vitest — see "Testing" below
|
||||
npm run typecheck # main/preload and renderer are checked separately
|
||||
npm run build # production bundles into out/
|
||||
npm run dist # AppImage + deb into release/
|
||||
npm run dist # package for the current platform into release/
|
||||
npm run dist:win # explicitly build the Windows x64 NSIS installer
|
||||
npm run rebuild:native # rebuild native modules against Electron's ABI
|
||||
```
|
||||
|
||||
@@ -119,6 +138,12 @@ npm run build
|
||||
npm run smoke
|
||||
```
|
||||
|
||||
On Windows PowerShell, replace the second line with:
|
||||
|
||||
```powershell
|
||||
.\node_modules\electron\dist\electron.exe --remote-debugging-port=9222 .
|
||||
```
|
||||
|
||||
`scripts/ipc-smoke.mjs` attaches to the running app over the Chrome DevTools
|
||||
Protocol and calls `window.nexusterm` — the same object the UI calls. That makes
|
||||
it a real end-to-end check of preload bridge → `ipcRenderer.invoke` → handler →
|
||||
@@ -127,7 +152,8 @@ UI by hand.
|
||||
|
||||
It currently verifies folder and host creation, nesting, rename, moving to the
|
||||
root layer, refusal of a folder move into its own descendant, rejection of an
|
||||
invalid payload, and that it cleans up after itself.
|
||||
invalid payload, opening and closing a local PTY, and that it cleans up after
|
||||
itself.
|
||||
|
||||
### SSH, against a real server
|
||||
|
||||
@@ -156,39 +182,49 @@ remote wrote — which is the return path proven separately.
|
||||
## Packaging
|
||||
|
||||
```sh
|
||||
npm run dist # AppImage + deb into release/
|
||||
npm run dist # package for the current platform into release/
|
||||
npm run dist:win # Windows x64 NSIS installer into release/
|
||||
npm run verify:package # check the result can actually run
|
||||
```
|
||||
|
||||
The Windows installer is written to `release/NexusTerm-<version>-x64.exe`. The
|
||||
uninstalled layout is kept in `release/win-unpacked/` for quick testing before
|
||||
running the installer.
|
||||
|
||||
`scripts/verify-package.mjs` exists because a package that is broken in the
|
||||
ways that matter still builds, still installs, and still starts. Native modules
|
||||
cannot be loaded from inside an asar archive, and when they are left there the
|
||||
failure appears on a user's machine the first time they open a terminal — not
|
||||
in CI, and not in `npm test`. So the script extracts the AppImage and asserts
|
||||
that each native binary really is in `app.asar.unpacked`.
|
||||
in CI, and not in `npm test`. The script checks a Linux AppImage or the Windows
|
||||
`win-unpacked` directory and asserts that each current-platform native binary
|
||||
really is in `app.asar.unpacked`.
|
||||
|
||||
Two things it checks that are easy to get wrong:
|
||||
|
||||
- **`ssh2` carries a native module.** `sshcrypto.node` is easy to miss because
|
||||
the library reads as pure JavaScript — and because a failed load falls back
|
||||
to a JS implementation silently, so nothing would ever report it.
|
||||
- **`node-pty` must be unpacked whole, not just its `.node` files.** On macOS
|
||||
and Windows it ships `spawn-helper`, a real executable loaded from disk rather
|
||||
than `dlopen`ed, which stays trapped in the archive if only `*.node` is
|
||||
unpacked.
|
||||
- **`ssh2` carries an optional native module.** `sshcrypto.node` is easy to miss
|
||||
because the library reads as pure JavaScript. A no-compile Windows install
|
||||
warns when it is absent and uses the JS implementation.
|
||||
- **`node-pty` must be unpacked whole, not just its `.node` files.** Windows
|
||||
also needs `winpty-agent.exe`, `OpenConsole.exe`, and their DLLs; unpacking
|
||||
only `*.node` produces a terminal that fails after installation.
|
||||
|
||||
`npmRebuild` is off: every native module here is built on `node-addon-api`, so
|
||||
the binaries from `npm install` load unchanged in Electron's Node. Rebuilding
|
||||
would cost time and risk replacing working binaries for no gain.
|
||||
|
||||
Both `.deb` metadata and `desktopName` are set, and the AppImage's
|
||||
`.desktop` entry is generated from `linux.desktop.entry`.
|
||||
`.desktop` entry is generated from `linux.desktop.entry`. Windows uses an NSIS
|
||||
installer with a selectable install directory plus Start menu and desktop
|
||||
shortcuts.
|
||||
|
||||
### Packaging notes for whoever picks this up
|
||||
|
||||
- The `author`, `homepage` and `maintainer` fields are **placeholders**
|
||||
(`ylhan@example.com`, `example.com`). They are required by the deb format and
|
||||
must be replaced before distributing.
|
||||
- The Windows installer is currently **not digitally signed**, so SmartScreen
|
||||
may report an unknown publisher. Configure a code-signing certificate before
|
||||
public distribution.
|
||||
- `asarUnpack` lists `node_modules/@serialport/**` for a backend that does not
|
||||
exist yet. Harmless now; load-bearing once the serial transport lands.
|
||||
|
||||
@@ -224,7 +260,8 @@ need a module declaration — `"types": ["vite/client"]` supplies it.
|
||||
|
||||
**`better-sqlite3` ships N-API prebuilds.**
|
||||
|
||||
`node_modules/better-sqlite3/prebuilds/` contains `linux-x64.node` and friends,
|
||||
`node_modules/better-sqlite3/prebuilds/` contains `linux-x64.node`,
|
||||
`win32-x64.node`, and friends,
|
||||
built with `node-addon-api`. Because N-API is ABI-stable across Node and Electron
|
||||
versions, this normally removes the need for an `electron-rebuild` step. If a
|
||||
native load ever fails at runtime, check here first before reaching for a
|
||||
@@ -232,7 +269,8 @@ rebuild.
|
||||
|
||||
**The Electron binary may need a manual download.**
|
||||
|
||||
If `npm install` completes but `node_modules/electron/dist/electron` is missing,
|
||||
If `npm install` completes but `node_modules/electron/dist/electron` (or
|
||||
`electron.exe` on Windows) is missing,
|
||||
the package's postinstall was skipped. Run:
|
||||
|
||||
```sh
|
||||
@@ -350,13 +388,14 @@ multi-byte sequences across reads rather than mangling them. Re-encoding with
|
||||
uniform — everything above speaks bytes.
|
||||
|
||||
**node-pty needs no rebuild against Electron.** It is built on `node-addon-api`,
|
||||
so the binary compiled during `npm install` loads unchanged in Electron's Node —
|
||||
verified by spawning a real PTY from inside Electron 44. This retired what the
|
||||
plan had ranked as the project's highest risk.
|
||||
so the binary selected or compiled during dependency installation loads
|
||||
unchanged in Electron's Node — verified by spawning a real PTY from inside
|
||||
Electron 44. This retired what the plan had ranked as the project's highest
|
||||
risk.
|
||||
|
||||
On Linux there is also no `spawn-helper`: that exists only for macOS and
|
||||
Windows. The `asarUnpack` rule covers the whole package anyway, so the
|
||||
executable surviving the asar boundary is handled without a special case.
|
||||
Linux has no additional terminal helper executable. The Windows prebuilds carry
|
||||
`winpty-agent.exe` and `OpenConsole.exe`; the `asarUnpack` rule covers the whole
|
||||
package so both survive the asar boundary.
|
||||
|
||||
### The sidebar, and where sessions live
|
||||
|
||||
|
||||
+42
-14
@@ -1,6 +1,6 @@
|
||||
# NexusTerm
|
||||
|
||||
Linux 桌面上的 SSH 会话管理器 —— 连接树、按主机分组的标签页、端口转发、本地终端与串口。
|
||||
Windows 与 Linux 桌面上的 SSH 会话管理器 —— 连接树、按主机分组的标签页、端口转发、本地终端与串口。
|
||||
|
||||
可以理解为:Xshell 的会话管理 + tmux 的多路复用,且本地优先 —— 你的连接资产不出本机,凭据由操作系统钥匙串保管,而不是托管在厂商的云上。
|
||||
|
||||
@@ -25,7 +25,22 @@ Linux 桌面上的 SSH 会话管理器 —— 连接树、按主机分组的标
|
||||
|
||||
## 前置条件
|
||||
|
||||
### 构建工具链(原生模块需要)
|
||||
### Windows
|
||||
|
||||
需要 Node.js 22.12 或更高版本。依赖包自带 Windows x64/ARM64 的
|
||||
`better-sqlite3` 与 `node-pty` 预编译文件,因此通常不需要 Visual Studio。
|
||||
若 `npm install` 仍回退到 `node-gyp`,可以使用已经验证过的无编译安装方式:
|
||||
|
||||
```powershell
|
||||
npm ci --ignore-scripts
|
||||
node node_modules/electron/install.js
|
||||
```
|
||||
|
||||
这种方式下 `ssh2` 使用 JavaScript 加密实现;功能不受影响,但没有可选的原生加速。
|
||||
若要编译所有可选原生模块,请安装 Python 3 和带“使用 C++ 的桌面开发”工作负载的
|
||||
Visual Studio Build Tools。
|
||||
|
||||
### Linux 构建工具链(原生模块需要)
|
||||
|
||||
```sh
|
||||
sudo apt install build-essential python3 pkg-config libudev-dev
|
||||
@@ -35,7 +50,7 @@ sudo apt install build-essential python3 pkg-config libudev-dev
|
||||
|
||||
### 运行时钥匙串
|
||||
|
||||
凭据存储依赖系统钥匙串守护进程。先确认它在运行:
|
||||
Windows 使用 DPAPI,不需要额外的钥匙串服务。Linux 上的凭据存储依赖系统钥匙串守护进程;先确认它在运行:
|
||||
|
||||
```sh
|
||||
ps -e -o comm= | grep -E 'gnome-keyring|kwalletd'
|
||||
@@ -72,7 +87,8 @@ npm run dev
|
||||
npm test # vitest,见下文「测试」
|
||||
npm run typecheck # 主进程/preload 与渲染进程分开检查
|
||||
npm run build # 产物打包到 out/
|
||||
npm run dist # AppImage + deb 到 release/
|
||||
npm run dist # 当前平台的安装包到 release/
|
||||
npm run dist:win # 明确构建 Windows x64 NSIS 安装器
|
||||
npm run rebuild:native # 按 Electron 的 ABI 重建原生模块
|
||||
```
|
||||
|
||||
@@ -98,9 +114,15 @@ npm run build
|
||||
npm run smoke
|
||||
```
|
||||
|
||||
Windows PowerShell 中第二行改为:
|
||||
|
||||
```powershell
|
||||
.\node_modules\electron\dist\electron.exe --remote-debugging-port=9222 .
|
||||
```
|
||||
|
||||
`scripts/ipc-smoke.mjs` 通过 Chrome DevTools 协议接入运行中的应用,调用 `window.nexusterm`——**和界面调用的是同一个对象**。于是它成了对 preload 桥 → `ipcRenderer.invoke` → 处理器 → zod 校验 → 仓储 → SQLite → 回包的完整端到端检查,且不需要用手去点界面。
|
||||
|
||||
它目前验证:创建文件夹与主机、嵌套、重命名、移回根层、拒绝把文件夹移入自己的子孙、拒绝非法载荷,以及跑完自己清理干净。
|
||||
它目前验证:创建文件夹与主机、嵌套、重命名、移回根层、拒绝把文件夹移入自己的子孙、拒绝非法载荷、本地 PTY 能启动和关闭,以及跑完自己清理干净。
|
||||
|
||||
### SSH:对真实服务端
|
||||
|
||||
@@ -123,24 +145,29 @@ npm run e2e:ssh
|
||||
## 打包
|
||||
|
||||
```sh
|
||||
npm run dist # AppImage + deb 到 release/
|
||||
npm run dist # 当前平台的安装包到 release/
|
||||
npm run dist:win # Windows x64 NSIS 安装器到 release/
|
||||
npm run verify:package # 检查产物是否真能运行
|
||||
```
|
||||
|
||||
`scripts/verify-package.mjs` 的存在理由:**一个在关键之处坏掉的包,照样能构建、能安装、能启动**。原生模块无法从 asar 归档内部加载,而当它们被留在里面时,故障出现在**用户机器上、第一次打开终端的那一刻**——不在 CI 里,也不在 `npm test` 里。所以这个脚本会解开 AppImage,逐一断言每个原生二进制**确实在 `app.asar.unpacked` 里**。
|
||||
Windows 安装器输出为 `release/NexusTerm-<version>-x64.exe`;未安装的展开版本位于
|
||||
`release/win-unpacked/`,便于在制作安装器前快速试运行。
|
||||
|
||||
`scripts/verify-package.mjs` 的存在理由:**一个在关键之处坏掉的包,照样能构建、能安装、能启动**。原生模块无法从 asar 归档内部加载,而当它们被留在里面时,故障出现在**用户机器上、第一次打开终端的那一刻**——不在 CI 里,也不在 `npm test` 里。脚本会检查 Linux AppImage 或 Windows 的 `win-unpacked` 目录,逐一断言当前平台的原生二进制**确实在 `app.asar.unpacked` 里**。
|
||||
|
||||
它检查的两件事都属于容易搞错的那类:
|
||||
|
||||
- **`ssh2` 带原生模块。** `sshcrypto.node` 容易被忽略,因为这个库读起来像纯 JavaScript——而且加载失败时它会**静默**退回 JS 实现,所以没有任何东西会报告这件事。
|
||||
- **`node-pty` 必须整包解出,而不只是它的 `.node` 文件。** 在 macOS 和 Windows 上它带一个 `spawn-helper`——一个从磁盘加载的真实可执行文件,不是 `dlopen` 进来的;只解包 `*.node` 会让它困在归档里。
|
||||
- **`ssh2` 带可选原生模块。** `sshcrypto.node` 容易被忽略,因为这个库读起来像纯 JavaScript;Windows 无编译安装缺少它时会明确告警,并使用 JS 实现。
|
||||
- **`node-pty` 必须整包解出,而不只是它的 `.node` 文件。** Windows 上还需要 `winpty-agent.exe`、`OpenConsole.exe` 与配套 DLL;只解包 `*.node` 会让终端在安装后失效。
|
||||
|
||||
`npmRebuild` 是关闭的:这里每个原生模块都基于 `node-addon-api`,所以 `npm install` 时编译出的二进制在 Electron 的 Node 下可原样加载。重建只会花时间、并冒着用不同方式编译的产物替换掉可用产物的风险,没有任何收益。
|
||||
|
||||
`.deb` 元数据与 `desktopName` 都已设置,AppImage 的 `.desktop` 条目由 `linux.desktop.entry` 生成。
|
||||
`.deb` 元数据与 `desktopName` 都已设置,AppImage 的 `.desktop` 条目由 `linux.desktop.entry` 生成。Windows 使用可选择安装目录的 NSIS 安装器,并创建开始菜单和桌面快捷方式。
|
||||
|
||||
### 给接手的人
|
||||
|
||||
- `author`、`homepage`、`maintainer` 三个字段目前是**占位值**(`ylhan@example.com`、`example.com`)。deb 格式要求这些字段存在,正式分发前必须替换。
|
||||
- Windows 安装包目前**没有数字签名**,因此 SmartScreen 可能显示“未知发布者”。正式分发前应配置代码签名证书。
|
||||
- `asarUnpack` 里列了 `node_modules/@serialport/**`,而那个后端尚不存在。现在无害;串口传输落地后它就是承重的了。
|
||||
|
||||
## 工具链约束
|
||||
@@ -165,11 +192,11 @@ Electron 44.4.1 内置 Node 24.21.0 与 Chrome 152——这是在运行时确认
|
||||
|
||||
**`better-sqlite3` 自带 N-API 预编译产物。**
|
||||
|
||||
`node_modules/better-sqlite3/prebuilds/` 里有 `linux-x64.node` 等文件,基于 `node-addon-api` 构建。由于 N-API 的 ABI 在 Node 与 Electron 版本间稳定,这通常免掉了 `electron-rebuild` 这一步。若某次运行时原生模块加载失败,先查这里,再去考虑重建。
|
||||
`node_modules/better-sqlite3/prebuilds/` 里有 `linux-x64.node`、`win32-x64.node` 等文件,基于 `node-addon-api` 构建。由于 N-API 的 ABI 在 Node 与 Electron 版本间稳定,这通常免掉了 `electron-rebuild` 这一步。若某次运行时原生模块加载失败,先查这里,再去考虑重建。
|
||||
|
||||
**Electron 二进制可能需要手动下载。**
|
||||
|
||||
如果 `npm install` 完成但 `node_modules/electron/dist/electron` 不存在,说明该包的 postinstall 被跳过了。执行:
|
||||
如果 `npm install` 完成但 `node_modules/electron/dist/electron`(Windows 为 `electron.exe`)不存在,说明该包的 postinstall 被跳过了。执行:
|
||||
|
||||
```sh
|
||||
node node_modules/electron/install.js
|
||||
@@ -235,9 +262,10 @@ preload contextBridge,窄白名单 —— 这个文件**就是**
|
||||
|
||||
两者只在一个地方值得注意地不同:ssh2 交出 `Buffer`,node-pty 交出 `string`——因为它自己解码 PTY 流,能在跨读取时接住被切断的多字节序列而不是损坏它。因此 `Buffer.from(chunk, 'utf8')` 是无损的,也就让边界保持一致:上层一律说字节。
|
||||
|
||||
**node-pty 不需要针对 Electron 重建。** 它基于 `node-addon-api`,所以 `npm install` 时编译的二进制在 Electron 的 Node 下原样加载——这一点已在 Electron 44 内部实际拉起一个 PTY 验证过。这退休了计划中原先排名第一的风险。
|
||||
**node-pty 不需要针对 Electron 重建。** 它基于 `node-addon-api`,所以依赖安装时选取或编译的二进制在 Electron 的 Node 下原样加载——这一点已在 Electron 44 内部实际拉起一个 PTY 验证过。这退休了计划中原先排名第一的风险。
|
||||
|
||||
Linux 上也没有 `spawn-helper`:那只存在于 macOS 和 Windows。`asarUnpack` 规则无论如何覆盖整个包,所以"可执行文件能否穿过 asar 边界"无需特例。
|
||||
Linux 上没有额外的终端辅助可执行文件;Windows 预编译包则带有
|
||||
`winpty-agent.exe` 和 `OpenConsole.exe`。`asarUnpack` 规则覆盖整个包,所以这些文件都能正确穿过 asar 边界。
|
||||
|
||||
### 侧边栏,以及会话住在哪里
|
||||
|
||||
|
||||
Reference in New Issue
Block a user