Files
dsh_ai1net_server/dsh-server-docs/04-调整方案/35-ws-cleanup误删平台bundle包.md
T

46 lines
2.7 KiB
Markdown
Raw Normal View History

# 35 · ws-cleanup 误删平台 bundle 包(P0 回归修复)
- 日期:2026-09-11
- 触发:验证档案 34 时 `apply` 报「安装失败」,追查发现是**与本改动无关的既有 P0**
- 状态:**✅ 已修复(commit `70302e6`)并 dry-run 验证**
## 一、事故
`scripts/ws-cleanup.cjs`(档案 28 用户数据清理)的 T1 规则:
```js
const T1_SUFFIX = ['.tgz', '.tmp', '.log', '.bak', '.part', '.crdownload']
...
const keepAll = existsSync(join(ws, '.keep'))
if (keepAll && t2.length > 0) { /* .keep 只豁免 T2 */ }
```
→ **ws 顶层的 `.tgz` 被无条件删除,`.keep` 也豁免不了**。
而平台三个自建 bundle 是以 `file:<ws>/xxx.tgz` 安装进 profile 的:
`@dsh-local/portal-entry` / `@dsh-local/business-plugins` / `@dsh-local/workspace-scoped-picker`。
**后果链**:清理一跑 → tgz 消失 → profile 的 `dependencies` 指向不存在的文件 → **之后任何 `pnpm` 操作都 `ENOENT` 失败**(用户侧「启用/禁用功能插件」正在此列)。
实测现状:**admin 三个包全丢、guest 丢一个**。之所以一直没被发现:`node_modules` 已经装好,实例照常运行,**问题只在下一次 pnpm 操作时才爆发**。
## 二、修复(不依赖硬编码文件名)
`ws-cleanup.cjs` 在扫描每个用户前,先读该用户**所有 profile 的 `file:` 依赖**,得到「被引用的 ws 文件名」集合 `protectedNames` → 循环里这些文件**豁免 T1**(体积计入「资产」而非「垃圾」)。
配套:把三个平台包重建到 `/opt/dsh/artifacts/`(`portal-entry-0.4.3` / `business-plugins-0.1.0` 从 `04-调整方案/poc/` 源码重新打包;`workspace-scoped-picker-0.1.4` 用现成产物),并**幂等地放回**各用户 ws(只在缺失时补,不覆盖用户文件)。
## 三、验证
`node scripts/ws-cleanup.cjs`(dry-run):
| 用户 | 修复前 T1 | 修复后 T1 |
|---|---|---|
| admin | 4 项(`.local` + **3 个 .tgz**) | **1 项(只剩 `.local`)** |
| guest | 2 项(`.cache` + **1 个 .tgz**) | **1 项(只剩 `.cache`)** |
三个 `.tgz` 全部转为「资产」;`pnpm add` 恢复正常(档案 34 的隔离实测得以继续跑完)。
## 四、教训
1. **T1「精确名或后缀」规则与「平台自己用 `file:` 引用的产物」天然冲突** —— 任何"按后缀删垃圾"的策略,都必须先排除"被其它组件引用的文件"。
2. **这类 bug 的隐蔽性来自"延迟爆发"**:删掉的是安装源,运行时不报错,只在下次安装时炸。→ 清理类脚本的验证不能只看"实例还能跑"。
3. `04-调整方案/poc/` 里的源码是**唯一可重建平台 bundle 的副本**(仓库里没有),`/opt/dsh/artifacts/` 补上了这个缺口。