Skip to content

重做 Windows installer:统一结果页、WebSetup handoff 与 en/zh-CN 多语言 #677

Description

@kxn

背景

当前 Windows NSIS installer 体验明显偏原始:安装阶段只有 instfiles 页面,成功/失败都通过额外 MessageBox 收尾,结果信息无法在主窗体内完成闭环;Admin UI 与日志路径只是普通文本,也没有统一的结果页状态模型。实际体验上会出现:安装刚结束就突然弹出新的对话框,甚至在需要 WebSetup 时还会直接拉起浏览器,handoff 时机过早且割裂。

当前实现更接近 bare NSIS 脚本而不是现代化的 MUI2 + 自定义结果页 安装器。随着 packaged installer 语义已经开始收敛,这一层的用户交互也需要同步升级。

目标

将 Windows installer 重做为基于 MUI2 的向导式安装器,并用统一结果页承载安装结果、后续动作与辅助入口。

范围

结果页统一模型

安装结束后不再弹出额外 MessageBox,而是在 installer 主窗体内显示单独结果页,统一覆盖以下状态:

  1. fresh install + setupRequired
  2. fresh install + no setupRequired
  3. reinstall / repair success
  4. failure

WebSetup handoff 语义

  • fresh install 阶段 installer 只负责 binary 安装与最基础设置。
  • 如果安装结果要求继续 WebSetup,不应在安装过程中自动打开浏览器。
  • 应在最终结果页提供主动作按钮 Continue WebSetup
  • 用户点击该按钮后,installer 任务视为完成,随后显式 handoff 给浏览器中的 WebSetup;用户不应再回到 installer 继续流程。
  • reinstall / repair 只用于修复文件与基础状态,默认不启动 WebSetup。

页面动作模型

  • fresh install + setupRequired
    • 主按钮:Continue WebSetup
    • 辅助链接:Open Logs
    • 不显示 Open Admin UI
  • fresh install + no setupRequired
    • 主按钮:Finish
    • 辅助链接:Open Admin UIOpen Logs
  • reinstall / repair success
    • 主按钮:Finish
    • 辅助链接:Open Admin UIOpen Logs
  • failure
    • 主按钮:Finish
    • 辅助链接:Open Logs

多语言

Windows installer 至少同时支持:

  • en
  • zh-CN

语言策略:

  • 跟随系统语言自动选择;不额外增加“安装器首屏选择语言”的前置步骤。
  • 所有用户可见文案收敛到 NSIS 的语言资源体系,不再散落在脚本拼接字符串中。

非目标

  • 不在本 issue 中顺带改造 macOS 安装器 UI。
  • 不在本 issue 中引入安装器首屏语言选择步骤。
  • 不在本 issue 中继续维持 Page instfiles + MessageBox 的旧收尾交互作为长期并存路径。
  • 不在本 issue 中把 Windows installer 额外扩展成完整设置向导;installer 仍以 binary 安装、基础设置与 handoff 为职责边界。

设计约束

  • 不要继续在当前 Page instfiles + MessageBox 结构上做增量补丁。
  • 目标是 MUI2 外壳 + 自定义结果页(可基于 nsDialogs),而不是继续依赖 stock finish page 的最小改造。
  • 结果页中的辅助入口应是清晰的可点击动作,不是裸文本路径。
  • 成功路径与失败路径都必须留在 installer 主窗体内完成收尾,不再额外弹系统对话框。
  • WebSetup 与 installer 的职责边界要明确:installer 完成后再 handoff。

完成标准

  1. 安装完成后不再弹出额外 MessageBox,而是进入主窗体结果页。
  2. fresh install + setupRequired 时不会提前自动拉起浏览器,只有点击 Continue WebSetup 才会启动 WebSetup。
  3. reinstall / repair 成功后不会启动 WebSetup。
  4. Admin UI / Logs 在需要出现的状态下都以可点击动作提供。
  5. Windows installer 在 enzh-CN 下都能正常展示对应语言文案。
  6. 至少补齐一组 Windows installer smoke / 回归验证,覆盖结果页与 WebSetup handoff 的核心行为。

调研结论

1. 当前 Windows installer 仍是 bare NSIS,不是 MUI2 向导流

已确认当前 deploy/windows/codex-remote-installer.nsi 只有:

  • Page instfiles
  • 安装完直接 MessageBox
  • 成功和失败都靠对话框收尾
  • Admin UI / Logs 只是字符串,不是页面内动作

因此当前体验问题不是局部文案或样式问题,而是 installer UI 模型本身过旧。

2. 现有 Go contract 已足够支撑结果页,不存在必须先扩展后端的阻塞

packaged-install 的结果文件已经稳定提供这些字段:

  • mode
  • adminURL
  • setupURL
  • setupRequired
  • logPath
  • error
  • startupMode
  • currentVersion
  • currentTrack
  • currentSlot

packaged-install-probe 还额外提供:

  • sameVersion
  • installLocationEditable
  • currentInstallBinDir
  • suggestedInstallBinDir

这意味着:

  • 统一结果页本身不需要先扩展 Go result contract 才能落地。
  • 若需要区分“首次安装 / 同版本重装修复 / 升级”文案,Windows wrapper 应接入 packaged-install-probe 并保留 probe 上下文,而不是强行往 result-file 里再补一层 Windows 私有字段。

3. Continue WebSetup 不能仅靠最终结果里的 setupRequired 判断

当前 ready 状态里 setupRequired=true 的含义并不等同于“这是 fresh install 且应该走专属 WebSetup handoff”。它只是 daemon 启动后的 bootstrap 状态结果。

所以 Windows 结果页的主动作必须按照:

  • probe.mode == first_installresult.setupRequired == true -> Continue WebSetup
  • probe.mode == repair -> 不显示 Continue WebSetup

也就是说,WebSetup handoff 是否出现,需要由 probe.moderesult.setupRequired 联合决定,而不是只看最终 result。

4. 现有 CI 不覆盖 NSIS GUI installer 本体

当前仓库里:

  • scripts/check/smoke-install-release.ps1 只验证 install-release.ps1
  • installer-smoke.yml 的 Windows job 也只跑上述 PowerShell 安装脚本 smoke
  • release.yml 虽然会构建 NSIS installer,但后续 smoke 仍然是 install-release.sh

因此当前 CI 能证明:

  • release 产物可下载
  • shell / PowerShell 安装脚本可用

但不能证明:

  • NSIS 结果页有没有正确出现
  • 有没有错误地自动打开浏览器
  • 链接与按钮是否按产品模型出现
  • 中英文文案是否完整

5. 验证层最需要新增的是“installer 自身可观测性”,不一定是重型 GUI 自动化

真正缺的不是 release 下载 smoke,而是 NSIS 页面状态的回归保护。

当前最合理的验证方向是:

  • 把 Windows installer 的结果页状态映射收敛成明确的 NSIS-side view model
  • 给它增加窄范围、显式门控的测试 capture / override 能力
  • 在 Windows CI 里运行 installer harness,校验:
    • fresh install + setupRequired 不会自动打开 WebSetup
    • 只有 Continue WebSetup 会触发 handoff
    • repair 成功时不会走 WebSetup handoff
    • 关键动作和语言分支都能被稳定断言

相较直接引入脆弱的桌面点击自动化,这条路更稳定,也更适合长期 CI。

6. 多语言与页面内链接在 NSIS/MUI2 能力边界内可实现

已确认:

  • nsDialogs 可做自定义页面与页面内控件
  • nsDialogs 自带 Link 控件,可用于页面内链接动作
  • MUI2 支持多语言资源组织;按当前需求,不需要增加安装器首屏语言选择,只需要按系统语言自动选择,并用 LangString 收拢所有用户文案

当前没有发现需要额外引入第三方 installer framework 或重写为别的打包器的信号。

相关文档

  • docs/general/install-deploy-design.md
  • docs/implemented/non-linux-user-autostart-design.md
  • NSIS Modern UI 2: https://nsis.sourceforge.io/Docs/Modern%20UI%202/Readme.html
  • NSIS nsDialogs: https://nsis.sourceforge.io/Docs/nsDialogs/Readme.html

涉及文件

  • deploy/windows/codex-remote-installer.nsi
  • internal/app/install/packaged_install.go
  • internal/app/install/packaged_install_result_file.go
  • internal/app/install/packaged_install_probe.go
  • internal/app/install/packaged_install*_test.go
  • scripts/release/build-windows-nsis.sh
  • scripts/check/smoke-install-release.ps1
  • .github/workflows/installer-smoke.yml
  • .github/workflows/release.yml
  • .github/workflows/dev-release.yml

执行决策

  • 是否拆分:否
  • 当前执行单元:#677 作为未拆分直做单元
  • 不拆原因:Windows installer 的 UI 重构、probe/result 状态整合、多语言资源收敛、以及新的 installer-specific 验证能力,共享同一个产品目标和同一条 Windows 验收路径;拆开后反而会把状态机、文案和验证面割裂
  • verifier 决策:需要。该 issue 会修改最终用户直接看到的安装器交互和 handoff 语义,完成后应做独立 verifier

建议范围

阶段 A:收敛 Windows installer 的内部状态模型

  • 在 NSIS wrapper 内新增 probe 阶段,读取:
    • mode
    • sameVersion
    • installLocationEditable
    • 其他结果页需要的 installer-facing metadata
  • 在 NSIS 侧明确结果页 view model,而不是用字符串拼接状态:
    • primary action kind
    • summary/body copy kind
    • Open Admin UI 是否显示
    • Open Logs 是否显示
    • Continue WebSetup 是否显示
  • 明确 probe + result 的联合判定规则,尤其是:
    • first install + setupRequired
    • first install + no setupRequired
    • same-version repair
    • upgrade repair
    • failure

阶段 B:把脚本迁移到 MUI2 + 自定义结果页

  • 从 bare Page instfiles 迁移到 MUI2 向导骨架
  • 保持页面数量最小,不额外扩展安装器职责
  • 结果页使用 nsDialogs 自定义页面承载:
    • 主按钮 Continue WebSetup / Finish
    • 次级链接 Open Admin UI / Open Logs
    • 模式化标题与摘要文案
  • 去掉额外 MessageBox 收尾
  • 将 WebSetup 打开动作改成只在 Continue WebSetup 点击后执行

阶段 C:收敛多语言资源

  • 为 Windows installer 建立统一 LangString 资源表
  • 至少覆盖:
    • 页面标题
    • 摘要文案
    • 主按钮文案
    • 链接文案
    • 失败提示文案
    • 安装中/结果页状态文案
  • 默认按系统语言自动选择 en / zh-CN
  • 避免把中英双语继续散落在脚本逻辑分支里拼字符串

阶段 D:补齐 installer-specific 验证与 workflow 接入

  • 为 NSIS installer 增加窄范围测试 capture / override 机制,使 CI 能稳定断言结果页状态与 handoff 行为
  • 在 Windows smoke 中新增 NSIS installer harness,覆盖至少:
    • fresh install + setupRequired
    • repair success
    • failure
    • Continue WebSetup 是否只在预期状态出现
    • repair 路径不会触发 WebSetup handoff
  • 对多语言至少补一条自动验证路径,确保 en / zh-CN 资源没有缺口

阶段 E:文档与发布链收尾

  • 更新 installer 设计文档中 Windows packaged installer 的页面与 handoff 语义
  • 确认 release.yml / dev-release.yml 的 installer 构建与 smoke 链路仍然完整
  • 如测试 hook 仅用于 CI,明确记录其边界,避免误当产品入口

