Skip to content

Commit 75bece7

Browse files
Merge pull request #213 from modelstudioai/feat/binary-install-diagnosis
Feat/binary install diagnosis
2 parents 20594b7 + 5545a61 commit 75bece7

13 files changed

Lines changed: 209 additions & 26 deletions

File tree

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
| sudo tar -xz -C /usr/local/bin gitleaks
5757
gitleaks version
5858
59-
- name: Ensure zip (per-platform binary archives)
59+
- name: Ensure zip and tar (per-platform binary archives)
6060
run: sudo apt-get update && sudo apt-get install -y zip
6161

6262
- run: pnpm install --frozen-lockfile
@@ -114,7 +114,7 @@ jobs:
114114
| sudo tar -xz -C /usr/local/bin gitleaks
115115
gitleaks version
116116
117-
- name: Ensure zip (per-platform binary archives)
117+
- name: Ensure zip and tar (per-platform binary archives)
118118
run: sudo apt-get update && sudo apt-get install -y zip
119119

120120
- run: pnpm install --frozen-lockfile

INSTALL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ bl --version
7070
which bl # Windows: where.exe bl
7171
```
7272

73-
> CDN / GitHub Release 未就绪或下载失败时,若本机已有合格 Node,回退到上方 npm 安装。
73+
> CDN / GitHub Release 未就绪或下载失败时,若本机已有合格 Node,回退到上方 npm 安装。Unix 二进制安装优先解 `.tar.gz`(不依赖 `unzip`);Windows 仍使用 `.zip`
7474
7575
---
7676

docs/agents/publish.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ workflow 的 `channel` 输入**只决定 npm dist-tag**(如 `mcp` / `plugin` /
7171
### channel 发布
7272

7373
1. 在 GitHub 触发 Publish workflow,mode 选 `channel`,channel 填 npm dist-tag 名:
74-
- **`bailian-cli`**:npm 发到该 tag;二进制同时刷新 CDN `sync-release.json`(与 tag 名无关)。本机验证:`BAILIAN_CHANNEL=sync-release`
74+
- **`bailian-cli`**:npm 发到该 tag;二进制同时刷新 CDN `sync-release.json`(与 tag 名无关)。本机验证:`BAILIAN_CHANNEL=sync-release`**先发二进制(zip+tar.gz 上齐)再发静态仓 `install.sh`**,避免新脚本去拉还不存在的 `.tar.gz`
7575
- **`knowledge-studio-cli`**:仅 npm(自动跳过 binary,不碰 `sync-release.json`
7676
2. CI 自动:生成 `0.0.0-beta-<sha7>-<YYYYMMDDHHMM>`(UTC 到分钟;同 commit 同分钟重跑会覆盖同号)→ 临时 bump → 自检 → **npm 发到 dist-tag** →(bailian-cli)**Bun 编二进制 + GH prerelease + 覆盖 `sync-release.json`** → 还原 package.json
7777
3. 对应脚本:`tools/release/publish-channel.mjs`
@@ -81,7 +81,7 @@ workflow 的 `channel` 输入**只决定 npm dist-tag**(如 `mcp` / `plugin` /
8181
1. 确保当前 release tooling 覆盖的包(`tools/release/lib/packages.mjs`)已升到目标版本且一致;当前基础集合为 `packages/core` / `packages/runtime` / `packages/commands` / `packages/cli``knowledge-studio-cli` 发布会额外包含 `packages/kscli`
8282
2. 在 GitHub 触发 Publish workflow,package 选目标包集合,mode 选 `stable`
8383
3. 需要 production environment 审批人批准
84-
4. CI 自动:自检 → **npm 发到 latest****推送 git tag `v<ver>`****Bun 编二进制并创建/更新 GitHub Release** →(bailian-cli)维护 CDN **`manifest.json`** → 完成
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)→ 完成
8585
5. 如果所选发布集合的当前版本已全部存在于 npm,stable 发布会失败并提示先升级版本号;如果只有部分包已发布,CI 会继续补发缺失包
8686
6. 对应脚本:`tools/release/publish-stable.mjs`
8787

packages/core/src/install/cdn.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
* FC-presigned URLs and holds no OSS credentials itself.
66
*
77
* Layout under the base:
8-
* v<version>/<asset>.zip —— immutable per-version binaries + SHA256SUMS
8+
* v<version>/<asset>.zip —— per-version zip (Windows installer, `bl update`, old Unix install.sh)
9+
* v<version>/<asset>.tar.gz —— unix install.sh default (darwin / linux); same inner binary
10+
* SHA256SUMS —— checksums for zip and tar.gz
911
* manifest.json —— stable install/update pointer (rolling-manifest shape)
1012
* latest.json —— stable alias; same body as manifest.json
1113
* sync-release.json —— official channel/verify rolling pointer (all bailian-cli
@@ -97,6 +99,15 @@ export function binaryAssetFileName(
9799
return `bl-${version}-${os}-${arch}.zip`;
98100
}
99101

102+
/**
103+
* Unix install.sh archive: `bl-<ver>-<os>-<arch>.tar.gz`.
104+
* Windows has no tar.gz (PowerShell Expand-Archive uses zip).
105+
*/
106+
export function binaryTarFileName(version: string, os: string, arch: string): string | undefined {
107+
if (os === "windows") return undefined;
108+
return `bl-${version}-${os}-${arch}.tar.gz`;
109+
}
110+
100111
/** Uncompressed binary name inside the zip. */
101112
export function binaryInnerFileName(
102113
version: string,

packages/core/src/install/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export {
1414
DEFAULT_INSTALL_SCRIPT_URL,
1515
GITHUB_RELEASES_BASE,
1616
binaryAssetFileName,
17+
binaryTarFileName,
1718
binaryInnerFileName,
1819
channelManifestUrl,
1920
detectBinaryPlatform,

packages/core/tests/install-method.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
getUpdateInstallMethod,
1010
isCompiledBinary,
1111
binaryAssetFileName,
12+
binaryTarFileName,
1213
binaryInnerFileName,
1314
writeInstallMethodSync,
1415
} from "../src/install/index.ts";
@@ -103,6 +104,12 @@ test("binaryAssetFileName uses per-platform zip", () => {
103104
expect(binaryAssetFileName("1.2.3", "darwin", "arm64", false)).toBe("bl-1.2.3-darwin-arm64.zip");
104105
});
105106

107+
test("binaryTarFileName is unix-only", () => {
108+
expect(binaryTarFileName("1.2.3", "darwin", "arm64")).toBe("bl-1.2.3-darwin-arm64.tar.gz");
109+
expect(binaryTarFileName("1.2.3", "linux", "x64")).toBe("bl-1.2.3-linux-x64.tar.gz");
110+
expect(binaryTarFileName("1.2.3", "windows", "x64")).toBeUndefined();
111+
});
112+
106113
test("binaryInnerFileName keeps exe suffix inside zip", () => {
107114
expect(binaryInnerFileName("1.2.3", "windows", "x64", true)).toBe("bl-1.2.3-windows-x64.exe");
108115
expect(binaryInnerFileName("1.2.3", "darwin", "arm64", false)).toBe("bl-1.2.3-darwin-arm64");

skills/bailian-protocol/assets/setup.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ curl -fsSL https://bailian.aliyun.com/cli/install.sh | bash
3232

3333
Windows PowerShell: `irm https://bailian.aliyun.com/cli/install.ps1 | iex`
3434

35+
Unix `install.sh` prefers the `.tar.gz` asset and does not require `unzip`. Windows keeps `.zip`.
36+
3537
### Skills
3638

3739
- **Supported:** `bl skill init` (installs every `bailian-*`, including `bailian-protocol`)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2+
import { tmpdir } from "node:os";
3+
import { join } from "node:path";
4+
import { spawnSync } from "node:child_process";
5+
import { describe, expect, test } from "vite-plus/test";
6+
import {
7+
BINARY_TARGETS,
8+
binaryTarAssetName,
9+
matrixAssetNames,
10+
unixTarTarget,
11+
} from "./binary-build.mjs";
12+
import { tarOne } from "./binary-tar.mjs";
13+
14+
describe("binary archive matrix", () => {
15+
test("unix targets ship zip and tar.gz; windows is zip-only", () => {
16+
const names = matrixAssetNames("1.2.3");
17+
expect(names).toEqual([
18+
"bl-1.2.3-darwin-arm64.zip",
19+
"bl-1.2.3-darwin-arm64.tar.gz",
20+
"bl-1.2.3-darwin-x64.zip",
21+
"bl-1.2.3-darwin-x64.tar.gz",
22+
"bl-1.2.3-linux-x64.zip",
23+
"bl-1.2.3-linux-x64.tar.gz",
24+
"bl-1.2.3-windows-x64.zip",
25+
]);
26+
expect(BINARY_TARGETS.filter((target) => unixTarTarget(target))).toHaveLength(3);
27+
expect(binaryTarAssetName("1.2.3", { os: "linux", arch: "x64" })).toBe(
28+
"bl-1.2.3-linux-x64.tar.gz",
29+
);
30+
});
31+
});
32+
33+
describe("tarOne", () => {
34+
test("tar.gz stores basename only and round-trips bytes", () => {
35+
const outdir = mkdtempSync(join(tmpdir(), "bl-bin-tar-"));
36+
try {
37+
const innerName = "bl-9.9.9-linux-x64";
38+
const innerPath = join(outdir, innerName);
39+
writeFileSync(innerPath, "fake-binary-payload\n");
40+
chmodSync(innerPath, 0o755);
41+
const packed = tarOne(
42+
{ innerName, innerPath, os: "linux", arch: "x64" },
43+
{ outdir, tarFileName: "bl-9.9.9-linux-x64.tar.gz" },
44+
);
45+
expect(packed.fileName).toBe("bl-9.9.9-linux-x64.tar.gz");
46+
expect(packed.sha256).toMatch(/^[a-f0-9]{64}$/);
47+
48+
const extractDir = join(outdir, "out");
49+
mkdirSync(extractDir);
50+
const extract = spawnSync("tar", ["-C", extractDir, "-xzf", packed.outfile, innerName], {
51+
encoding: "utf-8",
52+
});
53+
expect(extract.status).toBe(0);
54+
expect(readFileSync(join(extractDir, innerName), "utf-8")).toBe("fake-binary-payload\n");
55+
} finally {
56+
rmSync(outdir, { recursive: true, force: true });
57+
}
58+
});
59+
});

tools/release/lib/binary-build.mjs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* Build standalone `bl` binaries with Bun --compile, then pack each as a
3-
* per-platform `.zip` via binary-zip.mjs (Release / OSS download asset).
3+
* per-platform `.zip` (all OS) plus `.tar.gz` on darwin/linux (Release / OSS).
44
*
55
* Used by lib/binary-release.mjs (and publish-stable / publish-channel orchestrators).
66
* Debug:
@@ -21,6 +21,7 @@ import {
2121
rollingManifestChannelId,
2222
rollingManifestFileName,
2323
} from "./binary-options.mjs";
24+
import { ensureTar, tarOne } from "./binary-tar.mjs";
2425
import { ensureZip, zipOne } from "./binary-zip.mjs";
2526

2627
const BINARY_COMPILE = fileURLToPath(new URL("./binary-compile.mjs", import.meta.url));
@@ -42,14 +43,27 @@ export function binaryInnerName(version, { os, arch, exe }) {
4243
return `bl-${version}-${os}-${arch}${exe ? ".exe" : ""}`;
4344
}
4445

45-
/** Release asset basename: `bl-<ver>-<os>-<arch>.zip`. */
46+
/** Release zip basename: `bl-<ver>-<os>-<arch>.zip` (install.ps1 / bl update / old install.sh). */
4647
export function binaryAssetName(version, { os, arch }) {
4748
return `bl-${version}-${os}-${arch}.zip`;
4849
}
4950

50-
/** Full matrix zip basenames for a version (order matches BINARY_TARGETS). */
51+
/** Unix install.sh default archive: `bl-<ver>-<os>-<arch>.tar.gz`. */
52+
export function binaryTarAssetName(version, { os, arch }) {
53+
return `bl-${version}-${os}-${arch}.tar.gz`;
54+
}
55+
56+
export function unixTarTarget(target) {
57+
return target.os !== "windows";
58+
}
59+
60+
/** Full matrix: zip for every target, plus tar.gz for darwin/linux. */
5161
export function matrixAssetNames(version) {
52-
return BINARY_TARGETS.map((target) => binaryAssetName(version, target));
62+
return BINARY_TARGETS.flatMap((target) => {
63+
const zipName = binaryAssetName(version, target);
64+
if (!unixTarTarget(target)) return [zipName];
65+
return [zipName, binaryTarAssetName(version, target)];
66+
});
5367
}
5468

5569
function log(message = "") {
@@ -156,22 +170,32 @@ function compileOne({ bunTarget, os, arch, exe }, version, outdir, entry) {
156170
}
157171

158172
function writeChecksums(outdir, artifacts) {
159-
const lines = artifacts.map((item) => `${item.sha256} ${item.fileName}`);
173+
const lines = [];
174+
for (const item of artifacts) {
175+
lines.push(`${item.sha256} ${item.fileName}`);
176+
if (item.tarFileName && item.tarSha256) {
177+
lines.push(`${item.tarSha256} ${item.tarFileName}`);
178+
}
179+
}
160180
writeFileSync(join(outdir, "SHA256SUMS"), `${lines.join("\n")}\n`);
161181
}
162182

163-
/** Write the rolling channel manifest (`latest.json` / `sync-release.json`) with per-platform zip + sha256. */
183+
/** Write the rolling channel manifest (`latest.json` / `sync-release.json`) with per-platform zip + optional tar.gz. */
164184
function writeChannelManifest(outdir, version, artifacts, mode) {
165185
const channel = rollingManifestChannelId(mode);
166186
const assets = Object.fromEntries(
167-
artifacts.map((item) => [
168-
`${item.os}-${item.arch}`,
169-
{
187+
artifacts.map((item) => {
188+
const asset = {
170189
file: item.fileName,
171190
sha256: item.sha256,
172191
inner: item.innerName,
173-
},
174-
]),
192+
};
193+
if (item.tarFileName && item.tarSha256) {
194+
asset.tar = item.tarFileName;
195+
asset.tarSha256 = item.tarSha256;
196+
}
197+
return [`${item.os}-${item.arch}`, asset];
198+
}),
175199
);
176200
const manifest = {
177201
name: "bailian-cli",
@@ -203,14 +227,36 @@ function smokeTestHostBinary(compiled, outdir) {
203227
}
204228
}
205229

206-
/** Compile binaries into `outdir`, zip per platform, write checksums (+ channel manifest). */
230+
function packOne(compiled, version, outdir) {
231+
let tarMeta = null;
232+
if (unixTarTarget(compiled)) {
233+
tarMeta = tarOne(compiled, {
234+
outdir,
235+
tarFileName: binaryTarAssetName(version, compiled),
236+
log,
237+
});
238+
}
239+
const zipMeta = zipOne(compiled, {
240+
outdir,
241+
zipFileName: binaryAssetName(version, compiled),
242+
log,
243+
});
244+
return {
245+
...zipMeta,
246+
tarFileName: tarMeta?.fileName,
247+
tarSha256: tarMeta?.sha256,
248+
};
249+
}
250+
251+
/** Compile binaries into `outdir`, zip (+ unix tar.gz) per platform, write checksums (+ channel manifest). */
207252
export function buildBinaryArtifacts(rawOptions = {}) {
208253
const options = normalizeBuildOptions(rawOptions);
209254
const { outdir, mode, channel } = options;
210255
const bunVersion = ensureBun();
211256
ensureZip();
212257
const version = cliVersion();
213258
const targets = resolveTargets(options);
259+
if (targets.some((target) => unixTarTarget(target))) ensureTar();
214260

215261
mkdirSync(outdir, { recursive: true });
216262
log(`bun ${bunVersion}`);
@@ -220,9 +266,7 @@ export function buildBinaryArtifacts(rawOptions = {}) {
220266

221267
const compiled = targets.map((target) => compileOne(target, version, outdir, CLI_ENTRY));
222268
smokeTestHostBinary(compiled, outdir);
223-
const artifacts = compiled.map((item) =>
224-
zipOne(item, { outdir, zipFileName: binaryAssetName(version, item), log }),
225-
);
269+
const artifacts = compiled.map((item) => packOne(item, version, outdir));
226270
writeChecksums(outdir, artifacts);
227271

228272
const extras = ["SHA256SUMS"];
@@ -231,6 +275,9 @@ export function buildBinaryArtifacts(rawOptions = {}) {
231275
log(`\nBuilt ${artifacts.length} zip(s):`);
232276
for (const item of artifacts) {
233277
log(` ${item.fileName} ${item.sha256.slice(0, 12)}… (inner ${item.innerName})`);
278+
if (item.tarFileName) {
279+
log(` ${item.tarFileName} ${item.tarSha256.slice(0, 12)}…`);
280+
}
234281
}
235282
log(`Also wrote ${extras.join(", ")}`);
236283
return {

tools/release/lib/binary-release.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
* Publish bailian-cli binary assets to GitHub Releases.
33
*
44
* stable: release `v<version>` (tag must already be on origin; --verify-tag)
5-
* assets: bl-*.zip, SHA256SUMS (no latest.json on the GH release)
5+
* assets: bl-*.zip, unix bl-*.tar.gz, SHA256SUMS (no latest.json on the GH release)
66
* OSS: rewrite release/manifest.json + latest.json from build latest.json
7-
* channel: versioned prerelease `v<betaVersion>` (assets: bl-*.zip, SHA256SUMS)
7+
* channel: versioned prerelease `v<betaVersion>` (assets: bl-*.zip, unix bl-*.tar.gz, SHA256SUMS)
88
* + rolling prerelease `channel-sync-release` holding only sync-release.json
99
* OSS: always overwrite prefix-root sync-release.json
1010
*

0 commit comments

Comments
 (0)