aps-agent/docs/development/nsis-smoke.md

10 KiB
Raw Permalink Blame History

NSIS 安装包临时目录安装冒烟

round-44 并行 sub agent 方向 GG(矩阵 99 / 118 本地衔接部分)。 执行日期:2026-08-02;环境:Windows(PowerShell),Node v22.13.0。

1. 验收矩阵与本方向范围

矩阵行 条目 本地可测部分 剩余(外部项)
99 Defender 和真实 APS 求解通过 衔接 round-43 CC:Defender 静态扫描 4/4 clean(见 packaging-smoke.md);真实 APS 求解由四引擎黄金测试覆盖(既有体系,非本方向交付) Defender for Endpoint 门户/API 门禁;实时防护真实拦截演练(需受控样本与策略配合)
118 干净离线机安装冒烟 本方向补"真装冒烟":NSIS 静默安装到随机临时目录 + 布局/内容检查 + 卸载清理(本地实测 2 次 PASS,见 §6) 真实用户机安装(含 %LOCALAPPDATA%\Programs 落点、开始菜单/桌面快捷方式体验);干净离线机实机安装 + 冷启动 + 升级/回滚验收,需外部验证机

硬性纪律遵守情况:未修改 server/、apps/desktop/、packaging/**(仅只读引用);未 commit/push/merge;未重启/占用 8003/5173。

2. 涉及文件

新增(本方向写范围):

  • scripts/nsis-install-smoke.mjs —— NSIS 临时目录安装冒烟脚本
  • tests/node/nsis-smoke.test.mjs —— 可测逻辑的 node 单测
  • docs/development/nsis-smoke.md —— 本文档

只读引用:

  • scripts/offline-install-check.mjs —— 复用 sha256File / readDesktopConfig / resolveInstallerName / formatBytes(round-43 CC)
  • build/offline-check/offline-install-manifest.json —— 期望 sha256 来源(nsis-installer 条目)
  • apps/desktop/updater.cjs —— nsisInstall 参数约定(/S 静默;/D= 安装目录须为最后参数、不带引号),本轮真装实测验证该约定可行
  • apps/desktop/release/aps-agent-desktop-0.1.0.exe —— 本轮实测的 NSIS 安装包(163.1 MB,sha256 044c4a83aaf28476a1e2405a7a08185b5aff0dfab27f0299b8bcf16c8d32bf21)

3. 脚本原理与用法

原理:在系统临时目录下 fs.mkdtemp 随机目录,用 NSIS 参数 /S /D=<临时目录> 静默安装(/D 是最后参数、不带引号),校验安装后布局(主 exe + resources/app.asar + resources/sidecar/aps-sidecar.exe + 卸载器),可选启动主 exe 数秒做健康冒烟,最后卸载/删除临时目录。全程不触碰真实安装位置。

用法:

node scripts/nsis-install-smoke.mjs [installer.exe] [options]
node scripts/nsis-install-smoke.mjs --installer <path> --run-uninstaller
node scripts/nsis-install-smoke.mjs --self-test
选项 说明
位置参数 / --installer <path> 显式指定安装包路径(显式优先,不再自动发现)
--expected-sha256 <hex> 期望 sha256(覆盖清单)
--manifest <path> 期望 sha256 清单(默认 build/offline-check/offline-install-manifest.json)
--timeout <ms> 安装器超时(默认 600000 = 10 min)
--run-uninstaller 用安装产物自带的卸载器(Uninstall <productName>.exe /S)卸载后再清理;默认直接删除临时目录
--launch / --no-launch 启动主 exe 数秒健康冒烟(默认不启动,--launch-seconds 默认 8s,启动后强制 taskkill /T /F 树杀)
--temp-root <dir> 临时根(默认 os.tmpdir());非 ASCII %TEMP% 环境建议显式 ASCII 目录
--keep-temp-dir 保留临时安装目录供人工检查(打印路径,不自动删除)
--top-n <n> 内容清单打印条数(默认 10)
--json 机器可读 JSON 报告
--self-test 内部逻辑冒烟(不安装、不清理)

产物定位顺序:显式参数 → 清单 nsis-installer 条目 → apps/desktop/release/<artifactName> → build/offline-check/<artifactName> → release 目录下 ≥50MB 的 exe。缺产物时明确报错。

退出码:0 = PASS(校验、安装、布局、清理全部通过);1 = FAIL(sha256 不匹配 / 安装失败 / 布局无效 / 清理失败 / 非 Windows);2 = NO_INSTALLER(找不到安装包)。

4. 安全边界与取舍

  • 绝不写 %LOCALAPPDATA%\Programs 或系统目录:安装目录 = fs.mkdtemp 于系统临时目录,/D= 把 NSIS 指到那里;脚本自身不写注册表、不写开始菜单/桌面快捷方式。
  • 安装器自身的副作用:electron-builder NSIS 安装器(即便 /S 且 /D= 指向临时目录)会为本次临时安装写 per-user 卸载注册表项 + 开始菜单快捷方式(指向临时目录)。这是安装器自带逻辑,不是本脚本的行为。
    • --run-uninstaller:运行卸载器,删除文件并清掉这些注册表项与快捷方式(实测卸载后 HKCU 无 aps 卸载键、开始菜单无残留快捷方式)。
    • 默认直接删除临时目录:不执行卸载器,安装器自己的注册表项/快捷方式会残留并指向已删除路径 —— 取舍说明:CI 沙箱或不想跑卸载器的场景选默认;想彻底干净选 --run-uninstaller。
  • 删除前路径校验(Windows 安全规则):assertSafeTempDir() 先 path.resolve,确认目标位于系统临时目录下、不是临时目录根、basename 以 aps-nsis-smoke- 开头,才允许 fs.rmSync(recursive);守卫逻辑有单测覆盖。
  • 不驻留进程:默认不启动主 exe;--launch 启动后强制 taskkill /T /F 杀进程树,并把 --user-data-dir 指到临时安装目录内(应用用户数据不出沙箱)。卸载/删除前还会用 Get-CimInstance Win32_Process 枚举可执行路径在临时目录下的残留进程并杀掉(防安装器 runAfterFinish 自动拉起残留)。
  • 卸载器异步收尾:卸载器进程退出(exit 0)后文件删除是异步的,脚本会轮询等待目录消失(最长 25s),仍未消失才回退直接删除(重试 8 次、最长约 17s)。

5. 测试

  • 脚本自检:node scripts/nsis-install-smoke.mjs --self-test → 14/14 PASS(参数解析、路径守卫、布局检测、产物定位、清单哈希、内容清单、卸载器定位)。
  • node 单测:node --test tests/node/nsis-smoke.test.mjs → 13/13 PASS(复用 packaging-smoke.test.mjs 的 import 风格;覆盖路径安全守卫、布局校验、sha256 确定性、清单期望哈希、产物定位、内容清单排序;全部在临时目录内,不触发真安装)。
  • 真实安装路径不在单测内(避免每轮跑 1 分钟+写盘),由 §6 本地实测覆盖。

6. 本地实测记录(2026-08-02)

产物:apps/desktop/release/aps-agent-desktop-0.1.0.exe(171,025,378 B = 163.1 MB,sha256 044c4a83aaf28476a1e2405a7a08185b5aff0dfab27f0299b8bcf16c8d32bf21,与 build/offline-check 清单一致)。

运行 1:安装 + 布局/内容检查 + 卸载器清理(--run-uninstaller)→ PASS

node scripts/nsis-install-smoke.mjs --installer apps/desktop/release/aps-agent-desktop-0.1.0.exe --run-uninstaller
  • 校验:sha256 PASS(与清单 044c4a83…d32bf21 一致)
  • 安装:exit 0,耗时 33.6s(163MB → 解包 567MB,Defender 实时防护按需扫描下)
  • 布局:PASS(mainExe / resources / app.asar / sidecar / Uninstall 工业智核 APS.exe 全部存在)
  • 内容:1158 个文件 / 567.1 MB,前 10 项:
    • 215.2 MB 工业智核 APS.exe
    • 32.9 MB resources/sidecar/_internal/ortools/.libs/ortools.dll
    • 24.4 MB dxcompiler.dll
    • 19.5 MB resources/sidecar/_internal/numpy.libs/libscipy_openblas64_-b788215d9d47792bcba3a2e2a7114320.dll
    • 19.4 MB LICENSES.chromium.html
    • 17.5 MB resources/sidecar/aps-sidecar.exe
    • 12.9 MB resources/sidecar/_internal/ortools/.libs/libprotobuf.dll
    • 12.5 MB resources/sidecar/_internal/libprotobuf.dll
    • 12.2 MB resources/sidecar/_internal/ortools/.libs/libscip.dll
    • 10.4 MB icudtl.dat
  • 清理:卸载器 exit 0,临时目录 removed=true
  • 机器状态复查:%LOCALAPPDATA%\Programs 下无安装;无残留 aps-nsis-smoke-* 临时目录;无残留进程;HKCU 卸载注册表无 aps 键;开始菜单无残留快捷方式

运行 2:+ --launch(启动主 exe 8s 健康冒烟 + 树杀 + 卸载清理)→ PASS

node scripts/nsis-install-smoke.mjs --installer apps/desktop/release/aps-agent-desktop-0.1.0.exe --run-uninstaller --launch --launch-seconds 8
  • 安装:exit 0,耗时 30.9s;布局/内容与运行 1 一致(PASS)
  • 启动健康冒烟:主 exe 存活 9.1s(≥8s 窗口),进程树强制结束(killed=1),无驻留
  • 清理:卸载器 exit 0,removed=true;机器状态复查同样干净

过程中发现并修复:首次实跑时卸载器 exit 0 后目录仍被短暂占用(EBUSY,卸载器异步收尾删除文件),清理判定为 FAIL;已加"轮询等待目录消失(最长 25s)+ 直接删除重试 8 次"后复跑 PASS。EBUSY 属卸载器自身时序,非安装缺陷。

7. 矩阵达成情况与剩余风险

矩阵 99(Defender + 真实 APS 求解):

  • 本方向为衔接:Defender 静态扫描已在 round-43 CC 达成(4/4 clean);真实求解由黄金测试覆盖(既有体系)。
  • 剩余(外部):Defender for Endpoint 门户/API 门禁、CI 门禁;实时防护真实拦截演练(本机实时防护已开启,但真实拦截需受控样本与策略配合)。

矩阵 118(干净离线机安装冒烟):

  • 本方向达成:真实 NSIS 静默安装到随机临时目录 + 布局/内容检查 + 卸载/清理 本地 2 次 PASS;同时验证了 updater.cjs nsisInstall 的 /S + /D= 参数约定在真实安装包上可行(之前只做单元/校验和,未实跑)。
  • 剩余(外部):真实用户机安装(%LOCALAPPDATA%\Programs 落点、快捷方式、升级/回滚、冷启动);干净离线机(无 Python、无网络)实机安装 + 冷启动 + 升级/回滚验收,需外部验证机,可用 §4 的 portable 流程随 U 盘执行。

已知限制:

  • 安装器自身的 per-user 卸载注册表项与开始菜单快捷方式只有 --run-uninstaller 才会清掉;默认直接删除会残留指向已删路径的条目。
  • 本机非管理员,未测 perMachine(HKLM / 系统级)安装路径。
  • Defender 实时防护按需扫描会让安装耗时波动(实测 30.9s / 33.6s 两档);本次安装包静态扫描已在 CC 轮次 clean。
  • --launch 健康冒烟仅验证进程存活数秒(不驻留),完整渲染器/Sidecar 校验归 scripts/smoke-packaged-desktop.mjs。