fix: skip workspace plugin runtime deps

This commit is contained in:
Peter Steinberger
2026-04-21 07:30:39 +01:00
parent aacae4ce62
commit dc6ecd571a
4 changed files with 86 additions and 8 deletions

View File

@@ -43,8 +43,10 @@ describe("doctor bundled plugin runtime deps", () => {
writeJson(path.join(root, "dist", "extensions", "alpha", "package.json"), {
dependencies: {
"@openclaw/plugin-sdk": "workspace:*",
"dep-one": "1.0.0",
"@scope/dep-two": "2.0.0",
openclaw: "workspace:*",
},
optionalDependencies: {
"dep-opt": "3.0.0",

View File

@@ -262,6 +262,72 @@ describe("ensureBundledPluginRuntimeDeps", () => {
]);
});
it("skips workspace-only runtime deps before npm install", () => {
const packageRoot = makeTempDir();
const extensionsRoot = path.join(packageRoot, "dist", "extensions");
const pluginRoot = path.join(extensionsRoot, "qa-channel");
fs.mkdirSync(pluginRoot, { recursive: true });
fs.writeFileSync(
path.join(pluginRoot, "package.json"),
JSON.stringify({
dependencies: {
"@openclaw/plugin-sdk": "workspace:*",
"external-runtime": "^1.2.3",
openclaw: "workspace:*",
},
}),
);
const calls: BundledRuntimeDepsInstallParams[] = [];
const result = ensureBundledPluginRuntimeDeps({
env: {},
installDeps: (params) => {
calls.push(params);
},
pluginId: "qa-channel",
pluginRoot,
});
expect(result).toEqual({
installedSpecs: ["external-runtime@^1.2.3"],
retainSpecs: ["external-runtime@^1.2.3"],
});
expect(calls).toEqual([
{
installRoot: pluginRoot,
missingSpecs: ["external-runtime@^1.2.3"],
installSpecs: ["external-runtime@^1.2.3"],
},
]);
});
it("does not install when runtime deps are only workspace links", () => {
const packageRoot = makeTempDir();
const extensionsRoot = path.join(packageRoot, "dist", "extensions");
const pluginRoot = path.join(extensionsRoot, "qa-channel");
fs.mkdirSync(pluginRoot, { recursive: true });
fs.writeFileSync(
path.join(pluginRoot, "package.json"),
JSON.stringify({
dependencies: {
"@openclaw/plugin-sdk": "workspace:*",
openclaw: "workspace:*",
},
}),
);
const result = ensureBundledPluginRuntimeDeps({
env: {},
installDeps: () => {
throw new Error("workspace-only runtime deps should not install");
},
pluginId: "qa-channel",
pluginRoot,
});
expect(result).toEqual({ installedSpecs: [], retainSpecs: [] });
});
it("skips install when staged plugin-local runtime deps are present", () => {
const packageRoot = makeTempDir();
const extensionsRoot = path.join(packageRoot, "dist", "extensions");

View File

@@ -62,6 +62,17 @@ function collectRuntimeDeps(packageJson: JsonObject): Record<string, unknown> {
};
}
function normalizeInstallableRuntimeDepVersion(rawVersion: unknown): string | null {
if (typeof rawVersion !== "string") {
return null;
}
const version = rawVersion.trim();
if (version === "" || version.toLowerCase().startsWith("workspace:")) {
return null;
}
return version;
}
function isSourceCheckoutRoot(packageRoot: string): boolean {
return (
fs.existsSync(path.join(packageRoot, ".git")) &&
@@ -402,10 +413,10 @@ function collectBundledPluginRuntimeDeps(params: {
continue;
}
for (const [name, rawVersion] of Object.entries(collectRuntimeDeps(packageJson))) {
if (typeof rawVersion !== "string" || rawVersion.trim() === "") {
const version = normalizeInstallableRuntimeDepVersion(rawVersion);
if (!version) {
continue;
}
const version = rawVersion.trim();
const byVersion = versionMap.get(name) ?? new Map<string, Set<string>>();
const pluginIds = byVersion.get(version) ?? new Set<string>();
pluginIds.add(pluginId);
@@ -549,11 +560,10 @@ export function ensureBundledPluginRuntimeDeps(params: {
return { installedSpecs: [], retainSpecs: [] };
}
const deps = Object.entries(collectRuntimeDeps(packageJson))
.map(([name, rawVersion]) =>
typeof rawVersion === "string" && rawVersion.trim() !== ""
? { name, version: rawVersion.trim() }
: null,
)
.map(([name, rawVersion]) => {
const version = normalizeInstallableRuntimeDepVersion(rawVersion);
return version ? { name, version } : null;
})
.filter((entry): entry is { name: string; version: string } => Boolean(entry));
if (deps.length === 0) {
return { installedSpecs: [], retainSpecs: [] };