Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
950ad6b
feat(search): probe fd/rg for fs.search file-name search (fixes #203)
yanzhaohui1999 Aug 19, 2026
fec9637
feat(search): gate fs.search debug logging behind DSH_SEARCH_DEBUG
yanzhaohui1999 Aug 21, 2026
9cc1277
docs(search): sync README/AGENTS.md with fd/rg engines; log real-host…
yanzhaohui1999 Aug 21, 2026
ca16b34
fix(search): normalize Windows engine output shapes (backslash/CRLF);…
yanzhaohui1999 Aug 22, 2026
748c6c6
fix(search): enumerate Windows/profile layouts for DSH's bundled rg p…
yanzhaohui1999 Aug 22, 2026
2bf9e26
fix(search): pin rg output to '/' separators with --path-separator
yanzhaohui1999 Aug 22, 2026
c0c42be
docs(search): add Windows verification manual for fs.search engines
yanzhaohui1999 Aug 22, 2026
aac5305
test(search): add Windows engine-output shape checker script
yanzhaohui1999 Aug 22, 2026
bb1a9e7
test(search): add Windows real-probe checker for bundled rg layout
yanzhaohui1999 Aug 22, 2026
aa61c6d
test(search): add Windows full-chain e2e helper (mount -> dsh web -> …
yanzhaohui1999 Aug 22, 2026
02db9c3
fix(search): align fd truncation with rg (--max-results cap+1, pinned…
yanzhaohui1999 Aug 22, 2026
865a942
docs(search): record Windows real-machine acceptance in design doc
yanzhaohui1999 Aug 22, 2026
37afc80
fix(search): brace-safe rg globs, timeout keeps engines alive, .git-f…
yanzhaohui1999 Aug 22, 2026
3be5190
fix(search): align engine excludes with the walk's skip dirs on the n…
yanzhaohui1999 Sep 5, 2026
64dafd4
feat(search): derive directory matches for rg (fd/walk parity)
yanzhaohui1999 Sep 5, 2026
670eb0a
Merge remote-tracking branch 'origin/main' into sync/pr303
yanzhaohui1999 Sep 12, 2026
b97a003
ci(search): gate the fd/rg engine output shape inside the ci-windows …
yanzhaohui1999 Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ jobs:
# runs the same typecheck / test / build / consumer-type-surface steps on
# a real Windows runtime. The plugin-mount lane (real DSH + Playwright
# Chromium) stays ubuntu-only and is deliberately NOT mirrored here.
#
# It also carries the search-engine gate: real fd/ripgrep binaries land on
# PATH (choco) before the suite runs, and the byte-level output-shape
# assertions run against them afterwards. Windows is where the engine's
# output format bites hardest (backslash separators, CR, GBK code pages) —
# see docs/ci-windows.md for the red-line list.
ci-windows:
runs-on: windows-latest
timeout-minutes: 30
Expand Down Expand Up @@ -86,6 +92,12 @@ jobs:
run: |
git config --global user.name "dsh-better-sidebar-ci"
git config --global user.email "ci@dsh.invalid"
# Real engine binaries for the byte-level assertions below. The probe
# inside src/search-engines.ts reads PATH, so the whole suite now runs
# with engines present; the specs inject their own probes and are
# unaffected either way (tests/search-engines.spec.ts).
- name: Install fd / ripgrep (choco)
run: choco install fd ripgrep -y --no-progress
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
Expand Down Expand Up @@ -118,6 +130,13 @@ jobs:
run: pnpm build
- name: Check consumer type surface
run: pnpm check:consumer-types
# Byte-level assertions on the REAL engine binaries installed above:
# pinned (`--path-separator /`) output must carry no '\' and no CR, and
# a Unicode filename must survive the UTF-8 pipeline. An engine that is
# absent skips (so the script stays runnable on a dev machine); an
# assertion failure exits non-zero.
- name: Engine output-shape assertions (real fd/rg)
run: node scripts/win-engine-check.cjs --assert

# Pack the plugin as an npm tarball, mount it into a REAL DSH instance
# through the official `dsh plugin --profile web add` channel, and render
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

「npm 打包 → 真实挂载 → 无头渲染」门禁(证明打包产物在真实 DSH 挂载后不 crash):`pnpm build && pnpm pack` 产 tarball → `scripts/e2e-mount.sh` 装进全新 scratch profile(`dsh plugin --profile web add <tarball>`)并启动真实 `dsh web`(keyless,`--port 0`)→ `tests/e2e/mount.e2e.ts`(Playwright)断言 `[data-dsh-better-sidebar]` 挂载、无错误条/pageerror/console 错误,展开 DSH 原生右侧栏后经其 guide 页逐个打开插件 tab 类型(含终端懒加载 chunk),再经插件文件树(原生 `files` kind 接管)打开 seed 文件强制加载 editor chunk(`client-editor.js`),并跑 mermaid / README 预览与 sidechat 宿主路由烟测。

本地:`pnpm build && pnpm pack && pnpm exec playwright install chromium && pnpm test:mount`。CI 钉 `@deepseek-ai/dsh@0.1.5-rc.2`(npm `next`;`latest` 仍是 rc.1;peer 下限 `^0.1.5-rc.1`)。该步骤用 `NODE_OPTIONS=--max-old-space-size=4096`:钉版自身的传递依赖是浮动 `^` 范围,上游分阶段发布预发布版时(rc.2 于 2026-09-10 的 14:43–14:57 逐包上线)npm 会组出混合 peer 图,3062 条 ERESOLVE 后 OOM(exit 134);另一个失败模式是对未发布兄弟包 ETARGET,靠「钉一个已完整发布的版本」修掉。**不要加 `--legacy-peer-deps`**:它跳过的正是全局安装必须提供的 peer,`@deepseek-ai/dsh-app-boot` 在 boot 时 require 的 `@deepseek-ai/cordis-plugin-group` 是 peer 而非 dependency,加了它 CLI 直接 `ERR_MODULE_NOT_FOUND`(实测过一次,见 rc.2 计划 C 节)。`ci-windows` 的 `Test` 跑 `pnpm test:windows`(`--maxWorkers=2`)而非 `pnpm test`——多个 spec 真起进程(`agent-pty` / `pty-deps` / `pty-helpers` / `install-powershell` / `smoke`),2 核 runner 上并行起 ConPTY / 冷启 `powershell.exe` 是超时与 worker 静默死亡的放大器;`vitest.config.ts` 的全局 `testTimeout: 15_000`(默认 5000 在 Windows 上对真起进程的用例太低,三个不同文件先后翻车)是配套的一半。e2e spec 命名 `*.e2e.ts` + vitest `exclude` 双保险;**改 `exclude` 必须保留默认排除项**(exclude 整体替换默认值)。
本地:`pnpm build && pnpm pack && pnpm exec playwright install chromium && pnpm test:mount`。CI 钉 `@deepseek-ai/dsh@0.1.5-rc.2`(npm `next`;`latest` 仍是 rc.1;peer 下限 `^0.1.5-rc.1`)。该步骤用 `NODE_OPTIONS=--max-old-space-size=4096`:钉版自身的传递依赖是浮动 `^` 范围,上游分阶段发布预发布版时(rc.2 于 2026-09-10 的 14:43–14:57 逐包上线)npm 会组出混合 peer 图,3062 条 ERESOLVE 后 OOM(exit 134);另一个失败模式是对未发布兄弟包 ETARGET,靠「钉一个已完整发布的版本」修掉。**不要加 `--legacy-peer-deps`**:它跳过的正是全局安装必须提供的 peer,`@deepseek-ai/dsh-app-boot` 在 boot 时 require 的 `@deepseek-ai/cordis-plugin-group` 是 peer 而非 dependency,加了它 CLI 直接 `ERR_MODULE_NOT_FOUND`(实测过一次,见 rc.2 计划 C 节)。`ci-windows` 的 `Test` 跑 `pnpm test:windows`(`--maxWorkers=1`,单 fork,不再回收 worker)而非 `pnpm test`——多个 spec 真起进程(`agent-pty` / `pty-deps` / `pty-helpers` / `install-powershell` / `smoke`),2 核 runner 上并行起 ConPTY / 冷启 `powershell.exe` 是超时与 worker 静默死亡的放大器;`vitest.config.ts` 的全局 `testTimeout: 15_000`(默认 5000 在 Windows 上对真起进程的用例太低,三个不同文件先后翻车)是配套的一半。e2e spec 命名 `*.e2e.ts` + vitest `exclude` 双保险;**改 `exclude` 必须保留默认排除项**(exclude 整体替换默认值)。同一条 lane 还承载**搜索引擎门禁**:`choco install fd ripgrep` 把真二进制放进 PATH(整条套件因此在引擎在场的环境下跑),套件后对真引擎跑 `node scripts/win-engine-check.cjs --assert` 的字节级断言(钉死 `--path-separator /` 的输出不得含 `\`/CR,中文文件名必须命中;引擎缺失则跳过)。红线清单与标准动作见 [docs/ci-windows.md](docs/ci-windows.md)。

---

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ GitHub topic [`dsh-better-sidebar`](https://github.com/topics/dsh-better-sidebar

**✨ 新功能**

- 📁 **文件窗口与资源管理器二合一**([#151](https://github.com/omdsh-dev/DSH-better-sidebar/pull/151)):新 `editorExplorer` 设置(编辑器卡齿轮)——文件 tab 增加路径输入框头部 + 可开关的右侧停靠文件树(每 tab 记忆展开/宽度,左缘拖拽调宽 160~480px,全局文件名搜索走 host `fs.search` 路由预算封顶并跳过 `.git` / 符号链接目录);独立模式(默认)树点击 / 输入框 Enter **按路径新开**文件 tab,合并模式**原地切换**当前 tab;新会话默认 seed 空文件窗口(`Files`)替代 explorer tab,无路径窗口在独立模式为纯资源管理器、合并模式为带 chrome 的空文件窗口;树右键提供「在新 Tab 中打开」「在侧边打开」(split)
- 📁 **文件窗口与资源管理器二合一**([#151](https://github.com/omdsh-dev/DSH-better-sidebar/pull/151)):新 `editorExplorer` 设置(编辑器卡齿轮)——文件 tab 增加路径输入框头部 + 可开关的右侧停靠文件树(每 tab 记忆展开/宽度,左缘拖拽调宽 160~480px,全局文件名搜索走 host `fs.search` 路由(优先探测本机 fd / rg 原生引擎,DSH 自带 ripgrep 优先,缺失/失败时自动回退 JS 遍历,见 [#203](https://github.com/omdsh-dev/DSH-better-sidebar/issues/203);预算封顶并跳过 `.git` / `node_modules` 等噪声目录与符号链接目录));独立模式(默认)树点击 / 输入框 Enter **按路径新开**文件 tab,合并模式**原地切换**当前 tab;新会话默认 seed 空文件窗口(`Files`)替代 explorer tab,无路径窗口在独立模式为纯资源管理器、合并模式为带 chrome 的空文件窗口;树右键提供「在新 Tab 中打开」「在侧边打开」(split)
- 🎛️ **声明式设置 select 行**([#151](https://github.com/omdsh-dev/DSH-better-sidebar/pull/151)):设置项新增 `type: 'select'`(`options` 支持 value/title/desc/icon,`multi` 多选存数组);带图标的选项渲染大图标选项卡、收起态同样显示图标;`editorExplorer` 改为图标化下拉(合并 / 独立);能力清单新增 `settingSelect`
- 🔀 **与 dsh-web-ui 家族右侧面板互斥**([#181](https://github.com/omdsh-dev/DSH-better-sidebar/pull/181)):读取 `aionui-panel` 设置命名空间的提供方选择——当选择「使用 aionui-panel」时,整个 better-sidebar(右侧栏 / 底部面板 / 浮动入口 / 各类接管)不再挂载;选择 DSH-better-sidebar(或未安装 aionui)时正常。设置页保存后实时生效(settings-document 推送),无需刷新

Expand Down
70 changes: 70 additions & 0 deletions docs/ci-windows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Windows CI 维护指南(ci-windows lane + 搜索引擎门禁)

> 面向后续开发者:什么时候、为什么、怎么给 Windows 上的新功能加 CI 测试。
> 本文的 lane 结构落在 `.github/workflows/ci.yml` 的 `ci-windows` job;搜索引擎门禁由其中的两个步骤承载。

## 1. 这条 lane 是什么

`ci-windows`(**windows-latest** runner,Git Bash shell)与 ubuntu 的 `ci` job 同等强度:

1. `choco install fd ripgrep` —— 装**真引擎二进制**(`src/search-engines.ts` 的 probe 从 PATH 探测,与真实用户环境一致)
2. `pnpm install --frozen-lockfile`
3. `pnpm typecheck`
4. `pnpm lint`
5. `pnpm test:windows` —— **全量 vitest 套件**在真实 win32 Node 下跑(单 fork,理由见 ci.yml 内注释)
6. `pnpm build`(build script 已跨平台,**禁回退到 `rm -rf`**)
7. `pnpm check:consumer-types`
8. `node scripts/win-engine-check.cjs --assert` —— 用真 fd/rg 跑**字节级断言**:钉死的 `--path-separator /` 输出不得含 `\`/CR,中文文件名必须命中;任一失败 → 红灯

**Windows 全量是绿的**(2026-08-22 真机验证 773 passed / 3 skipped;2026-09-12 的 rc.2 基线为 127 文件 / 1356 用例)。全绿的达成靠两个平台修正,见 §3。这条 lane 不存在「Windows 例外测试不用跑」的说法——**新功能必须让全量保持绿**。

为什么曾有「只跑部分」的想法:最初 `pnpm test` 在 Windows 上有 4 个失败(3 个 smoke 的 CRLF 断言 + 1 个 pty-deps 的平台断言),都是测试自身不跨平台,不是产品 bug;修掉后全量直接绿,于是 lane 演进为全量门禁。**平台的坑应当修在测试里,而不是把测试从 Windows 门禁里排除**。

为什么引擎门禁并进 `ci-windows` 而不是单开一条 `search-windows`:单开要把「装依赖 + 跑全量」再付一遍(CI 分钟翻倍)却不增加覆盖;并进来则让**整个套件**都在真引擎在场的环境下跑,字节级断言也只是同一 runner 上多两个步骤。引擎相关 spec 是插桩式的(`tests/search-engines.spec.ts` 经 `setEngineHooks` 注入 probe),所以引擎在不在 PATH 上都不改变单测结论——ubuntu 的 `ci` job 因此继续覆盖「引擎缺失 → JS 遍历回退」这条路径。

## 2. 什么功能需要加 Windows 测试(红线清单)

后续新功能**只要涉及以下任一项,就必须保证 `ci-windows` lane 全绿**(新增测试或修正平台断言):

| 风险类别 | 具体坑(本仓库实测/已知) | 加测试的位置 |
|---|---|---|
| **子进程 spawn 外部命令** | PATH 解析、`.exe` 后缀、Windows 上 cmd/PowerShell 与 Git Bash 输出不同(rg#501:同一个 rg,`\` vs `/`) | spec 注入 runner + `--assert` 真二进制检查 |
| **路径字符串处理** | `\` vs `/` 分隔符、`.\` 前缀、CRLF 行尾、盘符/UNC、`MAX_PATH` 长度 | 把 Windows 形状的用例写进 spec(可给纯函数注入 `'\\'` 模拟,见 `normalizeEnginePaths` 测试) |
| **编码/Unicode 文件名** | UTF-8 管道 vs 系统代码页(GBK);中文/日文文件名 | `win-engine-check.cjs --assert` 的 Chinese 用例 |
| **依赖探测/环境布局** | 全局安装目录布局随 OS 不同(npm POSIX `lib/node_modules` vs Windows `%APPDATA%\npm\node_modules`) | `bundledRgCandidates` 的 win32 形状断言(已有);真实布局靠真机/CI 验证 |
| **文件系统语义** | symlink 权限(测试已有 `skipIf(!canSymlink)` 先例)、大小写不敏感、保留名(CON/NUL)、**git autocrlf 行尾**(smoke.spec.ts 先例:平台相关断言修进测试) | `skipIf` 平台门控或平台形状断言 |
| **原生模块/node-gyp** | node-pty 等需要 Windows 构建链(conpty);CI 的 `pnpm install` 已覆盖(含 approve-builds 配置) | 若新增原生依赖,确认 CI install 绿即可;修复命令的断言要按平台(pty-deps.spec.ts 先例) |

## 3. 历史平台修正(保持警觉的样板)

这些修正让 Windows 全量变绿,也是「平台坑修在测试里」的范例——**不要回退它们**(均已进 main):

- `tests/smoke.spec.ts`:scratch repo 钉 `git config core.autocrlf false`(Windows 默认 autocrlf=true 会把 checkout 文件转 CRLF,精确 LF 断言会挂;测试测的是 git 驱动逻辑,不是行尾转换)
- `tests/pty-deps.spec.ts`:`depsStatus()` 按**真实平台**生成修复命令(win32 → `powershell -Repair`,POSIX → `bash --repair`),断言必须按平台写,fixture 建双安装脚本
- `package.json` 的 `build`:`rm -rf lib` → `node -e "require('node:fs').rmSync('lib',{recursive:true,force:true})"`(Windows 无 `rm`)

## 4. 怎么加(标准动作)

```yaml
# ci.yml → ci-windows job → 在全量 Test 之后新增断言步骤:
- name: <你的断言说明>
run: pnpm vitest run tests/<你的spec>.spec.ts # 或 node scripts/<检查脚本> --assert
```

同时:

1. **新 spec 必须放进全量**(vitest 默认收集 `tests/*.spec.ts`,新文件自动进 `pnpm test:windows`,无需改 ci.yml);
2. **必须考虑 Linux 行为**:同一 spec 在 ubuntu lane 也会跑,平台差异用显式注入/`skipIf` 表达(参考 `tests/fs-search.spec.ts` 的 `canSymlink` 模式与 `normalizeEnginePaths` 的 separator 注入);
3. **检查脚本**(如 `scripts/win-engine-check.cjs`)**必须可幂等**:自建 scratch 目录(勿硬编码路径)、引擎缺失时跳过而非失败(CI 有 fd/rg,本地无则跳过)、`--assert` 退出码非零才是失败;
4. 新脚本放 `scripts/`,命名 `win-*.cjs`,README/设计文档顺带记录。

## 5. 排查红灯速查

- **全量跑挂**:先本地(任意 OS)`pnpm vitest run`,看是否平台无关逻辑问题;再在 Windows 真机 `pnpm vitest run` 复现
- **只有 Windows 挂**:八成是上面红线清单里的一项;用 `scripts/win-engine-check.cjs`(无 `--assert`)看引擎裸输出
- **choco 装引擎失败**:runner 镜像偶发;重跑 job 即可(保底可改 winget)
- **真机复验**:见 `docs/plans/2026-08-22-windows-verification.md`(完整链路手册)

## 6. 设计背景(为什么值得)

issue #203:大目录下 JS 遍历又慢又截断;搜索模块因此引入 fd/rg 原生引擎。**Windows 恰好是格式坑最多的平台**(分隔符/CRLF/布局),此前完全无 CI 覆盖。这批真机验证(2026-08-22)先以独立 `search-windows` lane 落地(PR #335),main 拿到自己的 `ci-windows` lane(PR #520)后,引擎门禁改为并入该 lane(PR #303),避免两套 Windows 重复安装与重复跑套件。更多实测数据见设计文档 §5「Windows 真机验收」。
2 changes: 1 addition & 1 deletion docs/external-plugin-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1029,7 +1029,7 @@ better-sidebar 的内置 tab 和 viewer 就是参考实现("吃狗粮"),
- **`src/client/SideCardSection.tsx`**:声明式设置页(注册表驱动清单 + 嵌套设置行 + 开关持久化)
- **`src/client/api.ts`**:`/sidebar` API 的封装(复制其 fetch 模式到你的插件)
- **`src/client/plugins-tabs.ts`** / **`plugins-viewers.ts`**:推荐插件目录(「添加插件」弹窗数据源;加一条数据即上架,`tests/plugin-list.spec.ts` 守护)
- **`src/client/FileTree.tsx`** / **`TreePanel.tsx`** / **`src/fs-search.ts`**:文件树 / 树面板 / host 文件名搜索(`fs.search`;`tests/fs-search.spec.ts`)
- **`src/client/FileTree.tsx`** / **`TreePanel.tsx`** / **`src/fs-search.ts`** / **`src/search-engines.ts`**:文件树 / 树面板 / host 文件名搜索(`fs.search`:优先探测本机 fd / rg 原生引擎(DSH 自带 ripgrep 优先),缺失/失败回退 JS walk;调试插桩 `DSH_SEARCH_DEBUG=1` 写 `$DSH_HOME/search-debug.log`(缺省 `~/.dsh`);测试 `tests/fs-search.spec.ts`、`tests/search-engines.spec.ts`)
- **`src/client/markdown-html.ts`** / **`MarkdownHtml.tsx`** / **`md-toc.tsx`**:markdown 内嵌 HTML 管线与目录大纲(注意 `md-toc.tsx` 头注释的「子组件读父 ref 为 null」时序陷阱)
- **`src/agent-opens.ts`** / **`/sidebar/ws/agent-opens`**:模型主动打开(`sidebar_open` 工具 + `agentOpenTools` 设置,默认关闭);文件夹窗口 = `meta.dir: true` 的 editor tab([设计文档](plans/2026-08-23-agent-open-tools-design.md))
- **`tests/service.spec.ts`** / **`tests/builtins.spec.ts`**:注册表生命周期 / 匹配算法 / dedupe / createTab / 启用态 gating;内置清单断言(7 tab + 6 viewer + 声明式元数据)
Expand Down
Loading