实现参考

  • deploy/windows/codex-remote-installer.nsi
    • 当前唯一 Windows installer UI 逻辑载体,需要直接重构
  • internal/app/install/packaged_install.go
    • 当前 result-file contract 的事实来源
  • internal/app/install/packaged_install_probe.go
    • Windows installer 获取 first-install / repair / sameVersion 上下文的推荐入口
  • deploy/macos/InstallerApp/Sources/InstallerBridge.swift
    • 当前 macOS wrapper 已有的 probe + result 组合方式,可参考其状态组织,但不要直接复制产品语义
  • scripts/check/smoke-install-release.ps1
    • 当前 Windows smoke 基线,只验证 PowerShell 安装脚本,不覆盖 NSIS GUI
  • .github/workflows/installer-smoke.yml
    • 需要补 Windows NSIS-specific smoke 入口

检查参考

  • fresh install 且 setupRequired=true 时,安装完成后不能自动弹浏览器
  • 只有点击 Continue WebSetup 才允许 handoff 给 WebSetup
  • repair / upgrade 成功后不出现 Continue WebSetup
  • Open Admin UIContinue WebSetup 不会同时出现
  • failure 仍然留在 installer 主窗体结果页内,不再弹额外 MessageBox
  • en / zh-CN 两套文案是否都有完整映射
  • 当前 release / dev-release 产物里 Windows installer 是否仍正常生成

收尾参考

  • 回写本 issue 的最终阶段结果与验证结论
  • 如 Windows packaged installer 语义发生变化,更新 docs/general/install-deploy-design.md
  • 若引入 installer-specific 测试 hook,记录其仅用于 CI/验证,不作为产品面公开能力
  • 完成后执行独立 verifier,再做 commit / push / finish

当前执行点

  • 当前阶段:closeout
  • 当前执行点:verifier 已完成,当前只剩 close-planfinish --close 两个 close-out 尾项
  • 已完成:
    • packaged-install-probe 新增 -result-file 输出,并补齐对应 Go 测试
    • Windows NSIS installer 改为 MUI2 + finish/result page 模型,去掉额外 MessageBox
    • 结果页主动作 / 链接 / WebSetup handoff 按 probe + result 联合判定
    • Windows installer 文案收敛到 en / zh-CN 两套 LangString
    • 新增 NSIS installer headless test capture / override 通道
    • 新增 scripts/check/smoke-windows-nsis-installer.ps1
    • installer-smoke workflow 新增 Windows NSIS 构建与 smoke 路径
    • docs/general/install-deploy-design.md 已同步新的 Windows packaged installer 语义
    • 本地已通过:go test ./...bash scripts/check/go-file-length.shgit diff --check
    • production / beta 两份 Windows NSIS installer 已在本地重新成功编译
    • 已定位并修复上一轮 smoke-windows 根因:NSIS FileWrite 会把 Unicode capture 文本按系统 ANSI code page 写出,导致中文文案在 smoke capture 中降成 ??
    • 修复提交 fe4b91ff 已推到 master
    • Installer Smoke run 26714208258 已全部通过,其中 smoke-windows job 78730184694 成功完成 Smoke test PowerShell installerSmoke test Windows NSIS installer
    • 独立 verifier 结果:pass 已以 comment 形式写回 issue
  • 下一步:
    1. 运行 close-plan
    2. 执行 finish --close
  • 当前 blocker:无
  • 最后一致状态:
    • 工作区干净,HEADorigin/master 一致
    • 最新提交:fe4b91ff (test: preserve NSIS unicode smoke capture)
    • 最新验收证据:Installer Smoke run 26714208258 通过,URL: https://github.com/kxn/codex-remote-feishu/actions/runs/26714208258
  • 未完成尾项:close-planfinish --close
  • 恢复步骤:
    1. 运行 bash .codex/skills/issue-workflow-guardrail/scripts/issuectl.sh close-plan --issue 677
    2. 运行 bash .codex/skills/issue-workflow-guardrail/scripts/issuectl.sh finish --issue 677 --close

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:releaseGitHub release workflow, versioning, milestones, and release automationarea:runtimeRuntime manager, process lifecycle, and persisted stateenhancementNew feature or requeststatus:implementable-nowReady to implement with current issue body and context

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions