Skip to content

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

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. 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

  2. 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

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

    @os-zhuang
    ContributorAuthor

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


    Generated by Claude Code

  5. 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

  6. 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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions