oh-my-openagent 的 CI 修复实践:fileURLToPath 修复 Windows 上 file URL 转换导致的 ENOENT 失败
oh-my-openagent 的 CI 修复实践fileURLToPath 修复 Windows 上 file URL 转换导致的 ENOENT 失败【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent本篇指南基于 oh-my-openagent 仓库中一次真实的 PR CI 修复记录PR #6521 Windows CI 修复完整还原“跨平台测试在 Windows 上因 file URL 路径转换错误而失败”的定位与修复过程。读完后你将掌握如何用fileURLToPath正确处理import.meta.url到原生路径的转换、如何理解该仓库“轻量级LIGHT tier证据驱动”的修复与自审流程以及一套可复用的红/绿/CI 三层验证方法。背景一个守护退役模型引用的仓库审计测试本次修复的直接原因是仓库中的审计测试 script/gpt-mini-reference-audit.test.ts 在 GitHub Actions 的test (windows-latest)作业上抛出了ENOENT错误。理解这个测试本身是理解整个修复的前提。该测试承担一个单一职责确保仓库中被 Git 跟踪的“shipped”发布物相关文件里不再出现已退役的 GPT Mini 模型引用。从源码看它包含两组测试const REPOSITORY_ROOT repositoryRootFromMetaUrl(import.meta.url) const OLD_MODEL_ID [gpt-5.4, mini].join(-) const OLD_DISPLAY_NAME [GPT 5.4, Mini].join( )第一组是“平台原生 file URL 解码”回归测试它构造一个路径中带空格的临时目录gpt mini audit把测试脚本的file://URL 传给repositoryRootFromMetaUrl断言返回值是带平台分隔符的原生路径。这个测试正是本次 CI 失败的“本地可复现”载体。第二组是真正的仓库级内容审计async function trackedFiles(): Promisestring[] { const process Bun.spawn([git, ls-files, -z], { cwd: REPOSITORY_ROOT, stdout: pipe, stderr: pipe, }) // ... return stdout.split(\0).filter((file) file.length 0 !file.startsWith(.omo/evidence/)) }几个值得注意的实现细节git ls-files -z用 NUL 分隔输出天然规避文件名含空格/特殊字符时的解析歧义。注意Bun.spawn的cwd参数就是REPOSITORY_ROOT——这正是失败发生的位置如果REPOSITORY_ROOT不是平台原生路径Bun.spawn会在扫描开始之前就直接拒绝该工作目录。.omo/evidence/前缀过滤证据目录本身可能包含历史输出里的模型名因此审计时排除保证审计对象是“发布面”而非“开发留痕”。20 秒超时是断路器不是等待expect(matches).toEqual([])所在的测试显式传入20_000毫秒超时。据 SUMMARY.md 记录该仓库级扫描对约 900 个文件的真实耗时在 4.28 秒曾因一次偶发在 5.00 秒触发 Bun 默认 5 秒超时。把超时提高到 20 秒是为了让“全仓扫描”这一确定性断言不再受默认超时干扰而非任何形式的 sleep、轮询或重试。script/AGENTS.md中的目录约定也印证了这类仓库级元审计的归属位置script/承载构建/发布自动化且“repo-wide meta-audits also live here and run in rootbun test”即该审计测试由根目录bun test直接执行与 package.json 中test: bun test --timeout 20000的全局 20 秒默认超时相呼应。RED一个只在 Windows 上暴露的跨平台失败修复的证据链以 RED.txt 开头记录了 GitHub Actions 失败运行run30627980408作业test (windows-latest)的原始现象error: ENOENT: no such file or directory at trackedFiles (script/gpt-mini-reference-audit.test.ts:30:23) (fail) GPT Mini reference audit tracked shipped files no longer reference the retired GPT Mini model 12605 pass / 22 skip / 1 fail关键的判别证据是操作系统三分法对照test (macos-latest)successtest (ubuntu-latest)successtest (windows-latest)failuremacOS 与 Ubuntu 双绿直接排除了“仓库中真的存在退役模型引用”这种内容性根因——问题一定出在路径边界上。RED 记录进一步做了本地回归复现在 macOS 上即可触发Expected: /var/folders/.../T/gpt mini audit/ Received: /var/folders/.../T/gpt%20mini%20audit/至此失败被归约为两个具体的 URL 路径缺陷均源于旧实现直接用new URL(../, import.meta.url).pathname取路径百分号编码不还原file://URL 的 pathname 中空格是%20.pathname原样返回gpt%20mini%20audit路径不再存在Windows 盘符形态非法在 Windows 上.pathname产生形如/D:/repo/...的前导斜杠 盘符混合路径Bun.spawn将其作为cwd时无法解析直接ENOENT——这就是 Windows 独有失败的真正机理测试在“扫描文件之前”就已失败。修复用 fileURLToPath 收口 URL 到原生路径的边界最终修复只改动了 script/gpt-mini-reference-audit.test.ts 一个文件纯代码行 48 行核心是这一行辅助函数function repositoryRootFromMetaUrl(metaUrl: string): string { return fileURLToPath(new URL(../, metaUrl)) }fileURLToPath来自node:url专门负责把file://URL 解码为平台原生路径对 macOS/Linux还原百分号编码%20变回空格并保证目录尾随分隔符与平台一致对 Windows把file:///D:/...规范为D:\...原生盘符路径消除.pathname留下的前导斜杠。这与 SELF-REVIEW.md 中“fileURLToPathfixes both percent decoding and Windows drive-letter paths”同时修复百分号解码与 Windows 盘符路径的表述完全一致。从代码结构看REPOSITORY_ROOT常量在模块顶层由repositoryRootFromMetaUrl(import.meta.url)计算一次随后被trackedFiles()的cwd与内容审计的${REPOSITORY_ROOT}${file}拼接共同消费——修复点位于“file URL 变成原生路径”的单一边界处符合 SELF-REVIEW 中“boundary purity”边界纯粹性的自审条目URL 在边界上即被转换为原生路径下游不再感知 URL 形态。值得强调的是这次修复没有削弱审计本身内容审计未被抑制、跳过、收窄或加白名单git ls-files的取文件方式、退役模型匹配规则gpt-5.4-mini小写包含 GPT 5.4 Mini显示名精确包含、以及“匹配列表必须为空”的断言全部保持不变。修复只动路径转换不动判定逻辑。GREEN本地分层验证与 CI 权威验收修复后的验证是分层递进的全部记录在 GREEN.txt 中且统一使用与 CI 一致的 Bun 1.3.12 版本通过mise x bun1.3.12 --前缀锁定第一层聚焦测试focused testmise x bun1.3.12 -- bun --cwd task-worktree test script/gpt-mini-reference-audit.test.ts (pass) GPT Mini reference audit repository root decodes a platform-native file URL (pass) GPT Mini reference audit tracked shipped files no longer reference the retired GPT Mini model 2 pass / 0 fail / 2 expect() calls, exit 0RED 阶段失败的第一条URL 解码回归与第二条仓库审计现在全部通过。第二层类型诊断与风格门禁mise x bun1.3.12 -- bun run --cwd task-worktree typecheck:script $ tsgo --noEmit -p script/tsconfig.json exit 0TypeScript no-excuse 审计同时报告No violations in 1 file(s)。这里有一个环境细节SELF-REVIEW 记录注册 LSP 的诊断无法覆盖兄弟 worktreesibling worktree因此以脚本tsgo作为替代类型诊断来源——这正是 package.json 中typecheck:script的定义。第三层全量仓库套件mise x bun1.3.12 -- bun --cwd task-worktree test 12626 pass / 3 skip / 0 fail / 1 snapshot / 76049 expect() calls Ran 12629 tests across 1665 files in 143.97s, exit 012,626 个测试零失败证明修复未对仓库相邻的脚本与测试行为产生回归。权威验收GitHub Actions本地全绿之后最终由 GitHub Actions 作为“权威 Windows 验收面”完成闭环记录于 CI-GREEN.txt精确 head SHAd9ef47e90dbb5f0b7698d4fed12c6acce0e40219的运行run30632258545以CI_EXIT0完成其中决定性的一条是——此前在 run30627980408中失败的test (windows-latest)全量套件作业转为 success同时 ubuntu/macos/windows 三平台的 test、typecheck、codex-compatibility、senpi-compatibility 各作业及 lazycodex-published-smoke、build、block-master-pr 全部成功。SELF-REVIEW 对此的边界声明是“GitHub Actions is the authoritative Windows acceptance surface”并明确没有在本地另建 Windows VM 去伪造这份证据。轻量级自审LIGHT tier 的判定与八条写后检查本次修复被定级为LIGHT tier其定义见 SELF-REVIEW.md仅一个仓库审计 harness 修复不涉及运行时、安全、会话、持久化、并发、缓存、集成或领域边界的任何变更。配套的范围约束改动范围限于script/gpt-mini-reference-audit.test.ts加证据文件内容审计未被抑制、跳过、收窄或加白名单git ls-files、退役模型匹配规则、空匹配断言均未改动无any、断言、忽略或告警抑制escape hatches 为零无冗余检查或 catch一次性辅助函数repositoryRootFromMetaUrl同时服务生产 setup 与回归测试仅接受一个参数无参数膨胀测试的回归效力把实现回退到.pathname时回归测试必然失败。这条“回退即红”的性质说明回归测试真正钉住了缺陷机理而非仅仅固化了修复后的输出。合并后的集成与清理留痕修复提交后dev分支先行推进合并时产生了集成工作过程与结果记录在 CLEANUP.txt 和 SUMMARY.mdorigin/dev已包含原生路径修复commitf4b1b0f93通过 merge commit 集成未改写 PR 历史冲突仅出现在生成的 Senpi bundle 文件上使用固定版本 Bun 1.3.12 的权威生成器重新构建build-extension.mjs --check报告 current冲突标记检测器与 unmerged index 均为空Senpi 门禁 511 个测试零失败1,431 次 expect() 调用81 个文件worktree 卫生早期 worktree 因未锁定版本的 postinstall 重建了无关 bundle 被移除重建后以--ignore-scripts安装依赖、用固定 Bun 构建最终 worktree 基于 PR headf75375603创建待 PR 合并后移除。SUMMARY.md 的 “Senpi surface scope” 一节还划定了验证边界本次修复只改script/不触及 PR #6521 的 Senpi 运行时本体原始 PR 的 xterm.js/Chromium TUI 证据在其任务 worktree 中重复该 TUI 并不会真正行使 Windows 路径转换这条路径因此不重复执行——验证手段的选择以“是否真正触达目标缺陷”为准。可迁移的教训从这次 PR #6521 的 CI 修复中可以提炼出几条适用于任何跨平台 Node/Bun 测试套件的工程经验永远不要直接消费URL.pathname作为文件系统路径。import.meta.url到原生路径的转换必须经由fileURLToPath它一次性解决百分号解码与 Windows 盘符两个问题Bun.spawn/子进程的cwd是路径边界的高危消费点非法路径会以ENOENT在进程启动前失败错误信息指向“文件不存在”而真实原因是路径形态——此时先做“操作系统三分法对照”多平台同一测试一红多绿即可把问题定位到平台边界而非内容缺陷用 NUL 分隔-z处理文件名git ls-files -z 按\0切分是对含空格文件名的最小正确做法该测试本身就用“带空格的临时目录”回归测试钉死了这一点显式超时应当声明意图test(..., 20_000)在此是“900 文件扫描的断路器”与默认 20 秒全局超时bun test --timeout 20000对齐避免了偶发边界抖动造成的假红证据分层、权威面唯一本地聚焦测试 → 类型/风格门禁 → 全量套件 → CI 平台验收四层递进且每层留档RED/GREEN/CI-GREEN/CLEANUP 四份证据文件对 Windows 缺陷CI 运行是唯一权威验收面不本地伪造修复不改判定路径转换修复与内容审计判定完全正交审计规则一行未动这是“轻量级LIGHT”定级能够成立、也易于评审的核心原因。完整证据链可按以下仓库内路径继续深入SELF-REVIEW.md定级与自审、SUMMARY.md测试了什么、观察到什么、为何足够、RED.txt 与 GREEN.txt本地红绿记录、CI-GREEN.txtCI 验收、CLEANUP.txt清理留痕以及被测对象 script/gpt-mini-reference-audit.test.ts 与 script/AGENTS.md 中的script/目录约定。【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考