Skip to content

fix(installer): Windows 一键安装器加磁盘空间闸,不足时就地拦下(dev-board#350) - #689

Merged
zeweihan merged 3 commits into
masterfrom
fix/win-installer-disk-space-gate-350
Sep 1, 2026
Merged

fix(installer): Windows 一键安装器加磁盘空间闸,不足时就地拦下(dev-board#350)#689
zeweihan merged 3 commits into
masterfrom
fix/win-installer-disk-space-gate-350

Conversation

@zeweihan

@zeweihan zeweihan commented Sep 1, 2026

Copy link
Copy Markdown
Owner

修 dev-board#350。AwdUpdateSpace 取了 DriveSpace 但只把它显示成「可用 X GB」,没有任何闸:磁盘不够时安装照常开始,NSIS 在解压中途报 Error opening file for writing,用户拿到一个残缺安装。

闸放在哪

AwdInstallClick 里、$INSTDIR 定下来之后——用户可能刚在「自定义安装」里换过盘,先算就算错了。不足时弹提示说明「需要约 X.X GB / 该盘可用 Y.Y GB」并把展开行的可用空间标签染红,然后 Return,不进安装页。

所需空间从哪来(不手抄数字)

桌面端在 desktop/build/installer.nsh 里直接接 electron-builder 的 APP_64_UNPACKED_SIZE:那是打包时对 win-unpacked 整个目录(含 extraResources:backend / jre / python / frontend dist / skills…)实测的解包体积,单位 KB。

它是 makensis 命令行 -D 传进来的(NsisTarget.jsexecuteMakensis 把 defines 逐个 args.push(\-D${name}=${value}`)),所以在生成脚本的第一行就已经存在——和 $launchLink/APP_EXECUTABLE_FILENAME/StdUtils 那批「晚于本 include 才出现、-WX` 下三连编译失败」的东西不是一回事。手抄常量才是错的:v0.28.0→v0.29.0 的安装包已经从 1516MB 长到 1517MB,每版都在长。

拿不到这个 define 就 !error 编译失败,不静默少一道闸USE_NSIS_BUILT_IN_COMPRESSOR 在 electron-builder 源码里硬编码 false,只有它改了契约才会撞上)。

余量 512MB = ARM64 机器上 customInstall 还要铺的纯 arm64 Electron 壳(约 260MB 未压缩,不在上面那个数里)+ NSIS/7z 解压临时占用 + 不把盘塞到 0 字节。刻意没给更大:闸设得过严会把「其实装得下」的用户挡在门外,那比不设闸更糟。

三条刻意的保守选择

  • 读不出可用空间一律放行(空串 / 0 / 非数字都按「读不出来」处理——IntCmp 会把非数字当 0,不特判就会误判成不足)。网络盘、UNC 路径、某些卷上 DriveSpace 会返回空。
  • 引擎侧整套代码在 !ifdef AWD_UI_REQUIRED_KB 内。插件端安装器不设闸(装的是一份 manifest),编译产物一字节不变。
  • 静默安装(/S,自动更新路径)不进 GUI 代码,这道闸对它不生效——那条路的失败处理归更新器管,此处不越界。

NSIS 地雷(按 .claude/agents/eng-infra.md 的记录处理)

  • FileFunc 的 GetRoot/DriveSpace 会踩调用方 $0-$2(历史上 DriveSpace 把窗口宽度写成了剩余 GB,CI 截图实锤;引擎里 AwdToggleCustom 还留着那条注释)。所以 AwdReadFreeSpace 进出各 Push/Pop 一次,结果只落 Var——调用方拿到的 $0-$2 与调用前逐字节一致,上面那段路径处理的结果不受影响。
  • GB 文案自己算一位小数:整数取整会把「差 1.9 GB」显示成「差 1 GB」,而差多少是用户要不要去清盘的唯一依据。
  • 只改标签颜色不改标签文案:AWDUI_SPACE_W 是 104px、按「可用 XX GB」排的版,文案与美术位图、热区坐标是同一套 96dpi 基准,改一处要改三处。

顺手修一个这次会自己造出来的陈旧态:AwdUpdateSpace 开头复位标签颜色,否则用户被拦下后换到大盘,标签还红着。

验证

本机 makensis 不能用来预检(2026-09-01 实测,Darwin 27):homebrew v3.12 与 electron-builder 缓存的 v3.04 两个 mac 构建都一样——非 ASCII 源码报 Bad text encoding,纯 ASCII 脚本走到写产物时 std::bad_alloc 崩。未改动的 HEAD 文件同样复现,是环境问题不是本改动引入的(已把这条记进 eng-infra,省得下次再查一遍)。

所以把反向用例做进了 CI,而不是靠人眼看代码:

  • ui-harness.nsi-DREQUIRED_KB
  • 工作流多编一个所需空间约 858GB 的 harness-nospace.exe
  • installer-smoke.ps1-ExpectBlocked,断言三件事:弹了提示框(前台窗不再是卡片自己)、进程没退出窗口还是 760 宽的大卡片(真开装会缩成 360×132 的角落进度卡)。截图落 shots/nospace/03-blocked.png
  • 两个正常 harness(zh/en)也打开了闸(200MB,payload 只有 160MB)——正向那条路本身就是「闸没把正常安装挡住」的回归证据。

installer-ui-smoke 已在本分支手动触发,跑完会把逐阶段截图作为 artifact 传上来;结论回评在本 PR 里。

复测提示

下个发版装 Windows 桌面端:正常磁盘应与现在毫无区别(点「立即安装」直接进进度卡);把安装盘塞到剩余空间小于安装包解包体积再点,应当弹出「可用空间不足」并留在大卡片上,展开「自定义安装」可见可用空间标签是红的。

🤖 Generated with Claude Code

zeweihan and others added 3 commits September 1, 2026 11:38
以前 AwdUpdateSpace 只把可用空间显示成「可用 X GB」,不足照装不误:NSIS 在解压
中途报「Error opening file for writing」,用户拿到一个残缺安装。

闸放在 AwdInstallClick 里、$INSTDIR 定下来之后(用户可能刚在「自定义安装」里换过盘):
不足则弹提示说明「需要约 X.X GB / 该盘可用 Y.Y GB」,并把展开行的可用空间标签染红,
不进安装页。

所需空间不手抄数字:桌面端在 installer.nsh 里接 electron-builder 的
APP_64_UNPACKED_SIZE——打包时对 win-unpacked 整个目录(含 backend/jre/python/
frontend dist/skills 等 extraResources)实测的解包体积,KB,由 makensis 命令行 -D 传入,
在生成脚本第一行就存在(与 $launchLink/StdUtils 那批晚到的东西不同)。拿不到就 !error
编译失败,不静默少一道闸。余量 512MB:ARM64 壳覆盖层约 260MB(不在上面那个数里)
+ 解压临时占用 + 不把盘塞到 0 字节。

三条刻意的保守选择:
- **读不出可用空间一律放行**(空串/0/非数字都算读不出来)。宁可让 NSIS 自己在解压时
  报错,也不能因为一次读盘失败把装得下的用户挡在门外——闸设得过严比不设闸更糟。
- 引擎侧整套代码在 !ifdef AWD_UI_REQUIRED_KB 内,插件端安装器(装一份清单)不设闸,
  编译产物一字节不变。
- 静默安装(/S,自动更新)不进 GUI 代码,这道闸对它不生效,那条路的失败处理归更新器管。

NSIS 地雷已按 eng-infra 的记录处理:FileFunc 的 GetRoot/DriveSpace 会踩调用方 $0-$2
(历史上把窗宽写成剩余 GB),所以 AwdReadFreeSpace 进出各 Push/Pop 一次、结果只落
Var;GB 文案自己算一位小数,取整会把「差 1.9 GB」显示成「差 1 GB」。顺手修一个会
自己造出来的陈旧态:AwdUpdateSpace 开头复位标签颜色,否则用户换到大盘后标签还红着。

验证:本机 makensis 不可用(homebrew v3.12 与 electron-builder v3.04 两个 mac 构建
都是非 ASCII 报 Bad text encoding、纯 ASCII 写产物 std::bad_alloc,**未改动的 HEAD 文件
同样复现**,已记进 eng-infra),只能靠 installer-ui-smoke。因此把反向用例做进了 CI:
ui-harness.nsi 收 -DREQUIRED_KB,工作流多编一个所需空间约 858GB 的 harness-nospace.exe,
installer-smoke.ps1 加 -ExpectBlocked 断言「弹了提示框 + 进程没退出 + 窗口还是 760 宽的
大卡片(真开装会缩成 360x132 的角落进度卡)」;两个正常 harness 也打开了闸(200MB),
正向那条路本身就是「闸没挡住正常安装」的回归证据。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
首轮 installer-ui-smoke(run 33466932338)证明闸本身是对的:截图里提示框写着
「安装需要约 858.3 GB,C: 盘当前可用 31.7 GB」,卡片没进安装页、窗口仍是 760 宽。
两处要修:

1. 标签没变红。InvalidateRect 只打脏标记,而提示框在卡片中部、盖不住右下角那个
   「可用 31 GB」标签,关框时不会顺带擦除重画它,颜色就一直是旧的灰。改用
   RedrawWindow(RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW) 同步重绘。

2. 冒烟收尾把用例判红了。拦下用例最后点大卡片右上角 ✕ 想干净退出,实跑里进程
   2 秒内没退(AwdCloseClick 是 Quit,从 nsDialogs::Show 的回调里调用是否即时生效
   没有验证过)——这条路径与本闸无关,不该拿它把用例判红。收尾改为直接杀进程;
   关窗行为由 zh/en 两条正常流程在完成卡上覆盖。
   顺带留个观察给维护者:大卡片 ✕ 这条路至今没有任何用例覆盖过,值得单独看一眼。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
两轮 installer-ui-smoke 的截图都证明运行期改这个控件的颜色不生效:先试
SetCtlColors + InvalidateRect(第一轮,文案对、标签还是灰的),再试
SetCtlColors + RedrawWindow 同步重绘(第二轮,还是灰的)。本机没有 Windows 真机可调,
而提示框已经把「需要多少 / 该盘有多少 / 怎么办」说全了——与其留一段看着像生效
其实没生效的代码,不如不留,把「为什么不做」的结论写进注释。

顺带把 AwdUpdateSpace 还原成 master 原样(那句颜色复位只为撤销染红而存在)。
本文件相对 master 现在是纯新增,一行都没删。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zeweihan

zeweihan commented Sep 1, 2026

Copy link
Copy Markdown
Owner Author

CI 验证结果

1. 真实桌面打包(决定性的那条)

desktop-build.ymlBuild (windows-latest) 在闸的那个提交上 success(run 33466993194)。这一条同时证明了两件事:

  • APP_64_UNPACKED_SIZE 确实在我们 include 展开的位置就已经存在——不存在的话我加的 !error 会直接把这次打包打死;
  • 整个引擎带着新代码在真实 electron-builder 管线里、-WX 下编译通过。

2. installer-ui-smoke 逐阶段截图(跑了三轮)

轮次 结果 说明
run 33466932338 smoke 失败 闸本身是对的03-blocked 截图里提示框写着「安装需要约 858.3 GB,C: 盘当前可用 31.7 GB」,04-still-welcome 显示窗口仍是 760 宽的大卡片(真开装会缩成 360×132 的角落进度卡)。失败点只在我写的收尾步骤:点大卡片 ✕ 后进程 2 秒内没退。
run 33467618292 smoke success 收尾改成直接杀进程;nospace / zh / en / addin 四条驱动全过。
run 33468075235 smoke success 去掉染红标签那段之后复跑,闸行为不变。

最终 03-blocked.png

  • 提示框:「可用空间不足,暂时无法安装。安装需要约 858.3 GB,C: 盘当前可用 32.3 GB。可以点「自定义安装」换一个磁盘,或清理后重试。」
  • 一位小数、盘符、双语分支都对;卡片停在欢迎页,立即安装 还在,没有开始拷任何文件
  • 两条正常 harness(zh/en,闸设 200MB、payload 160MB)与插件端安装器照常跑完整流程 —— 这就是「闸没把正常安装挡住」的正向回归证据。

一处做了又撤掉的

原本还想在展开行把「可用 X GB」标签染红。试了两轮都不生效:先 SetCtlColors + InvalidateRect(第一轮截图:文案对、标签还是灰),再 SetCtlColors + RedrawWindow 同步重绘(第二轮:还是灰)。本机没有 Windows 真机可调,而提示框已经把「需要多少 / 该盘有多少 / 怎么办」说全了——与其留一段看着像生效其实没生效的代码,不如撤掉,把结论写进注释。撤掉后 AwdUpdateSpace 也还原成 master 原样,本文件相对 master 现在是纯新增、一行都没删

顺带的一条观察(没有动,留给维护者)

拦下用例第一轮点大卡片右上角 ✕ 后,进程 2 秒内没退出。AwdCloseClickQuit,从 nsDialogs::Show 的回调里调用是否即时生效从来没有任何用例覆盖过(zh/en 两条正常流程关的是完成卡的 ✕,走的是 SendMessage WM_COMMAND)。只有这一个数据点,不足以断定是 bug,但值得单独看一眼。

@zeweihan
zeweihan merged commit da02d6e into master Sep 1, 2026
10 checks passed
@zeweihan
zeweihan deleted the fix/win-installer-disk-space-gate-350 branch September 1, 2026 04:13
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant