Files
apes-sidebar/docs/wiki/07-开发与测试.md
figmar 63bcb47ed0
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
docs(wiki): 建立项目百科(需求分析 → 架构 → 功能 → 部署 → 运维 → 排障 → 开发与测试)
Gitea 的 wiki 此前是空的。本次把百科的**源文件版本化进仓库**(docs/wiki/),
再用脚本发布到 wiki——文档因此与代码同源、可 review、可回溯,而不是只存在于网页上。

八页(Home 作为导航与项目速览):

  Home          项目速览 / 两条版本线的宿主兼容性差异 / 文档地图 / 谱系说明
  01-需求分析    五条核心需求(现象·根因·验收标准·状态)+ 非目标与约束 + 需求→实现→锚点
  02-系统架构    host/client 分层与产物、三层接口面、面板宿主层叠契约与 isolate 陷阱、关键不变量
  03-功能说明    面板/预览器总表、信息层三 tab 数据来源、HTML 预览交付方式、i18n 与令牌、配置面
  04-部署指南    宿主版本矩阵与选线决策、profile 接线(file: + bundles)、构建、验收清单、卸载与切换
  05-运维手册    分支/tag 策略与回滚剧本、发布纪律(版本三元组/非确定性产物)、巡检、备份铁律、诊断
  06-故障排查    8 类症状 → 根因 → 处置(含本会话实机定位过的全部案例)+ 定位手法
  07-开发与测试  工程约定、目录、门禁基线、测试分层与契约 spec、扩展点、测量式 re-base 方法

脚本 scripts/push-wiki.sh:幂等发布(POST 新建 / PATCH 覆盖),凭据走 git credential helper
(临时 netrc,600,退出即删),不落盘、不进 argv。已实测:8 页全部发布成功,复推为更新。

内容纪律:只写**本次会话实证过**的事实(提交号、行数、层级取值、产物大小、宿主版本矩阵、
静默禁用机制、re-base 步骤),未验证的部分明确标注为待验证或已知限制;并如实说明本仓库
历代承载过 v1 / v2 / 当前 fork 三代实现,本百科描述的是 main 上的 fork 线。
2026-10-04 01:19:31 +08:00

9.3 KiB
Raw Permalink Blame History

07 · 开发与测试

面向改代码的人(与 agent)。先读 02-系统架构 §六「关键不变量」, 再动手改,能省掉大部分回退。

一、工程约定

项 约定
语言 TypeScript strict(含 noUncheckedIndexedAccess)、ESM、导入写 .ts 扩展名
构建 tsdown(rolldown),host/client 双产物;懒加载 chunk 走 src/client/chunks/
包管理 pnpm(11.x);pnpm install 会触发 prepare 构建
测试 vitest;默认 node 环境,需要 DOM 的 spec 用 // @vitest-environment jsdom
宿主 peer dsh-better-sidebar(本仓库已改名)永不打包,必须留在 peer/neverBundle 一侧
命名 信息层 tab id 前缀 apes:;插件名 dsh-apes-sidebar
颜色 只用 --dsw-alias-* 设计令牌(var(--token, <回退>)),不写死色值

二、目录结构(main 实况)

src/
  index.ts              HOST 入口:name / inject / apply(ctx, config),路由与围栏装配
  invariant.ts          零依赖不变量面(host/client 共用)
  context-types.ts      cordis 服务面契约(ctx.sessions / sidebarRight / …)
  trust-fence.ts        请求信任围栏(Host 头 / 令牌)
  html-route.ts         /sidebar/html 路由(路径围栏 + 体积上限)
  config.ts             挂载行配置 schema 与默认值(readLimit / mediaLimit / …)
  fs-*.ts git.ts …      文件系统、Git、会话、任务、终端等 host 能力
  client/
    index.tsx           CLIENT 入口(apply):装配 store/service/native/apes
    service.ts          ctx.betterSidebar 服务实现(对外 API + 能力清单)
    state.ts            侧边栏状态存储(store)
    native/             原生右侧栏适配(tab 记录、tab 体、tab 芯片标题)
    builtins/           内置面板:editor / git / subagent / sidechat / terminal / browser + 预览器
    sidebar/            自绘底部工作台与开合控件
    apes/               信息层三 tab(本 fork 增量:tabs.tsx / icons.tsx / locales.ts)
    changes/ diff/      文件变动与 diff 视图
    chunks/             懒加载 chunk:editor / locale / mermaid / terminal
    locales*.ts         i18n(`locales.ts` + 19 个语言覆盖文件)
    *.module.css        组件样式(含 sidebar.module.css:面板宿主层契约)
