2 Commits
Author SHA1 Message Date
zijunchiang 1acc75f00e test(release): 逐文件核对「发布包 == 打标签的树」(T-WEB-018)
「发布包里的东西必须等于打标签的那棵树」此前靠人抽查几个文件(README、contracts/VERSION…),
必然会漏。上一轮实测就暴露了两个认知错误:Web 的发布包其实**包含** web-server/test/**
(33 个文件,我此前以为不含),以及包内另有 177 个**构建产物**(admin/、web-server/dist/)
必须与工作树构建比 —— 只比源码文件是不够的。

新增 web-server/scripts/check-tracked-release.py(纯标准库,支持 .tar.gz / .zip):
对包内**每一个**文件按路径决定比较基准 —— 构建产物(admin/、web-server/dist/)比工作树
构建结果,其余文件比 `git show <tag>:<路径>`;任何内容不一致 / 包内多余(既不在 git 也不是
声明的构建产物)/ 构建产物在工作树缺失都会列出并以非 0 退出。`npm run release:check` 已注册。

实测(**已发布**资产重新下载后跑):
  包内 356 个文件:源码/文档 179(与 tag v0.7.5 比)+ 构建产物 177(与工作树构建比)
  ✓ 逐文件一致:0 处不一致 / 0 处多余 / 0 处缺失
反向验证:篡改 README.md 一个字节 + 塞入 web-server/EXTRA.txt →
  ✗ 内容不一致 1 处(README.md)、✗ 包内多余 1 处(EXTRA.txt),退出码 1。

顺带修掉脚本自身一个路径映射缺陷:BUILD_OUTPUTS 的键若是不带 / 的**精确文件名**
(Windows/Android 的单文件产物),原实现会把剩余路径拼成 ".../OpenDance.exe/"
导致「工作树缺失」误报;现在区分「目录前缀」与「精确文件名」两种键。

注:accept-release.sh 的接入只在下一个版本的产物里生效(本仓 accept-release.sh 本身
也在发布包内,v0.7.5 已发布);对已发布的 v0.7.5 包仍可用 `npm run release:check -- <包> --tag v0.7.5`
逐文件核对(已跑通)。同时已把同一脚本复制到 opendance-windows / opendance-android 并接入
两端的 accept-release.sh。
2026-09-18 03:10:05 +08:00
zijunchiang 06b476c4e2 test(accept): 产物级端到端验收自动化 + 实体注册核对落到 dist(v0.7.4 实测全通过)
背景:`npm run verify` 跑的是源码与刚构建的 dist,而交付给部署方的是
`dist-release/*.tar.gz`;两者之间隔着「解包 → npm install --omit=dev → 迁移 →
起进程」四步。本项目三次生产事故(v0.7.0/v0.7.1 起不来、v0.7.3 场景上传 400、
v0.7.4 `No metadata for "Scene"`)全都只在产物真正跑起来时才暴露。

一、`scripts/check-bootstrap.mjs` 新增「产物级实体注册核对」
   直接读包内 `dist/entities/`:逐个 import 每个 *.entity.js(@Entity() 装饰器
   在模块加载时写 metadata),要求
     * 每个实体文件里被 @Entity() 装饰的类都在 ENTITIES 里;
     * ENTITIES 里不允许有未装饰的成员;不允许重复;
     * Scene 必须注册(点名)。
   为什么必须单独有这一条:漏注册实体**不影响启动**(TypeORM 只按 ENTITIES 注册,
   DI 图照样解析、/health 正常、migrationsPending 0),只在第一次查该实体时抛
   `No metadata`,导致 /api/scenes、/api/catalog/export、/api/catalog/version、
   /api/reviews?status=pending 全部 500 —— 客户端一个目录都拉不到。
   原来的启动冒烟与 entities-registry.spec.ts(查 src/)都抓不到它。

   反向验证(在真实 v0.7.4 发布包里复现生产缺陷):把包内
   dist/entities/index.js 的 ENTITIES 去掉 Scene 后,核对立即失败并给出
   「scene.entity.js 里的 Scene 没有注册进 ENTITIES」与「ENTITIES 里没有 Scene」;
   还原后通过。

二、`scripts/accept-release.sh`(新增):一个 tar.gz 的产物级端到端验收
   解包 → 装生产依赖 → 跑迁移 → 起服务 → 校验 /health 关键字段
   (版本 / 数据库 / migrationsPending / scenes.available)→ 产物级实体注册核对
   (用仓库当前的 checker 覆盖包内旧版本,验收标准以最新为准)→ 跑发布包自带的
   test/smoke.mjs → 停服务。只需要一个现成的 MySQL/MariaDB,不装数据库
   (与项目「不部署 MySQL」的边界一致)。

   实测(opendance-web-v0.7.4.tar.gz + 真实 MySQL 9.7.2,全新库从零迁移):
     ✓ /health version=0.7.4 与 package.json 一致
     ✓ 数据库已连接(opendance_accept,pending=0)
     ✓ scenes.available=true(生产 v0.7.3 是 false + No metadata)
     ✓ 产物级实体注册核对通过:6 个实体文件全部注册进 ENTITIES(含 Scene)
     == 结果:80 通过 / 0 失败 ==
   冒烟 [7] 段即 ④ 验收:POST /api/scenes 201、一律 pending、GLB 直传不重编码、
   glbUrl 生成、重力/风/摩擦/坡度/出生点/相机模式按配置原样入库、越界夹紧与非法
   枚举回退、审核通过后 catalog/export 的 scenes[] 带完整物理参数与 GLB/缩略图字段、
   目录指纹纳入场景变更、详情、状态过滤、DELETE 204。

   `npm run verify` 全绿:契约 6 项 / 35 路由 / 版本一致 / 295 项测试 / build /
   产物级注册核对 / 启动冒烟。
2026-09-17 04:44:30 +08:00