docs: document Windows build and packaging
This commit is contained in:
+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