tests/                  vitest specs(131 个文件,含 e2e/ 的挂载脚本型用例)
scripts/                install / switch / e2e 挂载 / 发布辅助
docs/                   external-plugin-guide.md(对外契约)+ plans/(上游历史设计)+ wiki/(本百科源文件)

三、门禁(提交前必跑)

pnpm typecheck     # tsc --noEmit(strict)
pnpm build         # rm -rf lib && tsc(d.ts) && tsdown
pnpm test          # vitest run

当前基线(本次会话实测):main = 131 文件 / 1367 通过 + 9 skip; sync/v0.24.1 = 141 文件 / 1515 通过 + 9 skip。全量 test 约 3–4 分钟。 任何一条不过就不要提交;三条全绿才算完成。

四、测试体系(按层次)

层次 环境 手法 代表 spec
宿主插件形状 / 对外契约 node 断言导出面与语义(含与 package.json 的版本一致性) smoke.spec.ts · plugin-shape.spec.ts · service.spec.ts · consumer-types.ts
原生侧边栏适配 node 用「类型为真、运行时为假」的 mock 注册表 native-surface.spec.ts
面板宿主与层叠契约 node + jsdom CSS 断言 + DOM 挂载点解析 panel-host-css.spec.ts · panel-host-parent.spec.ts
预览交付 node + jsdom 纯逻辑(<base> 注入)+ 客户端挂载(stub fetch,createRoot + act) html-preview.spec.ts · html-preview-frame.spec.tsx
沙箱契约 node 对渲染结果做字符串断言(服务端渲染把 srcDoc 输出为 camelCase srcDoc="",与客户端设置的真实属性 srcdoc 不同 —— 断言方式随之不同) sandbox-views.spec.tsx
信息层 tab node mock ctx.sessions.list 与投影 apes-tabs.spec.ts
桌面环境探测 node stub 全局(URL stamp / preload 标记) desktop-env.spec.ts

约定:

  • 不引入 jsdom 到默认环境:需要 DOM 的 spec 在文件头写 // @vitest-environment jsdom。
  • 不依赖真实 DSH 宿主:需要真宿主的用例归到 tests/e2e/(脚本型挂载),不进单元门禁。
  • 新增「契约」必须同时新增 pin 它的 spec(例如本文档提到的 25 层级、沙箱令牌、srcDoc 交付方式, 都有对应断言)。改契约 = 改代码 + 改 spec,两者必须一起提交。

五、扩展点(对外服务)

其他插件通过 ctx.betterSidebar 接入:

// 注册一个 tab 类型
await ctx.betterSidebar.registerTab(descriptor)
// 注册一个文件预览器
await ctx.betterSidebar.registerFileViewer(viewer)
  • 服务按能力清单做前向兼容门控(能力只增不减),消费方应据此判断可用性而非版本号比较。
  • 完整契约、字段表与示例见仓库内 docs/external-plugin-guide.md。
  • 改服务面时:只能新增;删除或改语义需同步更新能力清单、指南与本百科。

六、上游同步:测量式 re-base(本项目的方法论)

不 merge 上游。上游每个版本差距可达数百文件,直接合并会引入无关变更并放大冲突面; 本项目改用「先测量、再重建、后补增量」:

步骤

① 测出自己的增量(把改名归一化掉)

