mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
Merge branch 'main' into feat/self-built-framework
This commit is contained in:
+15
-5
@@ -4,6 +4,7 @@ import { fileURLToPath } from "url";
|
||||
import { packAndScan } from "./lib/pack-scan.mjs";
|
||||
import { run } from "./lib/proc.mjs";
|
||||
import { assertReadmeSync, loadAndValidatePackages } from "./lib/validate.mjs";
|
||||
import { ALL_PACKAGES, PACKAGES } from "./lib/packages.mjs";
|
||||
|
||||
function log(msg = "") {
|
||||
process.stdout.write(`${msg}\n`);
|
||||
@@ -17,25 +18,29 @@ function step(msg) {
|
||||
* Pure-validation pipeline. Reusable from publish-stable / publish-channel.
|
||||
* Returns { coreJson, cliJson } for callers that need the parsed package.jsons.
|
||||
*
|
||||
* @param {{ channel?: boolean }} [options]
|
||||
* @param {{ channel?: boolean, knowledge?: boolean }} [options]
|
||||
* @param {boolean} [options.channel] — When true (publish-channel): regenerate
|
||||
* `reference/` and assert it matches git, but do not sync `SKILL.md` from the
|
||||
* temporary beta `package.json` version (repo skill stays aligned with stable).
|
||||
* @param {boolean} [options.knowledge] — When true: also build and validate
|
||||
* knowledge-studio-cli alongside the base packages.
|
||||
*/
|
||||
export async function runCheck(options = {}) {
|
||||
const channel = options.channel === true;
|
||||
const knowledge = options.knowledge === true;
|
||||
const packages = knowledge ? ALL_PACKAGES : PACKAGES;
|
||||
|
||||
step("pnpm install --frozen-lockfile");
|
||||
run("pnpm", ["install", "--frozen-lockfile"]);
|
||||
|
||||
step("metadata: README sync, version consistency, workspace:* dep");
|
||||
assertReadmeSync();
|
||||
const { coreJson, cliJson } = loadAndValidatePackages();
|
||||
const { coreJson, cliJson } = loadAndValidatePackages({ packages });
|
||||
log(`bailian-cli-core@${coreJson.version}`);
|
||||
log(`bailian-cli@${cliJson.version}`);
|
||||
|
||||
step("build bailian-cli-core");
|
||||
run("pnpm", ["--filter", "bailian-cli-core", "run", "build"]);
|
||||
step("build bailian-cli dependencies (core, commands, runtime)");
|
||||
run("pnpm", ["--filter", "bailian-cli^...", "run", "build"]);
|
||||
|
||||
step(
|
||||
channel
|
||||
@@ -63,8 +68,13 @@ export async function runCheck(options = {}) {
|
||||
step("build bailian-cli");
|
||||
run("pnpm", ["--filter", "bailian-cli", "run", "build"]);
|
||||
|
||||
if (knowledge) {
|
||||
step("build knowledge-studio-cli");
|
||||
run("pnpm", ["--filter", "knowledge-studio-cli", "run", "build"]);
|
||||
}
|
||||
|
||||
step("pack + scan (publint, gitleaks)");
|
||||
packAndScan({ log });
|
||||
packAndScan({ log, packages });
|
||||
|
||||
log("\nrelease check passed.");
|
||||
return { coreJson, cliJson };
|
||||
|
||||
@@ -13,10 +13,11 @@ function extractTarball(tarball, tempDir, key) {
|
||||
return extractDir;
|
||||
}
|
||||
|
||||
export function packAndScan({ log }) {
|
||||
export function packAndScan({ log, packages }) {
|
||||
const pkgs = packages ?? PACKAGES;
|
||||
const tempDir = mkdtempSync(join(tmpdir(), "bailian-release-"));
|
||||
try {
|
||||
for (const pkg of PACKAGES) {
|
||||
for (const pkg of pkgs) {
|
||||
const json = readPackageJson(pkg);
|
||||
log(`packing ${pkg.name}@${json.version}`);
|
||||
const tarball = pnpmPack(pkg, tempDir, json);
|
||||
|
||||
@@ -6,9 +6,16 @@ export const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../..")
|
||||
|
||||
export const PACKAGES = [
|
||||
{ key: "core", dir: "packages/core", name: "bailian-cli-core" },
|
||||
{ key: "runtime", dir: "packages/runtime", name: "bailian-cli-runtime" },
|
||||
{ key: "commands", dir: "packages/commands", name: "bailian-cli-commands" },
|
||||
{ key: "cli", dir: "packages/cli", name: "bailian-cli" },
|
||||
];
|
||||
|
||||
// knowledge-studio-cli shares the same library deps as bailian-cli.
|
||||
// Published via publish.yml with package=knowledge-studio-cli (passes --knowledge flag).
|
||||
export const KSCLI_PACKAGE = { key: "kscli", dir: "packages/kscli", name: "knowledge-studio-cli" };
|
||||
export const ALL_PACKAGES = [...PACKAGES, KSCLI_PACKAGE];
|
||||
|
||||
export function readJson(path) {
|
||||
return JSON.parse(readFileSync(path, "utf-8"));
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ export function assertReadmeSync() {
|
||||
}
|
||||
}
|
||||
|
||||
export function loadAndValidatePackages() {
|
||||
export function loadAndValidatePackages({ packages } = {}) {
|
||||
const pkgs = packages ?? PACKAGES;
|
||||
const internalNames = new Set(pkgs.map((p) => p.name));
|
||||
const jsonByKey = new Map();
|
||||
for (const pkg of PACKAGES) {
|
||||
for (const pkg of pkgs) {
|
||||
const json = readPackageJson(pkg);
|
||||
if (json.name !== pkg.name) {
|
||||
throw new Error(`${pkg.dir} name must be ${pkg.name}, got ${json.name}`);
|
||||
@@ -30,18 +32,23 @@ export function loadAndValidatePackages() {
|
||||
|
||||
const coreJson = jsonByKey.get("core");
|
||||
const cliJson = jsonByKey.get("cli");
|
||||
const version = coreJson.version;
|
||||
|
||||
if (cliJson.version !== coreJson.version) {
|
||||
throw new Error(
|
||||
`core and cli versions must match, got ${coreJson.version} and ${cliJson.version}.`,
|
||||
);
|
||||
}
|
||||
|
||||
const cliCoreDep = cliJson.dependencies?.["bailian-cli-core"];
|
||||
if (cliCoreDep !== "workspace:*") {
|
||||
throw new Error(
|
||||
`packages/cli source dependency on bailian-cli-core must be "workspace:*", got ${cliCoreDep}.`,
|
||||
);
|
||||
for (const pkg of pkgs) {
|
||||
const json = jsonByKey.get(pkg.key);
|
||||
if (json.version !== version) {
|
||||
throw new Error(
|
||||
`all package versions must match ${version} (bailian-cli-core), ` +
|
||||
`but ${pkg.name} is ${json.version}.`,
|
||||
);
|
||||
}
|
||||
for (const [dep, range] of Object.entries(json.dependencies ?? {})) {
|
||||
if (internalNames.has(dep) && range !== "workspace:*") {
|
||||
throw new Error(
|
||||
`${pkg.name} dependency on ${dep} must be "workspace:*", got ${String(range)}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { coreJson, cliJson };
|
||||
|
||||
@@ -6,7 +6,8 @@ import { runCheck } from "./check.mjs";
|
||||
import { headSha7, utcDateStamp } from "./lib/git.mjs";
|
||||
import { npmViewExists, pnpmPublish } from "./lib/npm.mjs";
|
||||
import {
|
||||
findPackage,
|
||||
ALL_PACKAGES,
|
||||
PACKAGES,
|
||||
packageJsonPath,
|
||||
readPackageJson,
|
||||
writePackageJson,
|
||||
@@ -25,11 +26,14 @@ const { values } = parseArgs({
|
||||
options: {
|
||||
channel: { type: "string" },
|
||||
"dry-run": { type: "boolean", default: false },
|
||||
knowledge: { type: "boolean", default: false },
|
||||
},
|
||||
allowPositionals: false,
|
||||
});
|
||||
const channel = values.channel;
|
||||
const dryRun = values["dry-run"];
|
||||
const knowledge = values.knowledge;
|
||||
const packages = knowledge ? ALL_PACKAGES : PACKAGES;
|
||||
assertChannel(channel);
|
||||
|
||||
if (!dryRun && !process.env.CI) {
|
||||
@@ -37,16 +41,15 @@ if (!dryRun && !process.env.CI) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const core = findPackage("core");
|
||||
const cli = findPackage("cli");
|
||||
const corePath = packageJsonPath(core);
|
||||
const cliPath = packageJsonPath(cli);
|
||||
const coreOriginal = readFileSync(corePath, "utf-8");
|
||||
const cliOriginal = readFileSync(cliPath, "utf-8");
|
||||
// Snapshot every package.json so the temporary version bump is reverted in
|
||||
// `finally`, even when the release fails midway.
|
||||
const originals = packages.map((pkg) => {
|
||||
const path = packageJsonPath(pkg);
|
||||
return { pkg, path, content: readFileSync(path, "utf-8") };
|
||||
});
|
||||
|
||||
function restoreOriginals() {
|
||||
writeFileSync(corePath, coreOriginal);
|
||||
writeFileSync(cliPath, cliOriginal);
|
||||
for (const { path, content } of originals) writeFileSync(path, content);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -57,32 +60,29 @@ try {
|
||||
log(`channel=${channel} version=${betaVersion}`);
|
||||
|
||||
step("temporarily bump package.json (not committed)");
|
||||
const coreJson = readPackageJson(core);
|
||||
const cliJson = readPackageJson(cli);
|
||||
coreJson.version = betaVersion;
|
||||
cliJson.version = betaVersion;
|
||||
writePackageJson(core, coreJson);
|
||||
writePackageJson(cli, cliJson);
|
||||
// pnpm pack resolves `workspace:*` to the in-tree version, so CLI tarball
|
||||
// will depend on bailian-cli-core@<betaVersion> after this bump.
|
||||
for (const pkg of packages) {
|
||||
const json = readPackageJson(pkg);
|
||||
json.version = betaVersion;
|
||||
writePackageJson(pkg, json);
|
||||
}
|
||||
|
||||
await runCheck({ channel: true });
|
||||
await runCheck({ channel: true, knowledge });
|
||||
|
||||
step(`idempotency: check ${betaVersion} against registry`);
|
||||
const corePublished = npmViewExists(core.name, betaVersion);
|
||||
const cliPublished = npmViewExists(cli.name, betaVersion);
|
||||
log(`${core.name}@${betaVersion}: ${corePublished ? "already published" : "to publish"}`);
|
||||
log(`${cli.name}@${betaVersion}: ${cliPublished ? "already published" : "to publish"}`);
|
||||
if (corePublished && cliPublished) {
|
||||
log("\nboth packages already published; nothing to do.");
|
||||
const published = new Map();
|
||||
for (const pkg of packages) {
|
||||
const exists = npmViewExists(pkg.name, betaVersion);
|
||||
published.set(pkg.key, exists);
|
||||
log(`${pkg.name}@${betaVersion}: ${exists ? "already published" : "to publish"}`);
|
||||
}
|
||||
if (packages.every((pkg) => published.get(pkg.key))) {
|
||||
log("\nall packages already published; nothing to do.");
|
||||
} else {
|
||||
if (!corePublished) {
|
||||
step(`publish ${core.name}@${betaVersion} (tag=${channel}, provenance)`);
|
||||
pnpmPublish(core, { tag: channel, provenance: true, dryRun });
|
||||
}
|
||||
if (!cliPublished) {
|
||||
step(`publish ${cli.name}@${betaVersion} (tag=${channel}, provenance)`);
|
||||
pnpmPublish(cli, { tag: channel, provenance: true, dryRun });
|
||||
// Publish in dependency order (core → runtime → commands → cli [→ kscli]).
|
||||
for (const pkg of packages) {
|
||||
if (published.get(pkg.key)) continue;
|
||||
step(`publish ${pkg.name}@${betaVersion} (tag=${channel}, provenance)`);
|
||||
pnpmPublish(pkg, { tag: channel, provenance: true, dryRun });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { parseArgs } from "util";
|
||||
import { runCheck } from "./check.mjs";
|
||||
import { createTag, currentBranch, isWorkingTreeClean, pushTag, tagExists } from "./lib/git.mjs";
|
||||
import { npmViewExists, pnpmPublish } from "./lib/npm.mjs";
|
||||
import { findPackage } from "./lib/packages.mjs";
|
||||
import { ALL_PACKAGES, PACKAGES } from "./lib/packages.mjs";
|
||||
|
||||
function log(msg = "") {
|
||||
process.stdout.write(`${msg}\n`);
|
||||
@@ -17,10 +17,13 @@ function step(msg) {
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
"dry-run": { type: "boolean", default: false },
|
||||
knowledge: { type: "boolean", default: false },
|
||||
},
|
||||
allowPositionals: false,
|
||||
});
|
||||
const dryRun = values["dry-run"];
|
||||
const knowledge = values.knowledge;
|
||||
const packages = knowledge ? ALL_PACKAGES : PACKAGES;
|
||||
|
||||
try {
|
||||
if (!dryRun && !process.env.CI) {
|
||||
@@ -40,28 +43,26 @@ try {
|
||||
log("[dry-run] skipping working-tree + branch preflight");
|
||||
}
|
||||
|
||||
const { coreJson } = await runCheck();
|
||||
const version = coreJson.version; // === cliJson.version, asserted by runCheck
|
||||
const { coreJson } = await runCheck({ knowledge });
|
||||
const version = coreJson.version; // all packages share this, asserted by runCheck
|
||||
|
||||
step(`idempotency: check ${version} against registry`);
|
||||
const core = findPackage("core");
|
||||
const cli = findPackage("cli");
|
||||
const corePublished = npmViewExists(core.name, version);
|
||||
const cliPublished = npmViewExists(cli.name, version);
|
||||
log(`${core.name}@${version}: ${corePublished ? "already published" : "to publish"}`);
|
||||
log(`${cli.name}@${version}: ${cliPublished ? "already published" : "to publish"}`);
|
||||
if (corePublished && cliPublished) {
|
||||
log("\nboth packages already published; nothing to do.");
|
||||
const published = new Map();
|
||||
for (const pkg of packages) {
|
||||
const exists = npmViewExists(pkg.name, version);
|
||||
published.set(pkg.key, exists);
|
||||
log(`${pkg.name}@${version}: ${exists ? "already published" : "to publish"}`);
|
||||
}
|
||||
if (packages.every((pkg) => published.get(pkg.key))) {
|
||||
log("\nall packages already published; nothing to do.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!corePublished) {
|
||||
step(`publish ${core.name}@${version} (tag=latest, provenance)`);
|
||||
pnpmPublish(core, { tag: "latest", provenance: true, dryRun });
|
||||
}
|
||||
if (!cliPublished) {
|
||||
step(`publish ${cli.name}@${version} (tag=latest, provenance)`);
|
||||
pnpmPublish(cli, { tag: "latest", provenance: true, dryRun });
|
||||
// Publish in dependency order (core → runtime → commands → cli [→ kscli]).
|
||||
for (const pkg of packages) {
|
||||
if (published.get(pkg.key)) continue;
|
||||
step(`publish ${pkg.name}@${version} (tag=latest, provenance)`);
|
||||
pnpmPublish(pkg, { tag: "latest", provenance: true, dryRun });
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
|
||||
Reference in New Issue
Block a user