mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 20:40:43 +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: [] });
|
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", () => {
|
it("does not treat sibling extension runtime deps as satisfying a plugin", () => {
|
||||||
const packageRoot = makeTempDir();
|
const packageRoot = makeTempDir();
|
||||||
const extensionsRoot = path.join(packageRoot, "dist", "extensions");
|
const extensionsRoot = path.join(packageRoot, "dist", "extensions");
|
||||||
|
|||||||
@@ -91,6 +91,26 @@ function resolveSourceCheckoutDistPackageRoot(pluginRoot: string): string | null
|
|||||||
return isSourceCheckoutRoot(packageRoot) ? packageRoot : 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 {
|
function createRuntimeDepsCacheKey(pluginId: string, specs: readonly string[]): string {
|
||||||
return createHash("sha256")
|
return createHash("sha256")
|
||||||
.update(pluginId)
|
.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))));
|
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 {
|
function replaceNodeModulesDir(targetDir: string, sourceDir: string): void {
|
||||||
const parentDir = path.dirname(targetDir);
|
const parentDir = path.dirname(targetDir);
|
||||||
const tempDir = fs.mkdtempSync(path.join(parentDir, ".openclaw-runtime-deps-copy-"));
|
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 installRoot = resolveBundledRuntimeDependencyInstallRoot(params.pluginRoot);
|
||||||
|
const dependencySearchRoots = resolveBundledRuntimeDependencySearchRoots({
|
||||||
|
installRoot,
|
||||||
|
pluginRoot: params.pluginRoot,
|
||||||
|
});
|
||||||
const dependencySpecs = deps
|
const dependencySpecs = deps
|
||||||
.map((dep) => `${dep.name}@${dep.version}`)
|
.map((dep) => `${dep.name}@${dep.version}`)
|
||||||
.toSorted((left, right) => left.localeCompare(right));
|
.toSorted((left, right) => left.localeCompare(right));
|
||||||
const missingSpecs = deps
|
const missingSpecs = deps
|
||||||
.filter((dep) => !fs.existsSync(path.join(installRoot, dependencySentinelPath(dep.name))))
|
.filter((dep) => !hasDependencySentinel(dependencySearchRoots, dep))
|
||||||
.map((dep) => `${dep.name}@${dep.version}`)
|
.map((dep) => `${dep.name}@${dep.version}`)
|
||||||
.toSorted((left, right) => left.localeCompare(right));
|
.toSorted((left, right) => left.localeCompare(right));
|
||||||
if (missingSpecs.length === 0) {
|
if (missingSpecs.length === 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user