Files
ctms/docs/audits/desktop-release-stabilization-checklist.md
T
Cheng Zhou d5279b124f
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
release(main): 同步 dev 最新候选改动
2026-07-16 17:15:50 +08:00

22 KiB
Raw Blame History

CTMS Desktop Release Stabilization Checklist

状态: active 适用范围: Web 与 macOS Desktop 统一客户端发布 最后更新: 2026-07-14

本清单用于第一、二阶段桌面端能力完成后的准发布稳定化。当前允许按 docs/desktop-local-cache-plan.md 引入在线辅助本地缓存,但不引入离线登录、离线写入、本地业务权威数据、内嵌后端服务或离线同步。

1. 发布链路门禁

发布候选提交必须从同一 Git 提交构建 Web 与 Desktop 制品,并完成以下检查:

cd frontend
npm ci
npm run version:check
npm run release:env:check
npm run runtime:check
npm run desktop:release:check
npm run ui:contract
npm run type-check
npm run test:unit
npm run build
npm run desktop:build:app

正式发布还必须确认:

  • frontend/package.jsonpackage-lock.json、Tauri 配置、Cargo manifest/lock 版本一致。
  • VITE_BUILD_CHANNEL=releaseVITE_BUILD_COMMIT=<release tag commit> 由 CI 注入,且 npm run release:env:check 通过。
  • 在正式 release tag 和签名环境中执行 npm run desktop:release-readiness:check,确认 tag、构建元数据、签名/公证变量、updater 私钥和生产 artifact HTTPS 基址齐备。
  • macOS app 已签名和公证。
  • updater .sig 使用组织 CI secret 或密钥库中的私钥生成,私钥未进入仓库。
  • 设置 TAURI_SIGNING_PRIVATE_KEYTAURI_SIGNING_PRIVATE_KEY_PASSWORD 和 Apple 签名/公证变量后,以 REQUIRE_DESKTOP_SIGNING=true 再次执行 npm run release:env:check,随后执行 npm run desktop:build:macos-release -- --ci
  • 正式 updater feed 先执行 npm run desktop:update-feed:create -- --artifact <CTMS.app.tar.gz> --base-url <versioned-https-artifact-prefix> --output-dir <release-dir> 生成 latest.jsonSHA256SUMS.txt
  • 正式 updater feed 执行 npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir>,并确认 checksum manifest、updater artifact、.siglatest.json 均通过校验。
  • 不可变制品先上传,latest.json 最后原子替换;若 feed 校验未通过,不替换线上 latest.json
  • Web 与 Desktop 制品记录同一产品版本、Git 标签和完整提交 SHA。

2. 安全边界复审

自动门禁 npm run desktop:release:check 覆盖以下静态约束:

  • Tauri bundle 启用 appdmg 和 updater artifacts。
  • updater public key 已配置。
  • CSP 禁止 wildcard source、unsafe-eval、宽泛 HTTP API 访问和 object-src
  • script-src 保持仅允许自身;ONLYOFFICE 仅在 frame-src 增加 HTTPS 和 localhost/127.0.0.1 开发来源。
  • ONLYOFFICE frame 地址只由 frontend/src/runtime/onlyoffice.ts 使用已校验服务器 origin 与固定 /onlyoffice-host.html 生成。
  • ONLYOFFICE 宿主消息同时校验 originsource 和一次性内存 nonce,配置不进入本地缓存或请求去重。
  • Tauri capability 不包含 shell 权限、持久文件系统 scope 或宽泛目录读写。
  • Tauri capability 不包含 remote 授权,远程 ONLYOFFICE frame 无文件、通知、对话框或 opener 权限。
  • 文件系统仅开放临时文件所需的 read/write/mkdir/remove 命令,且文件系统与 opener scope 只允许 $TEMP/ctms-desktop/**
  • 单实例插件先于其他桌面插件注册。
  • macOS 首个顶层 submenu 为应用菜单,包含关于、设置、服务、隐藏和退出;文件菜单保持独立。
  • macOS 红色按钮保持真正关闭窗口的系统语义,Dock/Finder reopen 事件在需要时重建、显示并聚焦主窗口。
  • 桌面快捷键只通过受控 command 同步到原生菜单,不向 WebView 开放原始窗口或菜单控制权限。
  • 桌面主题支持跟随系统、明亮和暗黑,并通过受控 command 同步窗口外观。
  • Tauri command 白名单仅包含凭据、更新和已审计的桌面 UI 窄命令。
  • 前端源码不通过 query string 传递 token。
  • ctms_token 只允许由 secureSessionStorage 处理。
  • 登录表单密码不写入 localStoragesessionStorage;Web 端只使用浏览器凭据管理能力,Desktop 端只使用系统凭据库。
  • 本地缓存能力只通过 frontend/src/runtime/desktopDataCache.ts 或后续同名 runtime 入口暴露,业务模块不直接调用 Tauri 存储 API、SQLite、IndexedDB、Cache Storage 或文件系统。
  • 本地缓存命名空间包含 server origin、user id 和 cache schema version。
  • 本地缓存不保存 token、密码、Authorization/Bearer 文本、下载凭据、临时授权 URL 或系统通知正文。
  • 系统通知只能通过 frontend/src/runtime/notifications.ts 发送,标题和正文保持通用。
  • 通知 capability 只暴露权限查询、权限请求和发送通知,不使用 notification:default
  • opener capability 只允许打开 $TEMP/ctms-desktop/** 下的临时文件,不开放 URL 或 reveal 权限。
  • updater capability 不直接暴露给 WebView,自动更新只走受控 Tauri command。
  • 更新弹窗 release notes 过滤 URL、token 查询参数和 Authorization/Bearer 形态文本。
  • CI release 候选 workflow 包含 version/runtime/desktop/ui/type/unit/build/desktop app smoke 门禁。
  • signed macOS release candidate workflow 只允许从 vX.Y.Z tag 运行,并包含签名环境检查、Universal macOS 构建、update feed 生成、checksum 校验和 verified release directory 上传。

人工复审还必须确认:

  • token 不出现在 URL、日志、系统通知正文、下载链接或持久化业务缓存中。
  • 密码不出现在 URL、日志、系统通知正文、诊断信息或明文浏览器存储中。
  • 桌面端通知正文只显示通用内容,不包含项目、文件或版本详情。
  • 服务端权限、审计和业务数据持久化仍由 FastAPI 后端裁决。
  • 本地缓存只作为服务端响应副本,mutation、审批、权限判断和审计判断仍以后端结果为准。
  • Web 运行时不直接导入 Tauri API。

3. 端到端回归矩阵

场景 Web macOS Desktop 预期
登录与项目恢复 必测 必测 登录成功后恢复可访问项目;401 后重新登录
记住密码 必测 必测 Web 使用浏览器凭据管理/自动填充;Desktop 使用系统凭据库;未勾选时不继续写入保存密码
30 天免登录 不适用 必测 关闭并重启 App 后复用系统凭据库中的后端在线会话;超过 30 天或 /me 校验失败后重新登录
服务器地址未配置 不适用 必测 自动进入服务器设置,不进入业务页
服务器地址切换 不适用 必测 清除当前会话和项目上下文,要求重新登录
服务端不可达 必测 必测 显示可恢复错误,不进入离线模式
本地缓存命中 必测 必测 /me 校验通过后可先展示缓存再后台刷新
本地缓存清理 必测 必测 登出、切换服务器、切换用户、401/403 后旧命名空间不可读取
mutation 缓存失效 必测 必测 新增、编辑、删除或审批成功后相关列表、详情、统计和图表缓存失效
附件上传 必测 必测 Web 使用浏览器文件选择,Desktop 使用原生选择
附件下载/保存/打开 必测 必测 使用 Authorization header;无 ?token=
Office 附件/版本只读预览 必测 必测 独立全幅页面;不下载到本地;无编辑、复制、打印、下载入口;标签失活和服务器切换立即销毁
ONLYOFFICE 服务不可用 必测 必测 显示可重试错误,不回退 iframe 或自动下载;原保存/打开功能不受影响
临时文件清理 不适用 必测 启动时清理 $TEMP/ctms-desktop/**
系统通知开启 不适用 必测 用户主动开启后请求 OS 权限并创建订阅
系统通知拒绝 不适用 必测 开关回退,提示系统权限未开启
通知领取与 ack 不适用 必测 显示成功后 ack;失败等待租约重试
单实例重复启动 不适用 必测 恢复、显示并聚焦主窗口
关闭窗口后 Dock 重开 不适用 必测 红色关闭按钮真正关闭主窗口;点击 Dock 后重建、显示并聚焦
原生应用菜单 不适用 必测 CTMS、文件、编辑、显示、导航、窗口和帮助菜单顺序及职责符合 macOS 习惯
自定义快捷键同步 不适用 必测 设置修改后原生菜单立即显示并响应新的后退、前进和刷新快捷键
跟随系统主题 不适用 必测 系统明暗模式变化时 WebView、标题栏和系统控件同步更新
自动更新检查 不适用 必测 release 通道按当前 CTMS origin 派生清单
更新稍后提醒 不适用 必测 同版本 24 小时内不重复提示
更新安装失败 不适用 必测 不打断业务录入,显示可排障错误

4. 桌面体验验收

  • 登录页显示当前桌面服务器地址,长 URL 不撑破登录面板。
  • 30 天免登录仍只保存系统凭据库会话记录,不把 token 写入 URL、日志、通知正文或业务缓存。
  • 记住密码与 30 天免登录使用独立凭据记录;服务器切换后不复用旧服务器保存的密码。
  • 服务器设置页显示当前服务器、连接检查状态、HTTP 错误、超时和网络失败原因。
  • 个人中心显示客户端类型、版本、平台、构建通道、提交、服务器和能力状态。
  • 个人中心可复制诊断信息,内容不包含 token 或业务敏感数据。
  • 系统偏好或个人中心提供本地缓存记录数、容量和最近清理时间,且支持手动清理。
  • 通知开关显示 OS 权限状态。
  • 手动检查更新能反馈“已是最新版本”、未启用更新或检查失败。
  • 关键弹窗、表单、按钮在最小窗口尺寸 1180x760 下不重叠、不溢出。
  • “设置…”位于 macOS CTMS 应用菜单并使用 ⌘,;退出位于应用菜单而不是文件菜单。
  • “显示”和“导航”菜单中的快捷键与桌面偏好保存值一致。
  • 更新弹窗只显示版本、发布日期和通用 release notes,不展示 token、下载链接或业务详情。

5. 不允许项

  • 不实现离线登录、离线写入、离线队列或离线同步。
  • 不在 /me 校验通过前展示业务缓存。
  • 不把本地缓存作为 CTMS 业务权威数据或本地优先数据源。
  • 不内嵌 FastAPI、PostgreSQL 或本地业务 API 镜像。
  • 不绕过后端做本地权限裁决、本地审计判定或审计回放。

6. 2026-07-01 收尾验证记录

本轮收尾验证在 /Users/zcc/MyCTMS/ctms-dev/worktrees/ctms-desktop 的 detached HEAD c923f887 上执行,包含当前工作区文档与 CI 门禁调整。

已通过的自动门禁:

  • cd frontend && npm run version:check
  • cd frontend && npm run release:env:check
  • cd frontend && npm run runtime:check
  • cd frontend && npm run desktop:release:check
  • cd frontend && npm run ui:contract
  • cd frontend && npm run type-check
  • cd frontend && npm run test:unit
  • cd frontend && npm run build
  • cd frontend && npm run desktop:build:app
  • cd frontend && node --check scripts/verify-desktop-update-feed.mjs

验证结论:

  • Tauri 运行时边界、release 静态安全门禁、构建元数据预检、版本一致性和 UI 合约均通过。
  • Web 生产构建和未签名 macOS .app smoke 构建均可重复执行。
  • 当前 CI 已补齐 npm run release:env:checknpm run ui:contracttag 构建会将 VITE_BUILD_CHANNEL 规范为 release 并校验 tag 与版本号一致。
  • updater feed 校验脚本已完成语法检查;正式 latest.json 需要在签名 updater artifacts 生成后执行实物校验。

仍需正式发布前人工确认:

  • macOS 签名、公证、Apple Developer 凭据和组织 updater 私钥。
  • 签名后的 updater artifacts、.sig、checksum manifest 和 latest.json 在真实发布目录内通过 npm run desktop:update-feed:check
  • 不可变制品上传完成后,再原子替换线上 latest.json
  • Desktop 端到端人工回归矩阵、最小窗口体验验收和系统通知/自动更新真实环境验证。

7. 2026-07-02 发布稳定化推进记录

本次推进补齐了发布链路自动化,不改变桌面端产品边界:

  • 新增 npm run desktop:build:macos-release,封装 Universal macOS app/dmg release candidate 构建命令。
  • 新增 npm run desktop:update-feed:create,从签名 updater artifact 和 .sig 生成 latest.json、复制发布目录文件并生成 SHA256SUMS.txt
  • npm run desktop:update-feed:check 在传入 --artifacts-dir 时要求并校验 SHA256SUMS.txt
  • 新增 npm run desktop:release-readiness:check,在正式签名候选构建前检查 release tag、构建元数据、签名/公证变量、updater 私钥和生产 artifact HTTPS 基址。
  • 新增 .github/workflows/desktop-release-candidate.yml,在 release tag 上执行签名候选构建、feed 生成、feed 校验并上传 verified release directory。
  • npm run desktop:release:check 已检查上述脚本和 workflow,避免发布链路回退。

仍未自动完成、正式发布前必须人工确认:

  • Apple Developer 凭据、证书、签名身份、公证结果和组织 updater 私钥。
  • 生产下载源的不可变制品上传和线上 latest.json 原子替换。
  • 真实环境下的自动更新安装、系统通知、单实例和完整人工回归。

8. 2026-07-02 端到端回归优化记录

本轮端到端优化仍保持在线桌面客户端边界,不引入离线、本地业务存储或本地权限裁决。

已完成的自动化收口:

  • 服务器地址切换时,桌面设置页调用 auth.logout({ rememberCurrentStudy: false }),避免退出时把旧服务器项目记入当前用户的最近项目;随后继续清除当前项目上下文。
  • 系统偏好连接页与独立服务器设置页保持同一切换服务器语义,切换时不记忆旧服务器项目并清除当前项目上下文。
  • 系统通知轮询在部分通知显示失败或系统通知未实际发起时,先 ack 已成功发起系统通知的通知,再让失败项通过租约重试,贴合“显示成功后 ack;失败等待重试”的回归预期。
  • 自动更新管理器新增稍后提醒 24 小时抑制、安装失败可重试、检查失败不打断业务和未启用更新状态的单元覆盖。
  • 附件 API 新增 blob 下载、multipart 上传和删除端点单元覆盖,确保下载凭据继续由 axios Authorization header 承载而不是进入 URL。
  • 文件任务反馈 helper 新增选择、保存、取消保存和打开的单元覆盖,约束桌面保存/打开继续走 frontend/src/runtime/ 适配层。
  • Keychain/凭据库会话新增旧浏览器 token 迁移、未配置服务器不读取凭据、本地 30 天上限、服务器切换删除旧服务器凭据,以及恢复 token 必须先经 /me 校验的单元覆盖。
  • 桌面发布门禁新增单实例重复启动处理校验,要求重复启动时恢复、显示并聚焦 main 窗口。
  • 会话刷新后的跨窗口 token 更新不再写入 localStorage fallback,只通过内存态 BroadcastChannel 通知,避免 token 进入明文广播缓存。
  • desktop:release:check 新增 session broadcast 静态门禁,防止 TOKEN_UPDATED payload 回退写入 localStorage
  • 新增相关单元测试覆盖服务器切换不记忆旧项目、通知权限未授权不领取、部分通知失败时只 ack 成功项、系统通知未发起时不 ack、自动更新失败恢复路径、附件文件流契约、30 天在线会话恢复边界、单实例恢复行为和 token 广播存储边界。

仍需人工或真实环境验证:

  • Keychain/凭据库 30 天在线会话恢复。
  • 原生附件上传、下载、保存和打开。
  • 系统通知拒绝路径的 OS 级交互。
  • 单实例重复启动聚焦主窗口。
  • 签名 release 构建下的自动更新 feed、验签、安装和重启实物流。

9. 2026-07-02 安全边界复审记录

本轮安全复审在端到端自动化收口之后推进,不改变桌面端在线客户端边界。

已完成的安全边界收口:

  • Tauri notification capability 从 notification:default 收敛为 notification:allow-is-permission-grantednotification:allow-request-permissionnotification:allow-notify
  • desktop:release:check 新增 capability 最小化约束,拒绝 notification:default、opener URL/reveal 权限和 WebView 直连 updater 权限。
  • desktop:release:check 将 token URL 检查扩展到 tokenaccess_token 查询参数,并扩大日志敏感词检查到 tokenaccess_tokenauthorizationbearer
  • 更新弹窗 release notes 增加清理逻辑,过滤 URL、token 查询参数、access_tokenAuthorizationBearer 形态文本,避免 feed 内容把下载链接或凭据样式文本带入 UI。
  • 凭据库 Rust 单测新增带凭据 server origin 拒绝,以及 Keychain/Credential Manager account 不暴露原始服务器 origin 的覆盖。

仍需人工复审确认:

  • 真实生产 release notes 内容保持通用,不写入项目、文件、下载链接或敏感业务详情。
  • 正式签名、公证和 updater feed 环境继续使用组织 secret,不在日志、artifact 或配置中泄露私钥材料。

10. 2026-07-02 桌面体验收口记录

本轮体验收口聚焦登录、服务器设置、个人中心诊断、系统偏好和最小窗口布局稳定性,不改变业务能力边界。

已完成的体验收口:

  • 个人中心新增只读客户端诊断信息,展示客户端类型、版本、平台、构建通道、提交、服务器和能力状态,并支持复制诊断信息。
  • 登录页在桌面最小窗口附近收紧左右分栏 padding 和卡片宽度,长服务器地址继续在登录面板内换行,不撑破布局。
  • 服务器设置页增加面板内滚动和健康检查 URL 换行约束,避免 1180x760 下长 URL 或错误信息溢出。
  • 系统偏好通知/更新控制区允许换行,长状态说明使用 overflow-wrap,避免按钮和权限状态标签在窄视口重叠。
  • 个人中心对话框内容区改为内部滚动,诊断值使用强制换行,避免新增诊断信息后超过最小窗口高度。
  • 通知权限运行时改为优先读取 WebView Notification.permissiongranted/denied 状态;请求权限返回 default 时保持“待授权”,不再误标为“已拒绝”。
  • 系统偏好通知页已移除授权成功态的固定标签文案,开启状态只由开关表达;待授权、已拒绝和不可用状态继续显示提示标签。
  • 系统偏好通知页新增“测试通知”命令,完整走 frontend/src/runtime/notifications.ts -> @tauri-apps/plugin-notification 的系统通知发送链路;测试通知文案为固定通用内容,不包含项目、文件、token 或其他敏感业务信息。
  • showSystemNotification 和测试通知调用会在发送前确认系统通知权限,只有真正发起系统通知时才返回成功;桌面通知轮询据此只 ack 已实际发起系统通知的后端通知。
  • 真实 macOS .app 已验证系统偏好通知开关可调用系统通知权限并生效。验证截图见 /Users/zcc/Library/Application Support/CleanShot/media/media_boKznpjOwt/CleanShot 2026-07-02 at 11.07.52@2x.png
  • Layout.desktop.test.ts 新增静态契约覆盖个人中心诊断、登录页最小窗口断点、服务器设置滚动和偏好页控制区换行约束。
  • notifications.test.ts 新增授权、拒绝、待授权、取消授权请求和测试通知发送链路的单元覆盖。

仍需人工体验验收:

  • 在真实 macOS .app 中以 1180x760 检查登录页、服务器设置、个人中心、系统偏好和更新弹窗无重叠、无横向溢出。
  • 在真实系统通知权限拒绝流程中确认权限状态提示、开关回退和错误提示与 OS 状态一致。

11. 2026-07-13 macOS 原生体验适配记录

本轮继续保留 Tauri 和共享 Vue 前端,不引入 Swift 业务 UI,也不改变 Windows 内测边界:

  • macOS 新增标准 CTMS 应用菜单,将关于、设置、服务、隐藏和退出等项目收敛到首个应用 submenu;文件、编辑、显示、导航、窗口和帮助菜单保持独立。
  • macOS 红色关闭按钮保持真正关闭主窗口的系统语义;Tauri RunEvent::Reopen 在 Dock/Finder 再激活时从受控配置重建、显示并聚焦主窗口,重复启动继续复用同一恢复 helper。
  • 新增受控 desktop_menu_set_shortcuts command,只接受后退、前进和刷新三项经过双层校验的快捷键,并同步原生菜单 accelerator。
  • 桌面主题新增“跟随系统”,WebView 监听 prefers-color-scheme,受控 desktop_window_set_theme command 同步 Tauri 窗口外观;未向 WebView 增加原始窗口权限。
  • desktop:release:check 已增加应用菜单、Dock reopen、关闭后重建、快捷键同步、系统主题和新增 command 白名单约束。

真实 .app 仍需人工验证:红色按钮真正关闭主窗口后 Dock 重建、菜单顺序、个人中心自身关闭按钮、编辑菜单文本输入行为、自定义快捷键即时同步,以及系统明暗模式切换时的标题栏一致性。

本轮未引入以下条件性 Swift 能力:Quick Look 需先确认附件审阅频率和 Windows 降级语义;Touch ID 会改变现有 30 天会话恢复体验,需先确定凭据访问控制策略;通知点击回跳需先定义固定、无敏感信息的动作和目标页面。三项均不是当前发布前置条件。