build: multi channel install test

This commit is contained in:
rendianmeng
2026-07-23 17:36:14 +08:00
parent 66a797203c
commit 08bdc3be97
3 changed files with 0 additions and 383 deletions
-171
View File
@@ -1,171 +0,0 @@
# 技术方案:GitHub Release 发布 + 外部 FC 同步 OSS 安装
> 状态:**已定架构**(本仓库不直传 OSS;安装脚本外置;**不含 Homebrew**)。
> 范围:仅 `bailian-cli`(`bl`);不含 `kscli` 二进制。
> 操作清单:[docs/agents/binary-distribution.md](../agents/binary-distribution.md)
> 发版编排:[docs/agents/publish.md](../agents/publish.md)
---
## 1. 目标架构
```text
Publish workflow(本仓库)
├─ npm
└─ GitHub Release(source of truth)
↓
调用外部 FC:拉取最新 Release → 同步 OSS
↓
别处维护的 install.sh / install.ps1 → 只拉 OSS
```
| 环节 | 谁负责 | 本仓库是否实现 |
| ------------------------- | ------------ | ------------------------------- |
| npm publish | 本仓库 CI | ✅ |
| Bun 编译 + GitHub Release | 本仓库 CI | ✅ |
| Release → OSS 同步 | **外部 FC** | ❌(仅可选 webhook 触发) |
| 生产安装脚本 | **别处维护** | ❌(`packaging/` 仅作契约参考) |
| 用户 curl / irm | OSS 上的脚本 | ❌ |
本仓库**不存放、不上传 OSS AccessKey**;开源侧只用 `GITHUB_TOKEN`。
---
## 2. 本仓库发版(Publish)
入口:`.github/workflows/publish.yml` → `publish-stable.mjs` / `publish-channel.mjs`。
```text
stable:
check → npm latest → git tag v<ver> → gh release(正式)
assets: bl-*, SHA256SUMS, latest.json
(不再把 install.sh/ps1 挂到 Release;生产脚本外置)
channel:
bump beta → npm @channel → gh prerelease v<beta>
+ 滚动 prerelease tag channel-<name>(仅 <name>.json)
```
可选:发版成功后 `POST` `BAILIAN_OSS_SYNC_WEBHOOK`,通知 FC 开始同步(Secret 配置,无则跳过)。
`--skip-binary`:只发 npm。
### 构建矩阵
| Bun target | 产物 |
| ------------------ | ----------------- |
| `bun-darwin-arm64` | `darwin-arm64` |
| `bun-darwin-x64` | `darwin-x64` |
| `bun-linux-x64` | `linux-x64` |
| `bun-windows-x64` | `windows-x64.exe` |
不构建 `linux-arm64` / `windows-arm64`。
---
## 3. GitHub Release 约定(source of truth)
基址:`https://github.com/modelstudioai/cli/releases`
| 模式 | Tag | 资产 |
| -------------- | ---------------------------------------------- | --------------------------------------- |
| stable | `v<version>`(先 push tag,再 `--verify-tag`) | 矩阵二进制、`SHA256SUMS`、`latest.json` |
| channel 版本化 | `v0.0.0-beta-<sha7>-<date>`(prerelease) | 二进制、`SHA256SUMS` |
| channel 滚动 | `channel-<name>`(prerelease,clobber) | `<name>.json` |
Manifest 内 `url` 指向 GitHub download(给 FC / 镜像方解析用)。FC 同步到 OSS 时应**改写**为 OSS URL,或安装脚本忽略 `url`、按固定 OSS 路径拼接。
---
## 4. 外部 FC(本仓库不实现)
建议契约:
1. 触发:Publish webhook,或监听 `release` 事件 / 定时拉取 Latest
2. 读取 GitHub Latest(stable)或约定 channel tag
3. 下载资产 → 上传 OSS,建议布局:
```text
https://bailian-cli.oss-cn-hangzhou.aliyuncs.com/bailian-cli/
channels/latest.json # 改写 url 后的正式版 manifest
channels/<channel>.json # 可选测试渠道
releases/<version>/
bl-<version>-<os>-<arch>[.exe]
SHA256SUMS
install.sh # 由脚本维护方上传,非本仓库 CI
install.ps1
```
4. **完整校验后再对外**:SHA256 对齐、矩阵文件齐全,避免「Release 已发、OSS 半同步」窗口误导用户
5. 失败告警(钉钉/飞书/SLS),因用户安装不经过本仓库 CI
---
## 5. 安装脚本(别处维护)
生产入口(示例,以实际 OSS 域名为准):
```bash
curl -fsSL https://bailian-cli.oss-cn-hangzhou.aliyuncs.com/bailian-cli/install.sh | bash
# Windows:
irm https://bailian-cli.oss-cn-hangzhou.aliyuncs.com/bailian-cli/install.ps1 | iex
```
脚本只依赖 OSS;不要求用户访问 GitHub。
本仓 `packaging/install.sh` / `install.ps1`:**契约参考**(路径、校验、`install-method=binary`),不作为生产分发物挂 Release。
### 与本仓的契约(脚本方必须遵守)
- 资产名:`bl-<ver>-<os>-<arch>[.exe]`
- 校验:`SHA256SUMS`
- 渠道:默认 `latest`;可选 `BAILIAN_CHANNEL`
- 写入:`~/.bailian/install-method` = `binary`
- 不支持:linux-arm64 / windows-arm64 → 提示 `npm i -g bailian-cli`
---
## 6. 运行时(本仓库)
- `BAILIAN_COMPILED=1`、install-method、`bl update` 分流仍在本仓
- 二进制更新默认读 **OSS** manifest(`BAILIAN_CLI_CDN` 可覆盖);与用户安装源一致
- GitHub Release 仍是发版真相源;更新链路走 OSS 镜像
---
## 7. 职责边界(验收标准)
| 验收项 | 通过条件 |
| ----------------- | ----------------------------------------------------- |
| 本仓库 Publish 绿 | npm 可装 + GitHub Release 资产齐全 |
| 国内可一键安装 | FC 已同步 + 外置脚本可 curl(**不在本仓 CI 门禁内**) |
| 无 AK 进 git | OSS 密钥只在 FC / 脚本发布流水线 |
---
## 8. 风险
- Release 成功 ≠ 用户能装(依赖 FC)→ 必须有同步监控
- 脚本外置 → 命名/矩阵变更要同步通知脚本方
- `latest.json` 双份(GH / OSS)→ FC 负责改写与一致性
- channel 是否进 OSS:由 FC 与脚本方另定;本仓照常发 prerelease
---
## 9. 本仓库代码落点
| 路径 | 职责 |
| --------------------------------------------- | ------------------------------------- |
| `publish-stable.mjs` / `publish-channel.mjs` | npm + Release + 可选 webhook |
| `lib/binary-build.mjs` / `binary-compile.mjs` | 编译与 manifest |
| `lib/binary-release.mjs` | `gh release`(不含生产 install 脚本) |
| `packaging/*` | 安装契约参考 |
| `.github/workflows/publish.yml` | CI |
调试:
```sh
node tools/release/lib/binary-build.mjs --mode stable --host
node tools/release/lib/binary-release.mjs --mode stable --skip-build --dry-run
node tools/release/publish-stable.mjs --dry-run
```
-78
View File
@@ -1,78 +0,0 @@
# REFERENCE install script for the OSS layout (production scripts are maintained elsewhere).
# Expected production entry:
# irm https://bailian-cli.oss-cn-hangzhou.aliyuncs.com/bailian-cli/install.ps1 | iex
param()
$ErrorActionPreference = "Stop"
$CdnBase = if ($env:BAILIAN_CLI_CDN) { $env:BAILIAN_CLI_CDN.TrimEnd("/") } else { "https://bailian-cli.oss-cn-hangzhou.aliyuncs.com/bailian-cli" }
$Channel = if ($env:BAILIAN_CHANNEL) { $env:BAILIAN_CHANNEL } else { "latest" }
$Version = $env:BAILIAN_VERSION
$InstallRoot = if ($env:BAILIAN_SHARE_DIR) { $env:BAILIAN_SHARE_DIR } else { Join-Path $env:LOCALAPPDATA "bailian-cli" }
$ConfigDir = if ($env:BAILIAN_CONFIG_DIR) { $env:BAILIAN_CONFIG_DIR } else { Join-Path $env:USERPROFILE ".bailian" }
function Write-Log([string]$Message) {
[Console]::Error.WriteLine($Message)
}
if ([string]::IsNullOrWhiteSpace($Version)) {
$manifestUrl = "$CdnBase/channels/$Channel.json"
Write-Log "Fetching $manifestUrl"
$manifest = Invoke-RestMethod -Uri $manifestUrl
$Version = $manifest.version
if ([string]::IsNullOrWhiteSpace($Version)) {
throw "Could not parse version from $Channel channel manifest"
}
}
$Arch = if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { "arm64" } else { "x64" }
if ($Arch -eq "arm64") {
throw "windows arm64 is not supported for binary install; use: npm install -g bailian-cli"
}
$Asset = "bl-$Version-windows-$Arch.exe"
$Url = "$CdnBase/releases/$Version/$Asset"
$SumsUrl = "$CdnBase/releases/$Version/SHA256SUMS"
$TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("bailian-cli-install-" + [guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $TempDir | Out-Null
try {
$AssetPath = Join-Path $TempDir $Asset
$SumsPath = Join-Path $TempDir "SHA256SUMS"
Write-Log "Downloading $Asset…"
Invoke-WebRequest -Uri $Url -OutFile $AssetPath
Invoke-WebRequest -Uri $SumsUrl -OutFile $SumsPath
$Expected = $null
Get-Content $SumsPath | ForEach-Object {
if ($_ -match "^([a-fA-F0-9]+)\s+$([regex]::Escape($Asset))\s*$") {
$Expected = $Matches[1].ToLowerInvariant()
}
}
if (-not $Expected) { throw "Checksum for $Asset not found in SHA256SUMS" }
$Actual = (Get-FileHash -Algorithm SHA256 -Path $AssetPath).Hash.ToLowerInvariant()
if ($Expected -ne $Actual) { throw "Checksum mismatch for $Asset" }
$VersionDir = Join-Path $InstallRoot "versions\$Version"
New-Item -ItemType Directory -Force -Path $VersionDir | Out-Null
New-Item -ItemType Directory -Force -Path $ConfigDir | Out-Null
$Target = Join-Path $VersionDir "bl.exe"
Copy-Item -Force $AssetPath $Target
$ShimDir = Join-Path $InstallRoot "bin"
New-Item -ItemType Directory -Force -Path $ShimDir | Out-Null
Copy-Item -Force $Target (Join-Path $ShimDir "bl.exe")
Copy-Item -Force $Target (Join-Path $ShimDir "bailian.exe")
Set-Content -Path (Join-Path $ConfigDir "install-method") -Value "binary" -NoNewline
$UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
if (-not ($UserPath -split ";" | Where-Object { $_ -eq $ShimDir })) {
[Environment]::SetEnvironmentVariable("Path", "$ShimDir;$UserPath", "User")
Write-Log "Added $ShimDir to your user PATH. Open a new terminal to use bl."
}
Write-Log "Installed: $Target (bailian-cli $Version)"
Write-Log "Done. Run: bl --help"
}
finally {
Remove-Item -Recurse -Force $TempDir -ErrorAction SilentlyContinue
}
-134
View File
@@ -1,134 +0,0 @@
#!/usr/bin/env bash
# REFERENCE install script for the OSS layout (production scripts are maintained elsewhere).
# Production users should curl the copy hosted on OSS after FC sync, not this file from git.
#
# Expected production entry:
# curl -fsSL https://bailian-cli.oss-cn-hangzhou.aliyuncs.com/bailian-cli/install.sh | bash
# BAILIAN_VERSION=1.10.1 bash install.sh
# BAILIAN_CHANNEL=latest bash install.sh
set -euo pipefail
CDN_BASE="${BAILIAN_CLI_CDN:-https://bailian-cli.oss-cn-hangzhou.aliyuncs.com/bailian-cli}"
CDN_BASE="${CDN_BASE%/}"
CHANNEL="${BAILIAN_CHANNEL:-latest}"
VERSION="${BAILIAN_VERSION:-}"
BIN_DIR="${BAILIAN_BIN_DIR:-${HOME}/.local/bin}"
SHARE_DIR="${BAILIAN_SHARE_DIR:-${HOME}/.local/share/bailian-cli}"
CONFIG_DIR="${BAILIAN_CONFIG_DIR:-${HOME}/.bailian}"
log() { printf '%s\n' "$*" >&2; }
die() { log "error: $*"; exit 1; }
need_cmd() {
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
}
detect_os_arch() {
local uname_s uname_m
uname_s="$(uname -s)"
uname_m="$(uname -m)"
case "${uname_s}" in
Darwin) OS="darwin" ;;
Linux) OS="linux" ;;
*) die "unsupported OS: ${uname_s}" ;;
esac
case "${uname_m}" in
arm64|aarch64) ARCH="arm64" ;;
x86_64|amd64) ARCH="x64" ;;
*) die "unsupported architecture: ${uname_m}" ;;
esac
if [[ "${OS}" == "darwin" ]] && [[ "$(sysctl -n sysctl.proc_translated 2>/dev/null || true)" == "1" ]]; then
ARCH="arm64"
fi
if [[ "${OS}" == "linux" && "${ARCH}" == "arm64" ]]; then
die "linux arm64 is not supported for binary install; use: npm install -g bailian-cli"
fi
}
resolve_version() {
if [[ -n "${VERSION}" ]]; then
return
fi
need_cmd curl
local manifest
manifest="$(mktemp)"
curl -fsSL "${CDN_BASE}/channels/${CHANNEL}.json" -o "${manifest}" \
|| die "failed to download channel manifest: ${CDN_BASE}/channels/${CHANNEL}.json"
VERSION="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "${manifest}" | head -n1)"
rm -f "${manifest}"
[[ -n "${VERSION}" ]] || die "could not parse version from ${CHANNEL} channel manifest"
}
download_and_verify() {
local asset="bl-${VERSION}-${OS}-${ARCH}"
local url="${CDN_BASE}/releases/${VERSION}/${asset}"
local sums_url="${CDN_BASE}/releases/${VERSION}/SHA256SUMS"
local tmp_dir version_dir
tmp_dir="$(mktemp -d)"
# shellcheck disable=SC2064
trap "rm -rf '${tmp_dir}'" EXIT
log "Downloading ${asset}…"
curl -fsSL "${url}" -o "${tmp_dir}/${asset}" || die "download failed: ${url}"
curl -fsSL "${sums_url}" -o "${tmp_dir}/SHA256SUMS" || die "download failed: ${sums_url}"
need_cmd shasum
local expected actual
expected="$(awk -v file="${asset}" '$2 == file { print $1; exit }' "${tmp_dir}/SHA256SUMS")"
[[ -n "${expected}" ]] || die "checksum for ${asset} not found in SHA256SUMS"
actual="$(shasum -a 256 "${tmp_dir}/${asset}" | awk '{ print $1 }')"
[[ "${expected}" == "${actual}" ]] || die "checksum mismatch for ${asset}"
version_dir="${SHARE_DIR}/versions/${VERSION}"
mkdir -p "${version_dir}" "${BIN_DIR}" "${CONFIG_DIR}"
install -m 755 "${tmp_dir}/${asset}" "${version_dir}/bl"
ln -sfn "${version_dir}/bl" "${BIN_DIR}/bl"
ln -sfn "${version_dir}/bl" "${BIN_DIR}/bailian"
printf 'binary\n' > "${CONFIG_DIR}/install-method"
chmod 600 "${CONFIG_DIR}/install-method" 2>/dev/null || true
}
warn_path() {
case ":${PATH}:" in
*":${BIN_DIR}:"*) ;;
*)
log ""
log "Add ${BIN_DIR} to your PATH, then open a new terminal:"
log " export PATH=\"${BIN_DIR}:\$PATH\""
;;
esac
}
warn_npm_conflict() {
if command -v npm >/dev/null 2>&1; then
local npm_bl
npm_bl="$(npm root -g 2>/dev/null)/bailian-cli" || true
if [[ -d "${npm_bl}" ]]; then
log "warning: npm global bailian-cli is also installed; PATH may prefer one over the other."
log " Consider: npm uninstall -g bailian-cli"
fi
fi
}
main() {
need_cmd curl
need_cmd uname
detect_os_arch
resolve_version
log "Installing bailian-cli ${VERSION} (${OS}-${ARCH}) from ${CDN_BASE}"
download_and_verify
warn_npm_conflict
warn_path
log ""
log "Installed: ${BIN_DIR}/bl → bailian-cli ${VERSION}"
if command -v "${BIN_DIR}/bl" >/dev/null 2>&1 || [[ -x "${BIN_DIR}/bl" ]]; then
"${BIN_DIR}/bl" --version >&2 || true
fi
log "Done. Run: bl --help"
}
main "$@"