8.3 KiB
在线更新发布源修复与测试验收(2026-10-07)
本次修复已在 https://demo.51easyai.com 完成真实浏览器验收。候选镜像和测试发布源只用于测试环境;验收时尚未提交代码;该测试验收未更新正式 latest 镜像和仓库根目录的正式发布清单。
已复现问题与根因
- 测试站运行
3.22.0.20260930.3,页面从官方站运行版本接口检测到3.23.1.20261006。 - 浏览器点击“立即更新”,任务
bcaa8c58-3628-42fc-af02-bc5f97bbc1c2返回no_update,实际版本和核心容器均未改变;页面仍提示有新版本。 - 更新器实际读取
/var/lib/easyai-updater/release-manifest.json,该路径由data/deployment-config/legacy-compose.json覆盖,内容仍指向旧版镜像。更新器不获取远程发布清单。 - 页面检测来源与执行来源不同;前端没有核对任务实际提供的版本,误把旧清单的“无需更新”作为新版已经完成更新。
- 正式部署仓库的发布清单也仍为
3.22.0.20261002.2;镜像仓库有后端3.23.1.20261006标签,但没有同版本前端标签。单纯改成本地另一条清单路径不能解决发布信息不一致。
最小修复及兼容性
- 更新器默认从部署仓库
main分支获取正式清单;私有源使用UPDATE_MANIFEST_URL,离线部署须显式选择UPDATE_MANIFEST_SOURCE=local。默认远程模式忽略历史本地路径覆盖,获取失败不会退回旧清单。 - 受控模式的版本检测通过更新器获取可安装版本,不再取官方站当前运行版本;旧 Watchtower 模式保留原协议和检测缓存。
- 新版客户端提交检测到的版本与发布标识;执行前重新获取清单,目标发生变化时拒绝安装另一版本。旧客户端只传
action的请求继续兼容。 - 任务保存完整清单,恢复及回滚使用同一快照。前端核对成功或“无需更新”的实际版本,避免错误提示;普通更新、强制更新和管理员新版提醒成功后均刷新检测结果。
- 只对更高版本显示新版提示;受控更新拒绝自动回退到更低版本,包括强制更新。发布源不可用时显示“暂时无法检查”和“重新检查”。
- 主要调用方:系统设置更新卡片、管理员新版提醒、任务恢复入口、自动更新调度及旧手动接口;未增加业务 Provider 或改变自动更新开关。
责任代码与本次改动位置:
- 后端
apps/easyai-updater/src/release-source.ts、update-runner.ts、main.ts:发布源、目标固定、快照恢复与内部发布信息接口。 - 后端
apps/easyai-server-main/src/app.service.ts、system-update/managed-update.service.ts、system-update/system-update.controller.ts:统一检测来源、兼容请求及目标字段传递。 - 前端
composables/system-update/useSystemUpdateJob.ts:核对更新结果与检测目标;pages/admin/sys/operationConfig.vue和components/product-update/ProductUpdateAdminTouchpoint.vue:版本比较、检测异常提示及成功后刷新。 - 部署仓库
docker-compose.yml、.env.sample:默认远程发布源与显式离线选项;原正式清单保留不变。
真实运行验收
先部署保留旧版本号的修复版控制入口作为测试基线;前端基线使用独立镜像,使实际任务能够验证网页容器替换。最终安装的镜像来自本次工作区构建。候选清单见同目录 release-manifest.candidate.json,不作为正式发布清单。
| 场景 | 结果 |
|---|---|
| 管理员通过真实登录界面进入系统设置并点击立即更新 | 只提交一次请求;任务 5a2968c5-9615-4e1f-9d88-79ada0f6aa06 成功,页面约 88.9 秒完成反馈 |
| 版本与容器核对 | 3.22.0.20260930.3 → 3.24.0.20261007;后端和网页容器实际替换,实时服务镜像无需变化 |
| 页面刷新 | 显示 3.24.0.20261007、已是最新,不重复弹出新版提醒 |
| 同版本普通更新 | 返回 no_update,不重复替换容器 |
| 强制更新确认后取消 | 不创建任务 |
| 发布源真实返回 HTTP 503 | 页面显示暂时无法检查,不能误报已是最新;恢复源后点击重新检查恢复正常 |
| 发布源改成旧正式清单 | 不提示有新版;点击强制更新后,任务 92182a3e-1948-4d82-99bc-aacb7c1c601f 在镜像拉取前被拒绝,原因 release_older_than_current |
| 配置保留 | 核心服务环境变量、完整容器配置(除镜像)、端口、挂载路径及权限、网络地址和别名保持一致;宿主机 .env 与 docker-compose.yml 哈希不变 |
| 自动更新设置 | 验收前后均为开启,未改变原开关 |
| 更新器实际重启 | 已完成任务完整保留,恢复后仍提供同一测试发布信息;四个相关容器均健康 |
首次候选镜像补装运行依赖时移除了内部共享包,启动检查失败后恢复了原服务。补齐共享包并通过加载检查后,重新部署健康基线,再进行上述正式浏览器验收;没有把失败启动计为升级成功。
定向检查
本地检查使用 Node 22.22.0、Yarn 1.22.22,共 79 个不同的定向行为用例通过:
| 检查 | 结果与耗时 |
|---|---|
| updater Node 测试 | 14 个通过,约 1.6 秒;覆盖真实 HTTP 发布源、源失败、目标变化、快照恢复及旧版回退保护 |
| 后端 system-update Jest | 38 个通过,约 2.3 秒;包含新旧检测协议、实际 DTO 校验、自动更新调度和旧手动接口 |
| 前端任务与实际挂载测试 | 首轮 13 个通过,约 49 秒;追加后的设置与更新处理函数 5 个通过,约 4 秒,其中新增强制更新用例 1 个 |
| 版本工具和产品版本测试 | 13 个通过,约 0.13 秒 |
| 部署脚本隔离 dry-run | 通过,约 4.3 秒,确认默认远程源及原部署契约 |
| 后端及更新器构建 | 通过,约 25 秒 |
| 最终前端生产构建 | 通过,约 168 秒 |
隔离环境中的旧实现会失败:前端将旧版 no_update 误判成功;更新器六个新增发布源场景不能满足预期。相同失败用例在修复后通过。HTTP 发布源测试使用受控 Docker 替身,真实容器替换另外通过测试站验收;没有用源码断言或构建替代运行验收。
证据与测试环境维护
完整本地证据目录:/Users/wangbo/.codex/visualizations/2026/10/07/01a11505-cae4-76e0-9ba9-235f6962b2b4/system-update-diagnosis。
acceptance-summary.json、browser-upgrade-acceptance.json、browser-acceptance.json:实际请求、任务、版本及异常场景。acceptance-update-success.png、acceptance-final-card.png、acceptance-feed-unavailable.png、acceptance-older-release-blocked.png:真实页面结果。config-before-browser.json、config-after-browser.json、mount-verification.json、final-verification.json:配置、健康及任务持久化核对。Docker 返回的挂载数组顺序不稳定,已按内容核对路径和权限一致。*-tests.log、*-build*.log、updater-before-tests.log、frontend-before-tests.log:定向检查与修复前失败记录。
测试环境保留内部测试发布源容器 easyai-update-release-feed,无新增宿主机端口。任务专用配置位于 /root/easyai/data/update-acceptance-20261007,其中 accepted.json 和更新后的 baseline.json 指向实际验收镜像;rollback.json 保留原镜像引用。维护测试站时须使用原 Compose、原 legacy override 与该目录下 accepted.json 的组合,不能将该测试清单误发布成正式清单。
未验证范围及正式发布条件
- 未发布生产,未执行全站业务回归或全量质量门禁;健康检查和更新入口验收不能代表全部业务兼容性。
- 定时自动更新实际到点执行未验收,调度逻辑已做定向测试;自动更新设置保持原值。
- 已测试持久化快照恢复,并实际重启验证已完成任务;没有在真实容器替换过程中故意中断 updater。旧 updater 未带清单快照的在途任务跨版本迁移尚未验收,升级更新器前应先确认没有执行中的更新任务。
- 正式发布时须一起交付 updater 1.2.0、后端和前端,并在镜像完整发布后同步正式清单的版本、发布标识、不可变摘要及大小。只改官方站版本或只推送
latest仍无法代表客户可安装的新版本。