Skip to content

ci(release): gate the release PR on objectui pin freshness (#3340) - #4944

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-3340-objectui-pin-freshness-gate
Aug 3, 2026
Merged

ci(release): gate the release PR on objectui pin freshness (#3340)#4944
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-3340-objectui-pin-freshness-gate

Conversation

@xuyushun441-sys

@xuyushun441-sys xuyushun441-sys commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #3340 (仅 P0;P1 auto-bump 与 P2 覆盖对账不在本次范围内 —— P1「换判据源」已由 #4731 / #4843 落地)。

为什么

前端只有一次机会进入 changesets 流水线:.objectui-sha 移动、bump-objectui.sh 为它跨过的区间写下 @objectstack/console changeset。pin 之后 objectui 合并的一切,按构造就在这个区间之外 —— 没有 changeset、没有 changelog、没有 release page。而一份「看起来完整」的发布记录,和一份真正完整的记录,读者无法分辨。

v16 就是这样切的:发版时 objectui main 领先 pin 4 个提交 / 21 个 pending changeset,四条前端变更(其中两条 minor 特性)整体缺席,事后靠 #3331 手工补。#4731 / #4843 已经让两个消费者改读 objectui 声明的 changeset 而不是猜提交标题 —— 但一个正确的读者读一个过早停止的区间,给出的仍然是「看起来完整」的不完整清单。本 PR 堵的就是剩下的这个盲区。

判红条件

scripts/check-objectui-pin-fresh.mjs(pnpm check:objectui-pin-fresh):

  1. pin 不等于判据 ref(默认 objectui main,--ref 可指定 tag)—— 落后多少个提交、都是哪些提交,逐条列出;diverged / pin 反超 ref 各自单独出声。
  2. 有在 pin 之后声明、尚未 bundle 的 .changeset/*.md —— 逐个文件名列出。

第 2 条独立判定而不是挂在第 1 条下面:两者哪天不一致,就是其中一个错了,只看第 1 条的门禁不会发现。

挂载点

.github/workflows/objectui-pin-freshness.yml,job 名 = 分支保护 context 名:Console Pin Freshness

  • job 没有 job 级 if:没有 paths filter,因此在每个 PR 上都会汇报 —— 一个不运行的 check 什么也不报,而一个「required 却不汇报」的 context 会让每个 PR 永远卡在 Expected — waiting for status
  • 是否阻挡写在退出码里,不靠 skip:识别到 changesets 的 Version Packages / release PR(head 分支 changeset-release/main,或标题 chore: version packages,两者都查,免得将来改了一个就静默解除)时判红阻挡;其余普通 PR 传 --advisory,报告照打全文,只是不拦。普通代码 PR 期间 pin 落后是常态而非缺陷,每个 PR 都红只会训练所有人无视这个 check。
  • workflow_dispatch 手动跑按 enforcing 处理。

⚠️ required 强制还差什么(workflow 无法自足声明)

workflow 不能把自己设成 required。 需要维护者在 main 的分支保护里手动勾选:

Settings → Branches → main → Require status checks to pass → 加入 Console Pin Freshness

在此之前,本 workflow 在 release PR 上会汇报、会变红,但不会锁住 merge 按钮。这一点在 workflow 头部注释里也写明了,不假装 workflow 一己完成了强制。

Console Pin Gate(#4290)的区别 —— 名字相近,问的是相反的两件事

问题 做法
Console Pin Gate(#4290,ci.yml) 钉住的 SHA 还能不能构建? 在 pin 上 clone objectui 并构建 SPA
Console Pin Freshness(本 PR) pin 还是不是最新的? 把 pin 和 objectui main

两个门禁可以一个绿一个红:一个两个月前的 pin 构建得好好的(Pin Gate 绿),同时把两个月的前端发布挡在发布记录之外(本门禁红)。谁也不能替代谁。这段区别写在三处:脚本头部注释、两种判定的输出文案、docs/releases-maintenance.md —— 防止后人当成重复实现删掉其中一个。

网络失败绝不判绿

判据分层是刻意的:

  • git ls-remote 独自决定判决 —— head == pin 判绿,head != pin 判红。pin 是最新的时候,一次 API 都不调
  • 三个 GitHub API 调用(compare/< pin >...< head > + 两端 .changeset 目录列表)只负责把已经成立的落后展开成清单。所以 API 被限流 / 被代理挡掉 / 挂了,只会让报告降级(并把原因原样打出来),永远不可能把红变成绿
  • ls-remote 本身失败 → 判决 unreadable:enforcing 模式 exit 1,advisory 模式打 ::warning:: 并 exit 0 —— 任何模式下都不会渲染成 FRESH。一个因网络错误而变绿的新鲜度门禁,和没有门禁等价,它同样会放过 v16 那次。

本地 ../objectui 检出只用于进一步丰富报告(用 #4731/#4843 共享的 classifyRange() 走完整 log-walk),同样不参与判决;没有检出时出声说明远端那份 pending 清单只是端点 diff 的下界,不静默跳过(#4690 那一族)。

双向证明(真实数据,非构造)

判红 —— 用一个真实的历史 pin 785b8a5d432c:

✗ objectui pin is STALE — .objectui-sha no longer describes objectui `main`.

    pinned (.objectui-sha):       objectui@785b8a5d432c
    objectstack-ai/objectui main: objectui@91757a79f2fe
    relationship:                 ahead (37 ahead, 0 behind)

  • objectui `main` is 37 commit(s) AHEAD of the pin — everything in that range is outside the @objectstack/console changeset range.
  • 16 changeset(s) declared in objectui after the pin are not bundled into @objectstack/console at this pin.

  Frontend changes DECLARED after the pin — 16 changeset(s)
  present at main and absent at the pin:
    - aria-required-reaches-the-control.md
    - builtin-select-empty-state-translated.md
    - field-widget-error-slot-follows-spec.md
    - field-widget-single-metadata-carrier.md
    - flow-node-geometry-is-spec-position.md
    ... (共 16 条)

判红 —— 本分支上 main 当前真实的 pin(f5bc4c78be76),门禁今天就在响:

    relationship:                 ahead (6 ahead, 0 behind)

  Commits ahead of the pin (newest first, 6 of 6):
    - 91757a79f fix(console): emit maplibre's sibling worker beside its chunk (#3297) (#3304)
    - 30ae33a77 feat(fields): RichTextField honors `mobile_fullscreen` with a fullscreen editing dialog (#3301) (#3302)
    - f44d8727f fix(plugin-form,types): carry `mobile_fullscreen` on the field metadata, where widgets actually read (#3245) (#3300)
    - 680080ada fix(components): deliver `aria-required` to field controls (#3290) (#3298)
    - 6e794a19e fix(app-shell)!: converge flow node geometry to spec `FlowNode.position` (#3172) (#3294)
    - 042e09d77 refactor(types,react,components,fields)!: converge the widget metadata carrier to `field`, retiring `schema` (#3233) (#3296)

注意其中两条带 !(breaking)。这正是当前若开一次 release PR 会被静默漏掉的内容。

判绿 —— 把 pin 临时指向 objectui main HEAD(91757a79f2fe),验证后已还原:

✓ objectui pin is FRESH — .objectui-sha is objectui `main` (objectui@91757a79f2fe).

  Nothing objectui has merged is outside the @objectstack/console changeset range.
  This is NOT ci.yml's "Console Pin Gate" (#4290). ...
EXIT=0

(git status 已确认 .objectui-sha 未被本 PR 改动。)

--self-test(仓内 10 个 check 脚本的惯例)

29 条断言全绿,覆盖题目要求的四类 + 端到端跑真实 CLI(含退出码):

check-objectui-pin-fresh --self-test
  ✓ a pin behind objectui main is STALE
  ✓ the report states the lag in commits
  ✓ every pending changeset is listed BY NAME, not just counted
  ✓ the report names the remedy (bump-objectui.sh)
  ✓ the report distinguishes itself from the #4290 Console Pin Gate
  ✓ a pin equal to objectui main is FRESH
  ✓ the FRESH report still explains what it did NOT prove (#4290)
  ✓ pending unbundled changesets alone make the verdict STALE
  ✓ that verdict names the changeset
  ✓ a diverged pin is STALE
  ✓ a pin ahead of the judging ref is STALE (loudly, as its own case)
  ✓ an unreadable GitHub API still yields STALE when ls-remote proved the lag
  ✓ and the degraded itemization is announced with its reason
  ✓ while stating the verdict did not depend on it
  ✓ a missing local checkout is reported, not skipped
  ✓ and it says the remote pending list is a LOWER BOUND
  ✓ CLI exits 1 on a stale pin
  ✓ CLI output says STALE and names a pending changeset
  ✓ a fixture run announces itself as a fixture run
  ✓ CLI exits 0 on a current pin
  ✓ CLI output says FRESH
  ✓ CLI exits 1 when the remote cannot be read
  ✓ a network failure never renders as FRESH
  ✓ the network failure reason is printed verbatim
  ✓ advisory mode does not block on an unreadable remote…
  ✓ …but WARNS, and still never claims freshness
  ✓ advisory mode does not block on a stale pin (ordinary code PRs)
  ✓ …while the body still reports STALE in full
  ✓ JSON output carries the verdict, the lag and the pending list
✓ check-objectui-pin-fresh --self-test: all checks passed

--fixture < file >(自测专用,把远端状态从 JSON 读入,好让真实 CLI 的退出码也能被断言)每次运行都会先打一行 ⚠ REMOTE STATE READ FROM FIXTURE … — self-test output, not a real verdict.,不可能冒充真实判决。

其他验证

  • pnpm exec eslint scripts/check-objectui-pin-fresh.mjs --no-inline-config —— 无输出(干净)。
  • pnpm check:objectui-changeset(共享的 classifyRange() 消费者)仍全绿。
  • pnpm check:nul-bytes / check:doc-authoring / check:published-files 全绿。
  • workflow YAML 解析通过。

触碰的共享文件

The frontend enters the changesets pipeline exactly once: when .objectui-sha
moves and bump-objectui.sh writes the @objectstack/console changeset for the
range it crossed. Everything objectui merged AFTER the pin is outside that
range by construction — no changeset, no changelog, no release page — and a
complete-looking release record is indistinguishable from a complete one.
Cutting v16 that way lost four frontend changes, two of them minor features,
while objectui main sat 4 commits and 21 pending changesets ahead of the pin.

scripts/check-objectui-pin-fresh.mjs is red when the pin is not objectui main
(or a named --ref), and names both the commits ahead and the .changeset/*.md
files declared after the pin. git ls-remote alone decides the verdict, so the
three GitHub API calls only itemize an already-established lag: a rate-limited
or unreachable API degrades the report — loudly — and can never turn red into
green. An unreachable remote is verdict `unreadable`, never a pass.

Wired as the `Console Pin Freshness` job in a new workflow. It runs on every PR
so the context always reports and can therefore be required in branch
protection, but blocks only on the changesets Version Packages / release PR:
a pin lagging between bumps is the normal state of an ordinary code PR, and
failing every PR over it would train everyone to ignore the check.

Not to be confused with ci.yml's Console Pin Gate (#4290), which proves the
pinned SHA still BUILDS; this one proves the pin is still CURRENT. Either can
be green while the other is red. The distinction is spelled out in the script
header, in both workflows' output text and in docs/releases-maintenance.md.

--self-test covers: a lagging pin is red and lists the changesets by name; a
current pin is green; pending unbundled changesets are judged on their own; a
diverged/behind pin is red; an unreadable API still yields red with the reason
printed; a network failure never renders as FRESH in either mode.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 3, 2026 4:57pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file tooling size/l labels Aug 3, 2026
@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 3, 2026 17:03
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit 86552f2 Aug 3, 2026
24 of 25 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-3340-objectui-pin-freshness-gate branch August 3, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Release process: prevent frontend (objectui) changesets being missed when the console pin lags

2 participants