fix(scripts): digest 的回落读取不再刷一屏 git fatal:,改说一句可读事实 (#6175) - #6335
Merged
Conversation
`readAt()` 在 `to` 端点读不到 changeset 时会回落到「添加它的那个 commit」, 这条路径本身是对的 —— 范围内的 release commit 消费掉了当时挂着的全部 changeset,回落正是把它们留在发布记录里的机制。但 `execFileSync` 默认把子进程 stderr 直接接到我们自己的 stderr 上,于是第一次 `git show` 失败时 git 的 `fatal: path … does not exist in …` 在 `catch` 跑起来之前就已经打出去了: 真实 pin bump `f995a452d2ca..7dfbeb704e1e`(#6159 / PR #6173)上是九行 `fatal:` 之后接一行「全部成功」。 这正是本文件已经写死的那条原则的镜像面。#4731 写的是「a degraded list and a complete one must never look alike」,这里发生的是**一个完整的列表长得像一次 失败**,而且它指向错误的处置:下一个读者的第一反应是「digest 挂了,改用 `--no-changeset` 手写」—— 那条路才会真的丢掉 9 条 releasing changeset。 - B:第一次尝试改为**捕获** stderr(`stdio: ['ignore','pipe','pipe']`)而不是 继承;两次都失败时把两次的诊断按尝试点名重新打出来并继续抛错 —— 安静是 「回落成功」挣来的,永不外借给真正的失败。 - C:发生过回落时,在 accounting 行旁打一句事实(`absentAtTo`),0 次回落时 不打 —— 不制造新噪声。这句落在 stderr 而不是 changeset 正文:正文记录这个 区间发布了什么,而「工具怎么读到这个文件」不是发布事实。 self-test 新增 8 条断言独立成组(#4731/#6099 既有 fixture 的精确计数一字未动), 其中三条通过**子进程**跑真实 CLI —— git 的 `fatal:` 落在真实 stderr 上, 进程内断言看不见它。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
hotlong
marked this pull request as ready for review
August 7, 2026 14:32
This was referenced Aug 7, 2026
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6175
readAt()的回落路径本身是对的 —— 坏的是它把一次完整的结果打成了一次失败的样子。本 PR 落地分诊 07:57Z 同意的 B + C 组合:B 消掉误导,C 把「发生了回落」从噪声变成一句可读的事实。前提复核(#6289 合并后)
origin/main@3264516切出,已含 PR fix(scripts): digest 的破坏性判据扩到「声明 major 或作者正文标注」(#6099) #6289(7330c1d,判据扩到「声明 major 或作者正文标注」)。readAt()记在 :186–192;fix(scripts): digest 的破坏性判据扩到「声明 major 或作者正文标注」(#6099) #6289 之后行号漂移到 :313–319,函数体逐字一致(try { git show ${to}:${path} } catch { git show ${sha}:${path} })。按内容定位,前提成立。execFileSync未指定stdio时子进程 stderr 直接接到父进程的 stderr,所以第一次git show的fatal:在catch跑起来之前就已经写出去了。一处派发单事实更正
派发单称重放区间
f5bc4c78be76...f995a452d2ca的to是 release commit。实测不是:f995a452d2caci(scripts): type-check scripts/ via a standalone tsconfig.scripts.json (#3494)7dfbeb704e1echore: release packages (#3248)← 真正的 release commit7dfbeb704e1e正是 issue 正文自己观察到的那次 pin bump 的to。所以主重放改用 issue 自己的区间f995a452d2ca..7dfbeb704e1e(会回落),并把派发单给的f5bc4c78be76..f995a452d2ca(不回落)留作零回落对照组 —— 比原计划多出一个方向的证据。实现(仅
scripts/objectui-changeset-digest.mjs)B — 第一次尝试静默,但只静默「成功的回落」
git()增加captureStderr选项(默认关):打开时用stdio: ['ignore', 'pipe', 'pipe']把子进程 stderr 收进抛出的 error,而不是接到我们自己的 stderr。默认关是有意的 —— 失败即失败的调用(例如main()里那次git cat-file -e端点探测)应当继续把 git 的诊断直接打出来。readAt()因此变成:第一次尝试捕获 stderr;失败则回落到「添加它的那个 commit」,同样捕获;两次都失败时把两次的诊断按尝试点名重新打出来再继续抛错:安静是「回落成功」挣来的,永不外借给真正的失败 —— 彻底失败比改前更响(多了一行点名摘要和两段按尝试标注的诊断,退出码不变)。
C — 回落发生时,在 accounting 行旁说一句事实
classifyRange()统计absentAtTo(实际发生回落的 changeset 数,entries已按 path 去重,故一条只记一次),经buildDigest()透传到 CLI,在 accounting 行之后打一句、且仅在大于 0 时打:两个刻意的落点选择:
fatal:误导的那个读者。to的 subject 判断它是不是 release commit —— 那正是 bump-objectui.sh 的 pin changeset 只收 feat|fix 且静默截断到 40 条 —— 破坏性 refactor! 进不了前端发布记录 #4731 从这个文件里清掉的 subject 推断。只陈述实测事实(N 条在to处不存在、各自从添加它的 commit 读到),再用一句通用机制解释。真实区间重放(改前 / 改后)
主重放
f995a452d2ca..7dfbeb704e1e(to= release commit,9 次回落)539eb31c7b0674b9…539eb31c7b0674b9…逐字节不变(cmpexit 0)fatal:行数→ 9 releasing changeset(s), 0 breaking (0 declared major, 0 annotated), 0 release-nothing, 20 commit(s) without a changeset→ 9 of 9 changeset(s) … the account above is complete.)对照组
f5bc4c78be76..f995a452d2ca(0 次回落):stdout 和 stderr 双流逐字节不变(cmp两次 exit 0)—— 没有回落就没有新行,C 不制造新噪声。反向验证(先申报,后执行)
两个变异体都先申报预期方向再跑,实测逐条对上:
M1 —— 撤掉 B(第一次尝试恢复继承 stderr),保留全部新断言
#6175 the fallback no longer leaks git fatal: onto stderr转红;其余 7 条 finding(bump-objectui): pin 范围跨过 objectui 的 release commit 时,digest 会先刷一屏fatal:再报成功 —— 一个完整的结果长得像一次失败 #6175 断言应保持绿(零回落那条的区间根本不失败;两条彻底失败断言里,第一次的fatal:泄漏到同一条流、第二次仍被捕获重发,fatal:计数与双 sha 断言都还成立)。fatal:。申报命中。M2 —— 删掉 C 的输出,其余不动
#6175 the fallback SAYS SO — once, with the real count转红。并且明确申报另外两条不会转红,理由是结构性的,不是运气:a range that crossed no release gains NO new line是一条否定式断言,把 C 整个删掉恰好平凡地满足它;the fallback is COUNTED读的是返回值上的absentAtTo字段而不是打印出来的那行。这一条与派发单模板不同,如实记录:派发单写的是「删掉 C ⇒ (a/c 相关断言转红」,实测只有 (a) 侧那条肯定式断言会红。一条否定式断言在构造上无法察觉「它所禁止的东西被删掉了」—— 这正是那条 CLI 子进程肯定式断言必须单独存在的原因,两条并不冗余。
self-test
--self-test从 #6289 的 37 条 → 45 条,新增 8 条独立成组,⛔ 未改 #4731 / #6099 既有 fixture 的任何精确计数(新建第三个临时仓库objectui-released:两条 changeset 各自被一个 commit 添加,再由一个chore: release packagescommit 一并删除,范围base..release同时含增与删)。其中 3 条通过 spawnSync 子进程跑真实 CLI:git 的
fatal:落在真实 stderr 上,进程内断言根本看不见它。彻底失败那条通过新导出的readAt驱动 ——classifyRange到不了那个分支(它的sha来自--diff-filter=A,路径必然存在),而一条测试进不去的错误路径,正是会悄悄烂成静默的那条。门禁(均在
git add之后跑)pnpm check:objectui-changeset(digest 45 条 + objectui-range 自测)pnpm exec eslint scripts/objectui-changeset-digest.mjs --no-inline-configpnpm check:nul-bytesgrep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'不在本 PR 里
git cat-file -e探测再读)未采纳:它把回落变得完全无声,与本仓刚立的 absence/degradation must be loud 反向。B + C 保留「发生了回落」这个事实,只是把它从九行噪声压成一句可读的话。bump-objectui.sh一字未动 —— 它只是调用方,fatal:出自 digest 的readAt()。scripts/objectui-range.mjs未动(objectui 没有「发布性改动必须声明 changeset」闸门 —— changeset-guard.yml 的触发器决定了它只看得见已经带 changeset 的 PR(objectui#3518 因此整个从发布记录里消失) #6174 的面,该单在本 PR 合并后才派)。它经classifyRange消费本文件,新增的absentAtTo是纯增量字段,不改变任何既有返回值。main()里git cat-file -e端点探测的fatal:保留:那条路径失败即真失败(随后✗ … cannot walk …并 exit 2),响亮是对的,与本单要修的「完整结果长得像失败」不是一回事。Generated by Claude Code