Skip to content

空 changeset 会静默卡死已 version 的发布:Release run 全绿,但 npm 和 Docker 什么都没发(17.0.0-rc.2 现在就卡着) #4898

Description

@os-zhuang

现状:17.0.0-rc.2 已 version 未发布

#4422 于 14:19 合并(3cfd9f0),main 上所有包的 package.json 已经是 17.0.0-rc.2。随后的 Release run 30822140425 conclusion: success

但注册表上什么都没有:

npm  dist-tags: {"latest":"16.1.0","rc":"17.0.0-rc.1"}
     @objectstack/cli@17.0.0-rc.2 published? false

ghcr tags: 15.1.0, 15.1, 15, latest, 15.1.1, 16.0.0-rc.0, 16.0.0-rc.1,
           16.0.0, 16.0, 16, 16.1.0, 16.1, 17.0.0-rc.1

版本号进了仓库,包没进注册表,而流水线报绿。

根因

Create Release Pull Request or Publish to npm 这一步耗时 0 秒,只打了一行:

2026-08-03T14:23:19.5420695Z All changesets are empty; not creating PR

changesets/action 的分派逻辑(简化)是:

const hasChangesets        = changesets.length !== 0;
const hasNonEmptyChangesets = changesets.some(c => c.releases.length > 0);

switch (true) {
  case !hasChangesets && !hasPublishScript:  return;                  // 什么都不做
  case !hasChangesets &&  hasPublishScript:  await publish(); return; // ← 唯一的发布入口
  case  hasChangesets && !hasNonEmptyChangesets:
    core.info("All changesets are empty; not creating PR"); return;   // ← 落在这里
  case  hasChangesets:                       await runVersion(); return;
}

publish 分支只在「零 pending changeset」时进入。空 changeset 依然计入 hasChangesets,于是它既不产生版本 PR,也不发布 —— 静默 return,步骤成功,published=false,下游 Extract published @objectstack/cli versiondocker job 全部按 if: published == 'true' skipped。

当时 main 上的 pending 集合(用 .changeset/pre.jsonchangesets 数组扣掉已消费的 860 个后)恰好是:

磁盘 .md 总数: 862      已消费: 860      pending: 2
  EMPTY  pm-skill-landing-and-ci-discipline.md   (#4892 / #4893)
  EMPTY  release-pr-ci-gates.md                  (#4894 / #4896)

两个都是空 frontmatter。任意一个单独存在都足以触发,不是某个 PR 的个别失误。

为什么这必然会反复发生

.github/workflows/pr-automation.ymlCheck Changeset 明确把空 changeset 定为受祝福的声明方式:

An empty-frontmatter changeset still counts — it is the sanctioned "this PR releases nothing" declaration, on par with the skip-changeset label.

也就是说:仓库在制度上鼓励产出的东西,恰好是会卡住发布器的输入。只要在「版本 PR 合并 → 下一次 push 触发 publish」这个窗口里,main 上存在任何一个纯空的 pending changeset,这一轮发布就会消失,并且报绿。窗口不窄 —— 版本 PR 通常要等一段时间才合,期间落任何一个 docs/ci 类 PR 都会踩中。

危险的不是卡住,是卡住且报绿:没有任何信号说「本该发布的东西没发」。

修复方向

立即解堵(小、明确):删掉 main 上这两个 pending 的空 changeset。空 frontmatter 的 changeset releases 为空,对任何包的 CHANGELOG 都不产生条目,删除不丢信息 —— 它唯一的作用是满足 Check Changeset,而那两个 PR 早已合并、闸门早已通过。删完后下一次 main push 走 !hasChangesets && hasPublishScript 分支,changeset publish 补发 17.0.0-rc.2,Docker job 随之触发。

系统性修复(需要拍板,择一):

  1. 补一次幂等 publish:在 changesets/action 之后加一步,当 published != 'true' 但工作区存在未发布版本时执行 pnpm run releasechangeset publish 本身幂等 —— 已在 npm 上的版本会跳过。改动最小,直接消除「该发没发」这一类。
  2. 在 version 阶段就消费掉空 changeset:让 pnpm run version 把空 changeset 记入 pre.json / 删除,使 pending 集合永不含纯空项。更贴近 changesets 的语义,但要处理 pre 模式下的记账。
  3. 换掉空 changeset 这个约定:Check Changeset 不再接受空 changeset,只留 skip-changeset 标签一条路。最省事,但会让「本 PR 不发布」这件事失去仓库内的书面痕迹。

无论选哪条,都建议加一条断言:Release job 在 main 的 package.json 版本不存在于 npm 时,若本次没有 publish,应当 ::error:: 而不是静默成功 —— 这才是今天真正缺失的那个信号。


发现于 #4894 / #4896 合并后跟进 #4422 的过程中。当前 17.0.0-rc.2 处于卡住状态,建议优先处理「立即解堵」。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions