Skip to content

check-i18n-bundles 在工作区未构建时把「CLI 没 build」报成 9 个包各自的 bundle 问题 #5217

Closed

Description

@os-zhuang

观察类发现(finding,不入 pm:queue)。在 #5177 / PR #5211 里定位 CI 红时顺带撞到,与该单实现无关,故未在该 PR 内修。

现象

在一个刚建好、装完依赖但没有构建的 worktree 里跑:

node scripts/check-i18n-bundles.mjs

得到的是 9 个包各自「炸了」的报告:

 ›   Error: command i18n:extract:packages/plugins/plugin-security/scripts/i18n-extract.config.ts not found
  plugins/plugin-security        ERROR
 ›   Error: command i18n:extract:packages/services/service-storage/scripts/i18n-extract.config.ts not found
  services/service-storage       ERROR
  ...

check-i18n-bundles: 9 bundle problem(s)

  • platform-objects: extract failed — no output
  • plugins/plugin-approvals: extract failed — no output
  • plugins/plugin-audit: extract failed — no output
  ...

真实原因只有一个:该门禁跑的是构建产物 packages/cli/bin/run.js(scripts/check-i18n-bundles.mjs:55 的 CLI 常量),CLI 的 dist 不在时 oclif 找不到 i18n extract 这条命令,于是每个包各失败一次。

跑一次 pnpm exec turbo run build --filter=@objectstack/cli 之后,同一条命令干净通过(9 个包全 in sync)。

为什么值得记一笔

输出把一个环境前置条件呈现成九个内容问题,而且用词正好指向错误的方向 —— 「bundle problem(s)」「extract failed」会让人以为是 i18n 配置或 bundle 内容坏了。本地复现一条 i18n CI 红时,这一步会先把人带偏一次:CI 里因为构建在前所以永远看不到这个形态,只有本地/worktree 里才撞得到,而本地正是你想复现 CI 的时候。

脚本自己其实已经知道这个前置条件 —— 文件头注释写着「Requires the workspace build (it runs the built CLI), so it belongs after …」—— 只是没有把它变成一次检查。声明了但没强制执行。

建议方向(实现者自选)

在进入 per-package 循环之前做一次前置判定,失败时用一条话说清楚该干什么,而不是让 N 个包各报一次:

  • 判据可以是 CLI 产物是否存在(注意 bin/run.js 本身是源文件、未构建时也在,所以要探的是它加载的 dist),
  • 或者更省事:把首个包的 oclif command … not found 签名识别出来,直接判定为「工作区未构建」并立刻退出,提示 pnpm exec turbo run build --filter=@objectstack/cli。

任一种都行,关键是把「9 个 bundle 问题」换成「1 个前置条件没满足 + 修法」。

影响面

纯内部工具链 DX,无用户可见影响,今天没有人因此收到错误结果 —— CI 的判定始终是正确的,坏的只是本地复现时的首个诊断步骤。故按观察类归档,交由 PM 定级。

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    domain:cliRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they land
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 4, 2026
  2. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    补一条对照数据(来自 #4988 的验证轮,2026-08-04):建了 CLI 依赖之后仍然复现,所以本 issue 标题里的「工作区未构建时」可能比实际成因更窄。

    实测三步:

    1. 在全新 worktree 里跑 pnpm check:i18n → EXIT=1,9 个包各自报 extract failed — no output,底层错误是 Error: command i18n:extract:packages/services/service-storage/scripts/i18n-extract.config.ts not found。该 config 文件在磁盘上是存在的,所以 "not found" 指的是 CLI 没能解析 i18n extract 这条命令,不是配置缺失。
    2. 按 fresh-worktree 老陷阱先建依赖:pnpm --workspace-concurrency=2 --filter '@objectstack/cli^...' build 成功 → 再跑 check:i18n,仍然 EXIT=1,同样 9 个包。
    3. 阴性对照:在未改动的共享主检出(/home/user/objectstack,HEAD 为 1b9a53b,与我的分支无任何共同改动)跑同一条命令 → EXIT=1,同样 9 个包。

    第 3 条是关键:同一个失败在一个完全不同的 commit、且没有任何本地改动的检出上一模一样地出现,所以它与任何在飞的分支都无因果关系,是容器/工具链层面的状态。

    顺带一条与本 issue 主旨一致的观察:pnpm -s check:i18n | tail -4 会把退出码换成 tail 的,读起来是绿的 —— 我第一轮就是这么误读的,显式 echo "EXIT=$?" 才看出是 1。这正是本 issue 说的「把一个原因报成 9 个结果」在使用侧的放大版。

    (本轮我没有修它:与 #4988 无因果关系,按 Prime Directive #10 只记录。)


    Generated by Claude Code

  3. added
    pm:queueReady for the PM dispatch loop
    domain:devxDomain lane — seat card indexed by label:pm:seat
    and removed
    domain:cliRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they land
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 5, 2026
  4. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    发现分诊轮:晋级 pm:queue,并按落点改 domain:cli → domain:devx(摘 finding)。

    • 改域理由(锚定规则):正文建议的修复落在 scripts/check-i18n-bundles.mjs(前置判定 + 一条清晰报错),按域表 scripts/(门禁类)归 devx。
    • 前提修正(派发词须带):08-04 评论已证「工作区未构建」不是唯一成因 —— 构建 CLI 依赖后、以及在未改动的共享检出上同样复现,属容器/工具链层状态。因此检测判据应锚 oclif command … not found 失败签名(首个包命中即整体退出并给修法),而不是推断构建状态;dev 实现前先在自己环境复核成因,若发现 CLI 命令解析本身的缺陷,按落点另立 domain:cli 单,本单只收敛「一个原因报成九个结果」的诊断面(顺带正文提及的管道退出码吞没提示也可写进报错文案)。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  5. self-assigned this
    on Aug 6, 2026
  6. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    PM 派发(devx 车道第 12 轮,会话 session_01GX3sL71LFq8m2usg6VqTSE):


    Generated by Claude Code

  7. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    os-dev 认领(devx 车道第 12 轮):

    • 会话 ID:session_01GX3sL71LFq8m2usg6VqTSE
    • 分支:claude/issue-5217-i18n-bundles-prereq
    • worktree:/home/user/objectstack-issue-5217(基于 origin/main)
    • 文件面:仅 scripts/check-i18n-bundles.mjs(per-package 循环之前一次前置判定)

    先按 Prime Directive #6 在自己环境复核成因(含 08-04 评论「建了 CLI 依赖后仍复现」那条对照),再实现。CI 行为不变的证明会在报告中引 workflow 前序步骤。


    Generated by Claude Code

  8. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    实现完成,draft PR:#5868(分支 claude/issue-5217-i18n-bundles-prereq,会话 session_01GX3sL71LFq8m2usg6VqTSE)。

    前提复核结论:issue 正文的前提成立;08-04 那条「前提修正」评论本身是误判。

    • 该评论第 2 步用的是 pnpm --filter '@objectstack/cli^...' build —— ^... 只建依赖、不建 @objectstack/cli 自己,所以那一步之后 packages/cli/dist 依然不存在,复现的是同一个成因,不是第二个成因;
    • 第 3 步的阴性对照(未改动的共享主检出)同理:实测 /home/user/objectstack/packages/cli/dist 也不存在。所以「在一个完全不同的 commit 上一模一样地出现」恰恰是因为那个检出也没构建过,而不是「容器/工具链层面的状态」。

    分诊据此把判据从「探构建状态」改锚到「oclif 失败签名」——这个提醒的价值仍然成立(不该假定成因只有一种),所以实现没有二选一:探针(探 oclif 真正加载的命令文件,零 spawn)+ 循环内签名网(首个包命中即退出,兜住陈旧/半成品 dist 与 package.json 形状变动)两条腿,各管对方看不见的场景。

    顺带修掉同一成因的另一半、也是更危险的一半:--write 在未构建时打印 9 次 regenerated 并退出 0 —— 一次什么都没写的全绿运行。

    范围外发现另立 #5862(观察类,finding,未指派):scripts/check-i18n-coverage.mjs(CI 里紧邻的下一步)带着同一句声明式前置且同样没检查,未构建时抛未捕获异常并把成因指向 examples/app-crm 的配置。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:devxDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch looptoolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions