fix(ci): Check Changeset 实时读 skip-changeset 标签,首个 run 不再永久红 (#5580) - #5625
Merged
Conversation
`github.event.pull_request.labels` 是事件触发那一刻的快照。开 PR 后数秒内补 `skip-changeset` 标签,`opened` 事件的 run 看不见它 → 走计数路径 → 无 changeset → 红;而 `rerun_failed_jobs` 复用同一份载荷(pm-dispatch Operational notes 5), 于是这个红 run 按构造无法被重跑成绿。一日三例:#5467(本门禁自己的修复 PR)、 #5501、#5577,每例都要一个人或 agent 停下来「认签名解释掉」。 job 内新增第一个步骤,用 `gh api repos/$REPO/pulls/$PR` 实时读回标签集,产出 `steps.labels.outputs.skip`;其后每个步骤按它决定是否执行。载荷读法按 issue 建议 保留为 fast-path —— 载荷已有标签就整个 job 跳过,常规路径依旧零 runner 成本。 - **容忍方向朝着执行**:标签读不到(API 报错、无 PR 号)判为 `skip=false`,即 照常执行守卫。读不到输入的门什么也没验证,据此发豁免正是 #4690 反模式(静默 跳过、exit 0、看起来像「无违规」);失败以 `::warning::` 明说,由计数步骤定论。 - **实时读放在 checkout 之前**:标签在位时其后全部步骤跳过,整个 job 只花一次 API 调用 —— 收敛到实时状态比它替掉的那个 stale 红更便宜。 - **精确整行匹配**(`grep -qxF`,here-string 而非管道):被替换的 `contains(数组, 'skip-changeset')` 是数组元素精确匹配,子串匹配会让 `skip-changeset-audit` 这类标签新获豁免;here-string 让 `grep -q` 不进管道,避免 `-q` 首个命中即关闭 管道、写入端吃 SIGPIPE 在 `pipefail` 下把判定翻成 false。 - 保留 fast-path 留下唯一一个反向 stale 格:标签在开 PR 后被**移除**时本 run 仍 短路。该格自愈 —— 移除标签必然触发 `unlabeled` 事件,它起的 run 两处都看不到 标签而照常执行;#5580 那个方向没有这种救援(`labeled` run 的绿不会清掉 `opened` run 的红)。文件内注释写明了这笔交换。 ⛔ 未动 `BASE_SHA` diff 计数逻辑与 #5292/PR #5467 的三段有序失败文案(heredoc 终结符仍在块基缩进);未动其他 job。`allow-major` 步骤的同款载荷读法按边界留在 原样 —— RC pre-mode 期间休眠(`check-changeset-no-major.mjs` 整体让位),已记为 #5620。 验证:`check:workflow-status-functions` 与 `check:nul-bytes`(含各自 self-test) 全绿;从 YAML 抽出该步骤真实脚本,以 stub `gh` 在 `bash -e` 与 `bash -eo pipefail` 两种方言下跑 7 场景 × 2 = 14 例全通过(载荷 stale/标签实时在位、无标签、空标签、 API 失败、无 PR 号、近似标签名、401 个标签的 pipefail 压力);另建前后决策真值表, 7 格中仅「载荷无标签 + 实时有标签」的首 run 与其重跑两格改变(enforce → exempt), 与事前预测一致。 Fixes #5580 Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE Co-authored-by: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
Author
本 PR 自己成了活体验证:重跑从红收敛到绿PR body 里那条是预测,现在有实测了 —— 而且撞的正是 #5580 的核心格。 同一个 run(
attempt 2 的实时读步骤日志(逐字): 这正是 #5580 判定为不可能的那一步。 旧逻辑下 attempt 2 读的还是同一份冻结载荷, 两个顺带被实测确认的点:
残留的那一半,如实说明attempt 1 仍然红,这不是缺陷而是本修法的边界:实时读把读取时刻从「事件创建」推到 Generated by Claude Code |
os-zhuang
marked this pull request as ready for review
August 5, 2026 20:56
os-zhuang
enabled auto-merge
August 5, 2026 20:56
os-zhuang
pushed a commit
that referenced
this pull request
Aug 5, 2026
…s 现状」通病标本 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
This was referenced Aug 5, 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 #5580
症状
.github/workflows/pr-automation.yml的 changeset-check job 从事件载荷读标签:github.event.pull_request.labels是事件触发那一刻的快照。于是开 PR 后数秒内补skip-changeset标签时:opened事件的 run 看不见标签 → 走计数路径 → 无 changeset → 红;labeled事件的新 run → skipped(判定正确);rerun_failed_jobs复用原事件载荷(pm-dispatchOperational notes 5),重跑多少次都看不见后来的标签。
一日三例:#5467(该门禁自己的修复 PR)、#5501、#5577。每例的直接成本是一个需要人或
agent 停下来「认签名解释掉」的红检查 —— merge-queue-triage、PM 复核、事件订阅三方
会各自撞一次。
修法
job 内新增第一个步骤,用
gh api repos/$REPO/pulls/$PR实时读回标签集,产出steps.labels.outputs.skip;其后每个步骤按它决定是否执行。载荷读法按 issue 建议保留为 fast-path(载荷已有标签 → 整个 job 跳过,常规路径零 runner 成本)。
重跑因此收敛:载荷仍是旧的,但实时读每次都重新查询。
四个设计决定
skip=false,即照常执行守卫。读不到输入的门什么也没验证,据此发豁免正是 check:react-declaration-parity 是唯一没接进任何 workflow 的源码审计门禁,且无 MANIFEST 时静默 skip 退出 0 —— 它现在永远不可能红 #4690 反模式(静默跳过、
exit 0、看起来像「无违规」)。失败以
::warning::明说,由计数步骤定论。调用 —— 收敛到实时状态比它替掉的那个 stale 红更便宜。
grep -qxF,here-string 而非管道)。被替换的contains(数组, 'skip-changeset')是数组元素精确匹配,子串匹配会让skip-changeset-audit这类标签新获豁免。here-string(与 release.yml 一致)让
grep -q不进管道:-q首个命中即关闭管道,写入端可能吃 SIGPIPE,在
set -o pipefail下会把判定翻成 false。该步骤未写
shell:,两种方言都已实测。if:读不到自己 job 的步骤;另起一个 gate job 会给本已在与检查列表噪音作战的仓库再添一行检查和一个全新的变红
途径,而把标签判定塞进计数步骤里则要先付 checkout + install 才发现该 PR 豁免。
边界
BASE_SHAdiff 计数逻辑,也未动 Check Changeset 的失败文案把「空 changeset」推荐为出路 —— 而那正是 #4898 静默卡死发布的输入 #5292/PR fix(ci): Check Changeset 的失败文案不再把「空 changeset」当作与标签等价的出路 (#5292) #5467 的三段有序失败文案 ——heredoc 终结符仍在块基缩进(解析后确认
MSG落在生成脚本第 0 列)。allow-major步骤的同款载荷读法按边界留在原样,只补上「changeset 豁免时同样跳过」这一句以保持 Check Changeset 从事件载荷读
skip-changeset标签:开 PR 后 5 秒内加标签,首个 run 永久红(重跑复用载荷)—— 一日三例 #5580 之前的语义(那时整个 job 连带此步骤一起跳过)。它自身的竞态在 RC pre-mode 期间休眠 ——
check-changeset-no-major.mjs整个 RC 窗口让位,标签目前根本用不上 —— 已记为 pr-automation.yml 的 allow-major 步骤仍从事件载荷读标签:#5580 同款竞态的孪生体(pre-mode 期间休眠) #5620,文件内注释指向它。
验证
pnpm check:workflow-status-functions(#5343/PR #5477 门禁)if:;self-test 34 断言通过pnpm check:nul-bytespnpm check:node-versionBASE_SHAenv 与计数命令逐字未变分支矩阵(从 YAML 抽出真实
run:脚本,stubgh,两种 shell 方言)跑的是 workflow 里那段脚本本身,不是重敲的副本。
bash -ebash -eo pipefailskip-changeset-audit/no-skip-changeset)反向验证
GitHub 表达式语言无法在本地执行,所以前后决策函数是建模的(shell 那一半是真跑
的)。方向先预测后运行:应当只有一格改变。
rerun_failed_jobs(同一份冻结载荷)unlabeled事件实际改变 2 格,而非 1 —— 因为「首 run」与「它的重跑」是同一个缺陷的两次,预测里写的
就是这两格;其余 5 格逐格不变。
保留 fast-path 留下的唯一反向 stale 格已如实记在文件里:标签在开 PR 后被移除时
本 run 仍短路,即凭快照放行。该格自愈 —— 移除标签必然触发
unlabeled事件,它起的run 两处都看不到标签而照常执行。#5580 那个方向没有这种救援:
labeledrun 的绿不会清掉
openedrun 的红。这是保留 fast-path 换来常规路径零成本的代价,值得,但下一个读者应当看见它,所以写进了注释而不是只写在这里。
本 PR 自身
skip-changeset标签:纯 workflow 改动,⛔ 未写空 frontmatter changeset(那正是 Check Changeset 的失败文案把「空 changeset」推荐为出路 —— 而那正是 #4898 静默卡死发布的输入 #5292/PR fix(ci): Check Changeset 的失败文案不再把「空 changeset」当作与标签等价的出路 (#5292) #5467 判定为最后手段的东西)。请 PM 落标签。
pull_request事件的 workflow 取自 merge ref,所以本 PR 的 Check Changeset 跑的已经是新逻辑。预测:标签若在首个 run 的第一步之后才落上,该 run 仍会红 —— 但
这次
rerun_failed_jobs就能变绿,而这恰是修好了的那半。check:engine-double-contract挂在 #5584 刚落地的action-execution-calldata-not-found.test.ts(2 个 double 未接assertEngineDeleteDispatch,基线无条目)——所有新 PR 的 ESLint job 都过不去 #5604 ——check:engine-double-contract在packages/runtime/src/action-execution-calldata-not-found.test.ts第 69、102 行。在本分支 base(
cc5b048a0)上直接跑该脚本复现,退出码 1,签名逐字一致;本 PR 只改一个 workflow 文件。ESLint 非必需检查。注意上面两个门禁也在
ESLint job 里,且排在那个失败步骤之前,所以它们各自的绿在 job 日志里仍可见,
只是 job 总结论为红。
Generated by Claude Code