Skip to content

fix(scripts): check-i18n-coverage 的构建前置改为真检查,不再让第一个 example 顶罪 - #6032

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5862-i18n-coverage-precondition
Aug 6, 2026
Merged

fix(scripts): check-i18n-coverage 的构建前置改为真检查,不再让第一个 example 顶罪#6032
os-zhuang merged 1 commit into
mainfrom
claude/issue-5862-i18n-coverage-precondition

Conversation

@hotlong

@hotlong hotlong commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #5862

问题

scripts/check-i18n-coverage.mjs#5217 修的门禁是 lint.yml 里紧邻的两步,并且带着同一句只写在注释里的前置声明("Requires the workspace build (it runs the built CLI)")。在装完依赖但未构建的 worktree 里实跑,它给出的是一个未捕获异常加一段 node 栈:

Error: os lint produced no output for examples/app-crm/objectstack.config.ts
    at countI18nIssues (…/check-i18n-coverage.mjs:101:28)

成因只有一个,而且不在那个文件里:门禁跑的是构建产物 packages/cli/bin/run.js,oclif 从 dist/commands 解析 os lint,CLI 没 build 时什么也不输出。examples/app-crm/objectstack.config.ts 只是恰好第一个被处理的配置,替整个环境顶了罪 —— 排障时的第一手读数在说谎。

CI 永远命中不到(Build workspace packages 排在这两步之前),这也正是它能活下来的原因:唯一会遇到它的人,是在本地复现红色 i18n CI 的人,恰好是错误首诊代价最高的时刻。

改动(与 #5217 落地形状同构)

  1. 进入 per-config 循环之前一次前置判定 —— 探测 oclif 真正要加载的命令文件(从 CLI 自己的 oclif.commands.target 推导,而不是写死),失败时给一条前置结论 + 一句修法,并明确声明什么都没测量、baseline 一字未动(含 --update)。
  2. 循环内保留签名安全网 —— 覆盖前置探测结构上看不见的情形(stale / partial dist:命令文件在、但构建产物已答不上这个 id)。首个 config 即中止,并且消息里点名该 config 不是成因,正好把原缺陷反过来。
  3. --self-test —— 语料逐字录自本仓未构建实跑,并按仓内惯例接进 package.jsoncheck:i18n-coverage(--self-test && 门禁,与 check:i18n / check:nul-bytes / check:doc-authoring 一致)。不接线的话自测永远不跑,本身就是这仓反复付过学费的 phantom check。

为什么选「提取共享」而不是「照抄两份」

两个纯函数提取到新的 scripts/cli-build-prerequisite.mjs,check-i18n-bundles.mjs 改为 import。理由:

  • 它们编码的知识不显然,而且只会被改一边。核心是 oclif 会把那句 command … not found 硬换行成两三行(本仓实测,长路径会拆到第三行),逐行正则一条也匹配不到 —— 这正是「读起来正确」的那个实现。两份拷贝就是两套事实来源(check:durability-log-level 结构性看不见「读接缝把故障答成空值」这一类 —— #4825 / #5108 全家都在闸门盲区里 #5186 先例)。
  • 接线成本很低:check-i18n-bundles.mjs 只少了两个函数体、多了一条 import,其余行为逐字未变(见下方实测的前置消息比对)。
  • 反向验证给了这次提取一个直接的证据:把共享实现临时换成朴素的逐行版,两个门禁的自测同时变红(coverage 2 条、bundles 4 条)。一处写错、两套语料一起报警 —— 这是两份拷贝拿不到的。

申报文件面的扩面说明(认领时已预期):scripts/check-i18n-bundles.mjs(改 import)与新增的 scripts/cli-build-prerequisite.mjs 属于共享路线的必要扩面;package.json 一行是 --self-test 的接线,否则新增自测不进 CI。⛔ 未碰 lint.yml

一处诚实的修正:修法不能只说一半

按 issue 建议的 pnpm exec turbo run build --filter=@objectstack/cli 实跑后,前置确实清掉了(app-crm 正常计数),但墙只是往后挪了一格 —— 这个门禁不同于隔壁的 bundles 门禁,它还要 lint examples/*,而 example 配置会按名字 import 其它 workspace 包:

Error: os lint failed for examples/app-showcase/objectstack.config.ts:
  Cannot find module '…/@objectstack/connector-mcp/dist/index.mjs'

所以修法按两个宽度给:CLI_BUILD_FIX 是清掉「本次实际检查了什么」的最小命令(探测只测了 CLI,就不许替整棵树背书);紧跟一行说明从未构建过的树要 pnpm build。少这一行,新消息就变成「同一个缺陷、晚一步发生」。

实测

环境 命令 结果
未构建(改动前,origin/main 代码) node scripts/check-i18n-coverage.mjs 未捕获异常 + node 栈,点名 app-crm(EXIT=1)
未构建(改动后) 同上 一条 PREREQUISITE NOT MET + 修法,不点名任何 config(EXIT=1)
未构建 + --update … --update 同上;baseline sha 前后一致,未写入
partial dist(命令文件在、内容废掉) 同上 安全网触发,并写明 app-crm NOT at fault(EXIT=1)
已构建(full pnpm build) pnpm check:i18n-coverage OK (12 config(s), 660 baselined untranslated string(s), none new).(EXIT=0)
已构建 pnpm check:i18n(#5217 门禁) OK (9 package(s) …)(EXIT=0)
未构建 pnpm check:i18n 前置消息 与提取前逐字相同
任意 --self-test ×2 均通过;朴素逐行实现下两边同时变红

pnpm lint(CI ESLint job 首步)、pnpm check:nul-bytes 均为 EXIT=0。

Changeset

scripts-only,根包 private,不发布任何东西 —— 走 skip-changeset 标签(照 #5986 先例),不写 changeset。


Generated by Claude Code

`scripts/check-i18n-coverage.mjs` 与 #5217 修的门禁是 lint.yml 里紧邻的两步,
带着同一句只写在注释里的前置("Requires the workspace build")。在装完依赖但
未构建的 worktree 里,它抛一个未捕获异常 + node 栈:

    Error: os lint produced no output for examples/app-crm/objectstack.config.ts

成因只有一个,而且不在那个文件里:门禁跑的是构建产物,oclif 从 dist/commands
解析 `os lint`,CLI 没 build 时什么也不输出 —— app-crm 只是恰好第一个被处理。
诊断把读者送进一个完全无辜的示例配置。

改动与 #5217 落地形状同构:
- 进入 per-config 循环之前一次前置判定(探测 oclif 真正要加载的命令文件),
  失败时一条前置结论 + 一句修法,并声明「什么都没测量」;
- 循环内保留签名安全网,覆盖探测看不见的 stale/partial dist,首个 config 即
  中止,并明说该 config 不是成因;
- 两个纯分类器(oclifCommandFileFor / looksLikeMissingCliCommand)提取到
  scripts/cli-build-prerequisite.mjs 共享,check-i18n-bundles.mjs 改为 import
  —— 两份拷贝就是两套事实来源(#5186),而 oclif 硬换行这类知识只会被改一边;
- 补 --self-test(语料逐字录自本仓未构建实跑),并按仓内惯例接进
  package.json 的 check:i18n-coverage,否则自测永远不跑。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 6, 2026 3:15pm

Request Review

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 31117146174 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 17 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Copy link
Copy Markdown
Contributor

队列管家原样重投(Routine trig_012yeNVMdFwXET5zvYQtUKXA,锚点 #5810

⛔ 未改代码、未切 ready/draft、未重跑(rerun 复用原合并 ref,对此类红无意义)。


Generated by Claude Code

Merged via the queue into main with commit d4fbfc6 Aug 6, 2026
29 of 30 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5862-i18n-coverage-precondition branch August 6, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants