mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:10:44 +00:00
fix: skip redundant bundled runtime dep repairs
This commit is contained in:
@@ -275,6 +275,80 @@ describe("ensureBundledPluginRuntimeDeps", () => {
|
||||
expect(result).toEqual({ installedSpecs: [], retainSpecs: [] });
|
||||
});
|
||||
|
||||
it("skips install when runtime deps resolve from the package root", () => {
|
||||
const packageRoot = makeTempDir();
|
||||
const pluginRoot = path.join(packageRoot, "dist", "extensions", "openai");
|
||||
fs.mkdirSync(path.join(packageRoot, "node_modules", "@mariozechner", "pi-ai"), {
|
||||
recursive: true,
|
||||
});
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "package.json"),
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
"@mariozechner/pi-ai": "0.67.68",
|
||||
},
|
||||
}),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(packageRoot, "node_modules", "@mariozechner", "pi-ai", "package.json"),
|
||||
JSON.stringify({ name: "@mariozechner/pi-ai", version: "0.67.68" }),
|
||||
);
|
||||
|
||||
const result = ensureBundledPluginRuntimeDeps({
|
||||
env: {},
|
||||
installDeps: () => {
|
||||
throw new Error("package-root runtime deps should not reinstall");
|
||||
},
|
||||
pluginId: "openai",
|
||||
pluginRoot,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ installedSpecs: [], retainSpecs: [] });
|
||||
});
|
||||
|
||||
it("installs only deps missing from plugin and package-root resolution", () => {
|
||||
const packageRoot = makeTempDir();
|
||||
const pluginRoot = path.join(packageRoot, "dist", "extensions", "codex");
|
||||
fs.mkdirSync(path.join(packageRoot, "node_modules", "ws"), { recursive: true });
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "package.json"),
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
ws: "^8.20.0",
|
||||
zod: "^4.3.6",
|
||||
},
|
||||
}),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(packageRoot, "node_modules", "ws", "package.json"),
|
||||
JSON.stringify({ name: "ws", version: "8.20.0" }),
|
||||
);
|
||||
const calls: BundledRuntimeDepsInstallParams[] = [];
|
||||
|
||||
const result = ensureBundledPluginRuntimeDeps({
|
||||
env: {},
|
||||
installDeps: (params) => {
|
||||
calls.push(params);
|
||||
},
|
||||
pluginId: "codex",
|
||||
pluginRoot,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
installedSpecs: ["zod@^4.3.6"],
|
||||
retainSpecs: ["ws@^8.20.0", "zod@^4.3.6"],
|
||||
});
|
||||
expect(calls).toEqual([
|
||||
{
|
||||
installRoot: pluginRoot,
|
||||
missingSpecs: ["zod@^4.3.6"],
|
||||
installSpecs: ["ws@^8.20.0", "zod@^4.3.6"],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not treat sibling extension runtime deps as satisfying a plugin", () => {
|
||||
const packageRoot = makeTempDir();
|
||||
const extensionsRoot = path.join(packageRoot, "dist", "extensions");
|
||||
|
||||
@@ -91,6 +91,26 @@ function resolveSourceCheckoutDistPackageRoot(pluginRoot: string): string | null
|
||||
return isSourceCheckoutRoot(packageRoot) ? packageRoot : null;
|
||||
}
|
||||
|
||||
function resolveBundledRuntimeDependencySearchRoots(params: {
|
||||
installRoot: string;
|
||||
pluginRoot: string;
|
||||
}): string[] {
|
||||
const roots = new Set<string>([params.installRoot]);
|
||||
const pluginRoot = path.resolve(params.pluginRoot);
|
||||
const extensionsDir = path.dirname(pluginRoot);
|
||||
const buildDir = path.dirname(extensionsDir);
|
||||
if (
|
||||
path.basename(extensionsDir) !== "extensions" ||
|
||||
(path.basename(buildDir) !== "dist" && path.basename(buildDir) !== "dist-runtime")
|
||||
) {
|
||||
return [...roots];
|
||||
}
|
||||
roots.add(extensionsDir);
|
||||
roots.add(buildDir);
|
||||
roots.add(path.dirname(buildDir));
|
||||
return [...roots];
|
||||
}
|
||||
|
||||
function createRuntimeDepsCacheKey(pluginId: string, specs: readonly string[]): string {
|
||||
return createHash("sha256")
|
||||
.update(pluginId)
|
||||
@@ -121,6 +141,12 @@ function hasAllDependencySentinels(rootDir: string, deps: readonly { name: strin
|
||||
return deps.every((dep) => fs.existsSync(path.join(rootDir, dependencySentinelPath(dep.name))));
|
||||
}
|
||||
|
||||
function hasDependencySentinel(searchRoots: readonly string[], dep: { name: string }): boolean {
|
||||
return searchRoots.some((rootDir) =>
|
||||
fs.existsSync(path.join(rootDir, dependencySentinelPath(dep.name))),
|
||||
);
|
||||
}
|
||||
|
||||
function replaceNodeModulesDir(targetDir: string, sourceDir: string): void {
|
||||
const parentDir = path.dirname(targetDir);
|
||||
const tempDir = fs.mkdtempSync(path.join(parentDir, ".openclaw-runtime-deps-copy-"));
|
||||
@@ -528,11 +554,15 @@ export function ensureBundledPluginRuntimeDeps(params: {
|
||||
}
|
||||
|
||||
const installRoot = resolveBundledRuntimeDependencyInstallRoot(params.pluginRoot);
|
||||
const dependencySearchRoots = resolveBundledRuntimeDependencySearchRoots({
|
||||
installRoot,
|
||||
pluginRoot: params.pluginRoot,
|
||||
});
|
||||
const dependencySpecs = deps
|
||||
.map((dep) => `${dep.name}@${dep.version}`)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
const missingSpecs = deps
|
||||
.filter((dep) => !fs.existsSync(path.join(installRoot, dependencySentinelPath(dep.name))))
|
||||
.filter((dep) => !hasDependencySentinel(dependencySearchRoots, dep))
|
||||
.map((dep) => `${dep.name}@${dep.version}`)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
if (missingSpecs.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user