Skip to content

check-i18n-bundles resolves its population CWD-relatively and dies with an uncaught ENOENT stack from any cwd but the repo root #11647

Description

@os-steve

Measured while fixing #11394. No gate is red — CI invokes this gate from the repo root. Filed unassigned, no pm:queue.

The shape

#10907 anchored check-i18n-coverage.mjs to a root derived from import.meta.url, behind one at() seam. Its sibling scripts/check-i18n-bundles.mjswas never given the same treatment: it contains no import.meta.url, no REPO_ROOT, and enumerates its population with two bare relative literals:

constconfigs=findExtractConfigs('packages','packages')

findExtractConfigs (scripts/i18n-bundle-surface.mjs:128) readdirSyncs that literal, so from any cwd but the repo root the gate does not merely mis-scan — it dies with an uncaught exception and a node stack:

$ cd /tmp && node .../scripts/check-i18n-bundles.mjs
node:fs:1590
Error: ENOENT: no such file or directory, scandir 'packages'
at readdirSync (node:fs:1590:26)
at findExtractConfigs (file:///.../scripts/i18n-bundle-surface.mjs:128:19)
at file:///.../scripts/check-i18n-bundles.mjs:748:17

Pre-existing, and not introduced by #11394

Verified on both trees. At origin/main3637731e2, off-root, the same stack appears — there preceded by the (now removed) probe-deferral line:

check-i18n-bundles: could not read packages/cli/package.json (ENOENT ...) — build prerequisite not pre-checked
Error: ENOENT: no such file or directory, scandir 'packages'

#11394 anchored the shared build probe and this gate's existence check on its answer; it deliberately did not widen into the population, which is this card.

Why it is worth recording

Two reasons, and the second is the load-bearing one.

  1. It is the last unanchored half of a pair whose other half is already fixed. check-i18n-coverage and check-i18n-bundles are described throughout their own headers as siblings running one step apart in lint.yml; one is cwd-independent and the other cannot start.

  2. The failure mode is the one this gate family has repeatedly been fixed for.check-i18n-bundles.mjs carries an elaborate prerequisite apparatus (check-i18n-bundles 在工作区未构建时把「CLI 没 build」报成 9 个包各自的 bundle 问题 #5217, check-i18n-bundles reports a stale-dist prerequisite failure as a content verdict ("1 bundle problem(s)") #7681, and its reportPrerequisiteNotMet printer, which states in words that nothing was checked) precisely so an environment fact is never reported as a content verdict or as a bare stack. An uncaught scandir 'packages' bypasses all of it — it is exactly the shape check-i18n-coverage:example 自身依赖未构建时仍抛裸异常 + node 栈(诊断准确,但不是结论) #6033 removed from the coverage gate's per-config loop, still live one file over, at the population level.

Note the direction is not#10907's silent-green: an uncaught throw is loud. The cost is a wrong first diagnosis (a node stack naming node:fs), not a false pass.

Suggested fix

The same shape both siblings now use: derive a root from import.meta.url, keep the repo-relative spellings as vocabulary, and put one at()/atRepoRoot seam between them. scripts/cli-build-prerequisite.mjs already exports atRepoRoot after #11394, and this gate already imports it — so the population change is the remaining half. #10907's anti-#4690 note applies too: an empty population must be refused, not reported OK.

Refs

#11394 (where this was measured; that card anchored the shared probe, not this gate's population)
#10907 (the same fix applied to the sibling gate)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions