Skip to content

Commit 2aba873

Browse files
fix(release): publish binaries before npm
OSS uploads can fail on a network blip. If npm latest lands first, the registry points at a version whose CDN binaries are missing. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 437ee4c commit 2aba873

4 files changed

Lines changed: 36 additions & 29 deletions

File tree

‎.github/workflows/publish.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ jobs:
7979
# Bucket / prefix / allowlists live in the FC function.
8080
FC_TRIGGER_URL: ${{ vars.FC_TRIGGER_URL }}
8181
FC_RELEASE_AUDIENCE: ${{ vars.FC_RELEASE_AUDIENCE }}
82+
# Script order: git tag, then binary GitHub Release + OSS, then npm.
83+
# OSS upload can fail on a network blip; npm must not succeed first.
8284
run: node tools/release/publish-stable.mjs ${{ inputs.package == 'knowledge-studio-cli' && '--knowledge' || '' }}
8385

8486
publish-channel:
@@ -132,6 +134,8 @@ jobs:
132134
# credentials in this repo (see publish-stable env notes).
133135
FC_TRIGGER_URL: ${{ vars.FC_TRIGGER_URL }}
134136
FC_RELEASE_AUDIENCE: ${{ vars.FC_RELEASE_AUDIENCE }}
137+
# Script order: binary GitHub Release + OSS, then npm.
138+
# OSS upload can fail on a network blip; npm must not succeed first.
135139
run: node tools/release/publish-channel.mjs ${{ inputs.package == 'knowledge-studio-cli' && '--knowledge' || '' }} --channel "${{ inputs.channel }}"
136140

137141
# bailian-kb-dsh is the dsh plugin (downstream host adapter): independent version,

‎docs/agents/publish.md‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515

1616
```text
1717
publish-stable.mjs / publish-channel.mjs ← 唯一发版入口
18-
├─ npm(pnpm publish)
19-
└─ binary(lib/binary-release
20-
→ binary-build
21-
→ gh-release
22-
→ oss-direct-upload → FC release 通道)
18+
├─ binary 先走(lib/binary-release
19+
│ → binary-build → gh-release
20+
│ → oss-direct-upload → FC release 通道)
21+
└─ OSS 成功后再 npm(pnpm publish)
2322
```
2423

24+
bailian-cli 的二进制(GitHub Release + OSS)在 npm 之前。OSS 上传受网络波动可能失败;若 npm 先成功,会出现 latest 已发出但 CDN 没有对应二进制。stable 仍要先推 `v<version>` tag,因为 GitHub Release 首次创建带 `--verify-tag`。`knowledge-studio-cli` / `--skip-binary` 没有二进制,仍只发 npm。
25+
2526
`tools/release/lib/binary-release.mjs` 等是实现,一般不要单独当发版入口(调试可用)。
2627

2728
### OSS 通道:FC 预签名上传(仓库不持有任何 OSS 凭据)
@@ -73,15 +74,15 @@ workflow 的 `channel` 输入**只决定 npm dist-tag**(如 `mcp` / `plugin` /
7374
1. 在 GitHub 触发 Publish workflow,mode 选 `channel`,channel 填 npm dist-tag 名:
7475
- **`bailian-cli`**:npm 发到该 tag;二进制同时刷新 CDN `sync-release.json`(与 tag 名无关)。本机验证:`BAILIAN_CHANNEL=sync-release`。**先发二进制(zip+tar.gz 上齐)再发静态仓 `install.sh`**,避免新脚本去拉还不存在的 `.tar.gz`。
7576
- **`knowledge-studio-cli`**:仅 npm(自动跳过 binary,不碰 `sync-release.json`)
76-
2. CI 自动:生成 `0.0.0-beta-<sha7>-<YYYYMMDDHHMM>`(UTC 到分钟;同 commit 同分钟重跑会覆盖同号)→ 临时 bump → 自检 → **npm 发到 dist-tag** →(bailian-cli)**Bun 编二进制 + GH prerelease + 覆盖 `sync-release.json`** → 还原 package.json
77+
2. CI 自动:生成 `0.0.0-beta-<sha7>-<YYYYMMDDHHMM>`(UTC 到分钟;同 commit 同分钟重跑会覆盖同号)→ 临时 bump → 自检 →(bailian-cli)**Bun 编二进制 + GH prerelease + 覆盖 `sync-release.json`** → **npm 发到 dist-tag** → 还原 package.json
7778
3. 对应脚本:`tools/release/publish-channel.mjs`
7879

7980
### stable 发布
8081

8182
1. 确保当前 release tooling 覆盖的包(`tools/release/lib/packages.mjs`)已升到目标版本且一致;当前基础集合为 `packages/core` / `packages/runtime` / `packages/commands` / `packages/cli`,`knowledge-studio-cli` 发布会额外包含 `packages/kscli`
8283
2. 在 GitHub 触发 Publish workflow,package 选目标包集合,mode 选 `stable`
8384
3. 需要 production environment 审批人批准
84-
4. CI 自动:自检 → **npm 发到 latest** → **推送 git tag `v<ver>`** → **Bun 编二进制并创建/更新 GitHub Release**(每平台 `.zip`,darwin/linux 额外 `.tar.gz`)→(bailian-cli)维护 CDN **`manifest.json`**(unix 资产含 `tar` / `tarSha256`,`file` 仍为 zip)→ 完成
85+
4. CI 自动:自检 → **推送 git tag `v<ver>`** → **Bun 编二进制并创建/更新 GitHub Release**(每平台 `.zip`,darwin/linux 额外 `.tar.gz`)→(bailian-cli)维护 CDN **`manifest.json`**(unix 资产含 `tar` / `tarSha256`,`file` 仍为 zip)→ **npm 发到 latest** → 完成
8586
5. 如果所选发布集合的当前版本已全部存在于 npm,stable 发布会失败并提示先升级版本号;如果只有部分包已发布,CI 会继续补发缺失包
8687
6. 对应脚本:`tools/release/publish-stable.mjs`
8788

@@ -160,4 +161,5 @@ node tools/release/publish-channel.mjs --channel test --knowledge --dry-run
160161
| CI 用 Node 22(npm 10)跑 publish | npm 10 不支持 OIDC token 交换,publish 报 404 |
161162
| stable 发布前没有升级版本号 | 所选发布集合的版本已全部存在于 npm,CI 明确报错并要求先升级版本号 |
162163
| channel job 缺少 `contents: write` | `gh release create` 失败 |
164+
| npm 先于二进制 / OSS 成功 | latest 已发出,CDN 上没有对应 zip/tar.gz;OSS 网络失败时无法回滚 npm |
163165
| stable 未先推 tag 就建 Release | `--verify-tag` 失败 |

‎tools/release/publish-channel.mjs‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,8 @@ try {
8080
published.set(pkg.key, exists);
8181
log(`${pkg.name}@${betaVersion}: ${exists ? "already published" : "to publish"}`);
8282
}
83-
if (packages.every((pkg) => published.get(pkg.key))) {
84-
log("\nall packages already published; nothing to do for npm.");
85-
} else {
86-
// 1) npm (dependency order: core → runtime → commands → cli [→ kscli])
87-
for (const pkg of packages) {
88-
if (published.get(pkg.key)) continue;
89-
step(`publish ${pkg.name}@${betaVersion} (tag=${channel}, provenance)`);
90-
pnpmPublish(pkg, { tag: channel, provenance: true, dryRun });
91-
}
92-
}
93-
94-
// 2) binary GitHub Release — must run before finally restores package.json versions.
95-
// Channel binary always refreshes OSS sync-release.json (npm tag is independent).
83+
// 1) binary GitHub Release before npm, and before finally restores package.json.
84+
// OSS sync-release.json can fail on a network blip; npm must not land first.
9685
if (skipBinary) {
9786
const reason = knowledge
9887
? "[knowledge] skipping binary (npm-only; does not touch sync-release.json)"
@@ -105,6 +94,17 @@ try {
10594
await releaseBinaryArtifacts({ mode: "channel", channel, dryRun });
10695
}
10796

97+
// 2) npm (dependency order: core → runtime → commands → cli [→ kscli])
98+
if (packages.every((pkg) => published.get(pkg.key))) {
99+
log("\nall packages already published; nothing to do for npm.");
100+
} else {
101+
for (const pkg of packages) {
102+
if (published.get(pkg.key)) continue;
103+
step(`publish ${pkg.name}@${betaVersion} (tag=${channel}, provenance)`);
104+
pnpmPublish(pkg, { tag: channel, provenance: true, dryRun });
105+
}
106+
}
107+
108108
const parts = ["npm"];
109109
if (!skipBinary) parts.push("binary/sync-release");
110110
log(`\nchannel release complete: ${channel}@${betaVersion} (${parts.join(" + ")})`);

‎tools/release/publish-stable.mjs‎

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,7 @@ try {
6262
);
6363
}
6464

65-
// 1) npm (dependency order: core → runtime → commands → cli [→ kscli])
66-
for (const pkg of packages) {
67-
if (published.get(pkg.key)) continue;
68-
step(`publish ${pkg.name}@${version} (tag=latest, provenance)`);
69-
pnpmPublish(pkg, { tag: "latest", provenance: true, dryRun });
70-
}
71-
72-
// 2) git tag — must be on origin before the GitHub Release step (--verify-tag)
65+
// 1) git tag — must be on origin before the GitHub Release step (--verify-tag)
7366
const tag = `v${version}`;
7467
if (dryRun) {
7568
log("\n[dry-run] skipping git tag");
@@ -81,14 +74,22 @@ try {
8174
pushTag(tag);
8275
}
8376

84-
// 3) binary GitHub Release (same version; orchestrated here, not a separate release entry)
77+
// 2) binary GitHub Release + OSS before npm. The OSS upload can fail on a
78+
// network blip; publishing npm first would leave latest without binaries.
8579
if (skipBinary) {
8680
log("\n[skip-binary] skipping binary GitHub Release");
8781
} else {
8882
step(`publish binary GitHub Release (mode=stable, version=${version})`);
8983
await releaseBinaryArtifacts({ mode: "stable", dryRun });
9084
}
9185

86+
// 3) npm (dependency order: core → runtime → commands → cli [→ kscli])
87+
for (const pkg of packages) {
88+
if (published.get(pkg.key)) continue;
89+
step(`publish ${pkg.name}@${version} (tag=latest, provenance)`);
90+
pnpmPublish(pkg, { tag: "latest", provenance: true, dryRun });
91+
}
92+
9293
const parts = ["npm"];
9394
if (!skipBinary) parts.push("binary");
9495
log(`\nstable release complete (${parts.join(" + ")}).`);

0 commit comments

Comments
 (0)