166 lines
5.9 KiB
TypeScript
166 lines
5.9 KiB
TypeScript
#!/usr/bin/env bun
|
||
/**
|
||
* 版本一致性校验脚本(发布前 + 发布后)。
|
||
*
|
||
* 设计目标:
|
||
* 1) 统一“版本真相来源”为 packages/opencode/package.json 的 version;
|
||
* 2) 发布前阻断所有“本地版本不一致”;
|
||
* 3) 发布后阻断所有“registry/安装态版本不一致”。
|
||
*
|
||
* 用法:
|
||
* bun run script/check-version-consistency.ts --phase pre --version 1.1.93
|
||
* bun run script/check-version-consistency.ts --phase post --version 1.1.93
|
||
*/
|
||
import { $ } from "bun"
|
||
import pkg from "../package.json"
|
||
|
||
type Phase = "pre" | "post"
|
||
|
||
function parseArg(flag: string): string | undefined {
|
||
const index = process.argv.indexOf(flag)
|
||
if (index === -1) return undefined
|
||
return process.argv[index + 1]
|
||
}
|
||
|
||
const phase = (parseArg("--phase") ?? "pre") as Phase
|
||
const targetVersion = parseArg("--version") ?? pkg.version
|
||
|
||
if (!["pre", "post"].includes(phase)) {
|
||
console.error(`不支持的 --phase: ${phase},可选值仅为 pre | post`)
|
||
process.exit(1)
|
||
}
|
||
|
||
async function ensureOptionalDependenciesConsistent(version: string) {
|
||
// package.json schema 推断类型里未必包含 optionalDependencies;运行时在 release.sh 中会写入同步。
|
||
const optional =
|
||
(pkg as typeof pkg & { optionalDependencies?: Record<string, string> }).optionalDependencies ?? {}
|
||
const entries = Object.entries(optional).filter(([name]) => name.startsWith("qimingcode-"))
|
||
|
||
if (entries.length === 0) {
|
||
// 当前仓库里 optionalDependencies 由 publish.ts 在 dist 聚合包里动态生成。
|
||
// 因此源码 package.json 允许没有该字段,不应阻断发布。
|
||
console.warn("package.json 未定义 qimingcode-* optionalDependencies,跳过源码 optional 一致性校验。")
|
||
return
|
||
}
|
||
|
||
for (const [name, depVersion] of entries) {
|
||
if (depVersion !== version) {
|
||
console.error(
|
||
`optionalDependencies 版本不一致:${name}=${depVersion},期望 ${version}。` +
|
||
"请先同步 package.json 后再发布。",
|
||
)
|
||
process.exit(1)
|
||
}
|
||
}
|
||
}
|
||
|
||
async function ensureDistBinariesConsistent(version: string) {
|
||
const distPaths = [...new Bun.Glob("*/package.json").scanSync({ cwd: "./dist" })]
|
||
if (distPaths.length === 0) {
|
||
console.error("dist 目录中未找到任何二进制包 package.json。请先执行构建。")
|
||
process.exit(1)
|
||
}
|
||
|
||
for (const rel of distPaths) {
|
||
const file = `./dist/${rel}`
|
||
const distPkg = (await Bun.file(file).json()) as { name: string; version: string }
|
||
if (!distPkg.name.startsWith("qimingcode-")) continue
|
||
if (distPkg.version !== version) {
|
||
console.error(
|
||
`dist 二进制包版本不一致:${distPkg.name}=${distPkg.version},期望 ${version}。` +
|
||
"请重建二进制后再发布。",
|
||
)
|
||
process.exit(1)
|
||
}
|
||
}
|
||
}
|
||
|
||
async function ensureLocalSmokeVersion(version: string) {
|
||
// 选择当前机器最常见的本地产物做烟测(mac arm64),不存在则跳过,不阻断非本机平台构建场景。
|
||
const smokeBin = "./dist/qimingcode-darwin-arm64/bin/opencode"
|
||
if (!(await Bun.file(smokeBin).exists())) {
|
||
console.warn(`未找到本地 smoke 二进制 ${smokeBin},跳过本地版本烟测。`)
|
||
return
|
||
}
|
||
|
||
const out = await $`${smokeBin} --version`.quiet().nothrow()
|
||
if (out.exitCode !== 0) {
|
||
console.error(`本地 smoke 二进制执行失败(exit=${out.exitCode})。`)
|
||
process.exit(1)
|
||
}
|
||
const actual = out.stdout.toString().trim()
|
||
if (actual !== version) {
|
||
console.error(`本地二进制版本不一致:smoke=${actual},期望 ${version}。`)
|
||
process.exit(1)
|
||
}
|
||
}
|
||
|
||
async function ensureRegistryMainVersion(version: string) {
|
||
const check = await $`npm view qimingcode@${version} version --registry=https://registry.npmjs.org/`
|
||
.quiet()
|
||
.nothrow()
|
||
if (check.exitCode !== 0) {
|
||
console.error(`registry 中未查到 qimingcode@${version}。`)
|
||
process.exit(1)
|
||
}
|
||
const actual = check.stdout.toString().trim()
|
||
if (actual !== version) {
|
||
console.error(`registry 主包版本不一致:${actual},期望 ${version}。`)
|
||
process.exit(1)
|
||
}
|
||
}
|
||
|
||
async function ensureRegistryOptionalsComplete(version: string) {
|
||
const result =
|
||
await $`bun run script/verify-registry-complete.ts ${version}`.cwd(process.cwd()).quiet().nothrow()
|
||
if (result.exitCode !== 0) {
|
||
console.error("registry optionalDependencies 完整性校验失败。")
|
||
if (result.stderr.toString().trim()) console.error(result.stderr.toString())
|
||
process.exit(1)
|
||
}
|
||
}
|
||
|
||
async function ensureInstalledCliVersion(version: string) {
|
||
const cmd = [
|
||
"tmpdir=$(mktemp -d)",
|
||
"cd \"$tmpdir\"",
|
||
"npm init -y >/dev/null",
|
||
"npm i \"qimingcode@" + version + "\" --registry=https://registry.npmjs.org/ >/dev/null 2>&1",
|
||
"./node_modules/.bin/qimingcode -v",
|
||
].join(" && ")
|
||
const run = await $`bash -lc ${cmd}`.quiet().nothrow()
|
||
if (run.exitCode !== 0) {
|
||
console.error("安装态 CLI 版本校验执行失败。")
|
||
process.exit(1)
|
||
}
|
||
const actual = run.stdout.toString().trim()
|
||
if (actual !== version) {
|
||
console.error(`安装态 CLI 版本不一致:${actual},期望 ${version}。`)
|
||
process.exit(1)
|
||
}
|
||
}
|
||
|
||
async function runPre(version: string) {
|
||
if (pkg.version !== version) {
|
||
console.error(`package.json 版本不一致:${pkg.version},期望 ${version}。`)
|
||
process.exit(1)
|
||
}
|
||
await ensureOptionalDependenciesConsistent(version)
|
||
await ensureDistBinariesConsistent(version)
|
||
await ensureLocalSmokeVersion(version)
|
||
console.log(`pre 校验通过:本地主包/optional/dist/烟测版本均为 ${version}`)
|
||
}
|
||
|
||
async function runPost(version: string) {
|
||
await ensureRegistryMainVersion(version)
|
||
await ensureRegistryOptionalsComplete(version)
|
||
await ensureInstalledCliVersion(version)
|
||
console.log(`post 校验通过:registry 与安装态 CLI 版本均为 ${version}`)
|
||
}
|
||
|
||
if (phase === "pre") {
|
||
await runPre(targetVersion)
|
||
} else {
|
||
await runPost(targetVersion)
|
||
}
|