mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
fix(release): tolerate legacy installed plugin min host floors
This commit is contained in:
@@ -1528,6 +1528,41 @@ describe("loadPluginManifestRegistry", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts legacy bare minHostVersion metadata for recorded installed globals", () => {
|
||||
const dir = makeTempDir();
|
||||
writeManifest(dir, { id: "codex", configSchema: { type: "object" } });
|
||||
|
||||
const registry = loadPluginManifestRegistry({
|
||||
installRecords: {
|
||||
codex: {
|
||||
source: "npm",
|
||||
installPath: dir,
|
||||
},
|
||||
},
|
||||
candidates: [
|
||||
createPluginCandidate({
|
||||
idHint: "codex",
|
||||
rootDir: dir,
|
||||
packageDir: dir,
|
||||
origin: "global",
|
||||
packageManifest: {
|
||||
install: {
|
||||
npmSpec: "@openclaw/codex",
|
||||
minHostVersion: "2026.3.22",
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(registry.plugins.map((plugin) => plugin.id)).toEqual(["codex"]);
|
||||
expect(
|
||||
registry.diagnostics.some((diag) =>
|
||||
diag.message.includes("openclaw.install.minHostVersion must use"),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "reports bundled plugins as the duplicate winner for auto-discovered globals",
|
||||
|
||||
@@ -617,9 +617,19 @@ export function loadPluginManifestRegistry(
|
||||
continue;
|
||||
}
|
||||
const manifest = manifestRes.manifest;
|
||||
const allowLegacyBareMinHostVersion =
|
||||
candidate.origin === "global" &&
|
||||
matchesInstalledPluginRecord({
|
||||
pluginId: manifest.id,
|
||||
candidate,
|
||||
config,
|
||||
env,
|
||||
installRecords: getInstallRecords(),
|
||||
});
|
||||
const minHostVersionCheck = checkMinHostVersion({
|
||||
currentVersion: currentHostVersion,
|
||||
minHostVersion: candidate.packageManifest?.install?.minHostVersion,
|
||||
allowLegacyBareSemver: allowLegacyBareMinHostVersion,
|
||||
});
|
||||
if (!minHostVersionCheck.ok) {
|
||||
const packageManifestSource = path.join(
|
||||
|
||||
@@ -59,6 +59,26 @@ describe("min-host-version", () => {
|
||||
expect(parseMinHostVersionRequirement(">=2026.3.22")).toEqual(MIN_HOST_REQUIREMENT);
|
||||
});
|
||||
|
||||
it("can parse legacy bare semver floors for runtime upgrade compatibility", () => {
|
||||
expect(parseMinHostVersionRequirement("2026.3.22", { allowLegacyBareSemver: true })).toEqual({
|
||||
raw: "2026.3.22",
|
||||
minimumLabel: "2026.3.22",
|
||||
});
|
||||
expect(
|
||||
checkMinHostVersion({
|
||||
currentVersion: "2026.3.22",
|
||||
minHostVersion: "2026.3.22",
|
||||
allowLegacyBareSemver: true,
|
||||
}),
|
||||
).toEqual({
|
||||
ok: true,
|
||||
requirement: {
|
||||
raw: "2026.3.22",
|
||||
minimumLabel: "2026.3.22",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.each(["2026.3.22", 123, ">=2026.3.22 garbage"] as const)(
|
||||
"rejects invalid floor syntax and host checks: %p",
|
||||
(minHostVersion) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { isAtLeast, parseSemver } from "../infra/runtime-guard.js";
|
||||
export const MIN_HOST_VERSION_FORMAT =
|
||||
'openclaw.install.minHostVersion must use a semver floor in the form ">=x.y.z"';
|
||||
const MIN_HOST_VERSION_RE = /^>=(\d+)\.(\d+)\.(\d+)$/;
|
||||
const LEGACY_MIN_HOST_VERSION_RE = /^(\d+)\.(\d+)\.(\d+)$/;
|
||||
|
||||
export type MinHostVersionRequirement = {
|
||||
raw: string;
|
||||
@@ -22,7 +23,10 @@ export type MinHostVersionCheckResult =
|
||||
currentVersion: string;
|
||||
};
|
||||
|
||||
export function parseMinHostVersionRequirement(raw: unknown): MinHostVersionRequirement | null {
|
||||
export function parseMinHostVersionRequirement(
|
||||
raw: unknown,
|
||||
options: { allowLegacyBareSemver?: boolean } = {},
|
||||
): MinHostVersionRequirement | null {
|
||||
if (typeof raw !== "string") {
|
||||
return null;
|
||||
}
|
||||
@@ -30,7 +34,9 @@ export function parseMinHostVersionRequirement(raw: unknown): MinHostVersionRequ
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
const match = trimmed.match(MIN_HOST_VERSION_RE);
|
||||
const match =
|
||||
trimmed.match(MIN_HOST_VERSION_RE) ??
|
||||
(options.allowLegacyBareSemver ? trimmed.match(LEGACY_MIN_HOST_VERSION_RE) : null);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
@@ -54,11 +60,14 @@ export function validateMinHostVersion(raw: unknown): string | null {
|
||||
export function checkMinHostVersion(params: {
|
||||
currentVersion: string | undefined;
|
||||
minHostVersion: unknown;
|
||||
allowLegacyBareSemver?: boolean;
|
||||
}): MinHostVersionCheckResult {
|
||||
if (params.minHostVersion === undefined) {
|
||||
return { ok: true, requirement: null };
|
||||
}
|
||||
const requirement = parseMinHostVersionRequirement(params.minHostVersion);
|
||||
const requirement = parseMinHostVersionRequirement(params.minHostVersion, {
|
||||
allowLegacyBareSemver: params.allowLegacyBareSemver,
|
||||
});
|
||||
if (!requirement) {
|
||||
return { ok: false, kind: "invalid", error: MIN_HOST_VERSION_FORMAT };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user