# 对每个变更文件:取出两边 blob,把改名归一化后比较
old = git('show', f'{base}:{path}'); new = git('show', f'HEAD:{path}')
rename_only = old.replace('dsh-better-sidebar', 'dsh-apes-sidebar') == new

→ 得到三张清单:仅改名(自动处理)/ 真改动 / 新增。数量级参考:v0.19.1→v0.24.1 为 78 / 11 / 10。

② 确定改名的精确范围(不要重新推断)

# 仍保留上游名字的文件 = 有意保留(本仓库为 README.md、package.json、docs/plans/*)
comm -12 <(git grep -l 'dsh-better-sidebar' <旧基线> | cut -d: -f2 | sort) \
         <(git grep -l 'dsh-better-sidebar' HEAD | cut -d: -f2 | sort)

其余文件只做子串替换 dsh-better-sidebar → dsh-apes-sidebar;camelCase 服务名 betterSidebar 必须保留。

③ 造一个"改名归一化基座",让增量与改名解耦

git checkout -B tmp/norm-<旧基线> <旧基线 tag>
#   → 套同一口径改名 → commit
git diff tmp/norm-<旧基线> HEAD -- . ':(exclude)pnpm-lock.yaml' > /tmp/apes-delta.patch
# 校验:这条 diff 的文件清单必须恰好等于 ① 的「真改动 + 新增」

④ 在新基线上重建 + 三点合并

git checkout -B sync/<新基线 tag> <新基线 tag>
#   → 套同一口径改名 → commit("rename only")
git apply -3 /tmp/apes-delta.patch        # 冲突会以 <<<<<<< ours / >>>>>>> theirs 留存(命令本身不失败)

⑤ 逐条判读冲突(只有五种形态)

形态 判定
版本/身份 JSON 取新基线侧,再改成本 fork 的版本;两侧各自新增的元数据取并集(依赖、files 条目、forkedFrom 块)
上游搬走了某个 import 取上游的新路径,保留本 fork 的新 import
上游新环境里的注册块 保留本 fork 的块,放进上游的新上下文
同一函数两边都定义(上游上移/下移了它) 删除本 fork 那份;用 grep -c 'const <name>' 断言只剩 1 处
长文档(README) 整体取上游原文,再机械套上本 fork 的两处增量:顶部 fork notice + 包名替换(跳过 github.com/<上游 org> 与徽章域名行)

⑥ 迁移检查(API 漂移)+ 门禁

  • 先解完所有冲突标记再 pnpm install(prepare: tsdown 会因为语法错误直接失败), 并删掉 lockfile 重新生成(不要把旧 lockfile 带过来)。
  • 逐条跑 pnpm typecheck 修类型漂移。经验:漂移通常集中在本 fork 自己新增的模块里。 上游会在 context-types.ts 的注释里写清迁移路径(例:0.1.6 的 subagentsByParent → 0.1.7 的 projectionsBySession[id].values.subagentCatalog,list.current 被删除),照注释做即可。
  • 最后跑门禁三连,并且用 grep 核对关键标记(tab id、srcDoc、dsh-desktop-content-viewport 等)。

⑦ 收尾(纪律)

  • 安全锚点:backup/pre-sync-<新基线> 分支 + apes-v<版本> tag,推送远端。
  • 不要把新线同步进正在使用的 profile:peer 范围可能已跨 minor,同步过去只会「静默失效」, 同时把本机环境弄脏。保持 profile 在已验证的线上。

七、提交与分支纪律

  • 提交信息用 type(scope): 摘要(feat / fix / chore / docs / refactor),正文说明为什么与验证方式。
  • 一个提交一件事;re-base 类改动拆成「rename only」+「增量移植」两个提交,便于回溯与回滚。
  • 分支命名:sync/<上游 tag>(跟随上游)、backup/pre-sync-<基线>(锚点)、其余按需。
  • 提交前:门禁三连 + 关键标记 grep + (若涉及部署)提醒 profile 重装与完整重启。