mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-02 12:51:57 +00:00
Plugins: stage local bundled runtime tree
This commit is contained in:
@@ -7,7 +7,6 @@ import { resolveBundledPluginsDir } from "./bundled-dir.js";
|
||||
const tempDirs: string[] = [];
|
||||
const originalCwd = process.cwd();
|
||||
const originalBundledDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
||||
const originalWatchMode = process.env.OPENCLAW_WATCH_MODE;
|
||||
|
||||
function makeRepoRoot(prefix: string): string {
|
||||
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
@@ -22,20 +21,15 @@ afterEach(() => {
|
||||
} else {
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = originalBundledDir;
|
||||
}
|
||||
if (originalWatchMode === undefined) {
|
||||
delete process.env.OPENCLAW_WATCH_MODE;
|
||||
} else {
|
||||
process.env.OPENCLAW_WATCH_MODE = originalWatchMode;
|
||||
}
|
||||
for (const dir of tempDirs.splice(0, tempDirs.length)) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe("resolveBundledPluginsDir", () => {
|
||||
it("prefers source extensions from the package root in watch mode", () => {
|
||||
const repoRoot = makeRepoRoot("openclaw-bundled-dir-watch-");
|
||||
fs.mkdirSync(path.join(repoRoot, "extensions"), { recursive: true });
|
||||
it("prefers the staged runtime bundled plugin tree from the package root", () => {
|
||||
const repoRoot = makeRepoRoot("openclaw-bundled-dir-runtime-");
|
||||
fs.mkdirSync(path.join(repoRoot, "dist-runtime", "extensions"), { recursive: true });
|
||||
fs.mkdirSync(path.join(repoRoot, "dist", "extensions"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(repoRoot, "package.json"),
|
||||
@@ -44,10 +38,9 @@ describe("resolveBundledPluginsDir", () => {
|
||||
);
|
||||
|
||||
process.chdir(repoRoot);
|
||||
process.env.OPENCLAW_WATCH_MODE = "1";
|
||||
|
||||
expect(fs.realpathSync(resolveBundledPluginsDir() ?? "")).toBe(
|
||||
fs.realpathSync(path.join(repoRoot, "extensions")),
|
||||
fs.realpathSync(path.join(repoRoot, "dist-runtime", "extensions")),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,20 +10,23 @@ export function resolveBundledPluginsDir(env: NodeJS.ProcessEnv = process.env):
|
||||
return resolveUserPath(override, env);
|
||||
}
|
||||
|
||||
if (env.OPENCLAW_WATCH_MODE === "1") {
|
||||
try {
|
||||
const packageRoot = resolveOpenClawPackageRootSync({ cwd: process.cwd() });
|
||||
if (packageRoot) {
|
||||
// In watch mode, prefer source plugin roots so plugin-local runtime deps
|
||||
// resolve from extensions/<id>/node_modules instead of stripped dist copies.
|
||||
const sourceExtensionsDir = path.join(packageRoot, "extensions");
|
||||
if (fs.existsSync(sourceExtensionsDir)) {
|
||||
return sourceExtensionsDir;
|
||||
}
|
||||
try {
|
||||
const packageRoots = [
|
||||
resolveOpenClawPackageRootSync({ cwd: process.cwd() }),
|
||||
resolveOpenClawPackageRootSync({ moduleUrl: import.meta.url }),
|
||||
].filter(
|
||||
(entry, index, all): entry is string => Boolean(entry) && all.indexOf(entry) === index,
|
||||
);
|
||||
for (const packageRoot of packageRoots) {
|
||||
// Local source checkouts stage a runtime-complete bundled plugin tree under
|
||||
// dist-runtime/. Prefer that over release-shaped dist/extensions.
|
||||
const runtimeExtensionsDir = path.join(packageRoot, "dist-runtime", "extensions");
|
||||
if (fs.existsSync(runtimeExtensionsDir)) {
|
||||
return runtimeExtensionsDir;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// bun --compile: ship a sibling `extensions/` next to the executable.
|
||||
|
||||
101
src/plugins/stage-bundled-plugin-runtime.test.ts
Normal file
101
src/plugins/stage-bundled-plugin-runtime.test.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { stageBundledPluginRuntime } from "../../scripts/stage-bundled-plugin-runtime.mjs";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
function makeRepoRoot(prefix: string): string {
|
||||
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
tempDirs.push(repoRoot);
|
||||
return repoRoot;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs.splice(0, tempDirs.length)) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe("stageBundledPluginRuntime", () => {
|
||||
it("hard-links bundled dist plugins into dist-runtime and links plugin-local node_modules", () => {
|
||||
const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-");
|
||||
const distPluginDir = path.join(repoRoot, "dist", "extensions", "diffs");
|
||||
fs.mkdirSync(path.join(repoRoot, "dist"), { recursive: true });
|
||||
const sourcePluginNodeModulesDir = path.join(repoRoot, "extensions", "diffs", "node_modules");
|
||||
fs.mkdirSync(distPluginDir, { recursive: true });
|
||||
fs.mkdirSync(path.join(sourcePluginNodeModulesDir, "@pierre", "diffs"), {
|
||||
recursive: true,
|
||||
});
|
||||
fs.writeFileSync(path.join(distPluginDir, "index.js"), "export default {}\n", "utf8");
|
||||
fs.writeFileSync(
|
||||
path.join(sourcePluginNodeModulesDir, "@pierre", "diffs", "index.js"),
|
||||
"export default {}\n",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
stageBundledPluginRuntime({ repoRoot });
|
||||
|
||||
const runtimePluginDir = path.join(repoRoot, "dist-runtime", "extensions", "diffs");
|
||||
expect(fs.existsSync(path.join(runtimePluginDir, "index.js"))).toBe(true);
|
||||
expect(fs.statSync(path.join(runtimePluginDir, "index.js")).nlink).toBeGreaterThan(1);
|
||||
expect(fs.lstatSync(path.join(runtimePluginDir, "node_modules")).isSymbolicLink()).toBe(true);
|
||||
expect(fs.realpathSync(path.join(runtimePluginDir, "node_modules"))).toBe(
|
||||
fs.realpathSync(sourcePluginNodeModulesDir),
|
||||
);
|
||||
});
|
||||
|
||||
it("hard-links top-level dist chunks so staged bundled plugins keep relative imports working", () => {
|
||||
const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-chunks-");
|
||||
fs.mkdirSync(path.join(repoRoot, "dist", "extensions", "diffs"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(repoRoot, "dist", "chunk-abc.js"),
|
||||
"export const value = 1;\n",
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(repoRoot, "dist", "extensions", "diffs", "index.js"),
|
||||
"export { value } from '../../chunk-abc.js';\n",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
stageBundledPluginRuntime({ repoRoot });
|
||||
|
||||
const runtimeChunkPath = path.join(repoRoot, "dist-runtime", "chunk-abc.js");
|
||||
expect(fs.readFileSync(runtimeChunkPath, "utf8")).toContain("value = 1");
|
||||
expect(fs.statSync(runtimeChunkPath).nlink).toBeGreaterThan(1);
|
||||
expect(
|
||||
fs.readFileSync(
|
||||
path.join(repoRoot, "dist-runtime", "extensions", "diffs", "index.js"),
|
||||
"utf8",
|
||||
),
|
||||
).toContain("../../chunk-abc.js");
|
||||
const distChunkStats = fs.statSync(path.join(repoRoot, "dist", "chunk-abc.js"));
|
||||
const runtimeChunkStats = fs.statSync(runtimeChunkPath);
|
||||
expect(runtimeChunkStats.ino).toBe(distChunkStats.ino);
|
||||
expect(runtimeChunkStats.dev).toBe(distChunkStats.dev);
|
||||
});
|
||||
|
||||
it("removes stale runtime plugin directories that are no longer in dist", () => {
|
||||
const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-stale-");
|
||||
const staleRuntimeDir = path.join(repoRoot, "dist-runtime", "extensions", "stale");
|
||||
fs.mkdirSync(staleRuntimeDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(staleRuntimeDir, "index.js"), "stale\n", "utf8");
|
||||
fs.mkdirSync(path.join(repoRoot, "dist", "extensions"), { recursive: true });
|
||||
|
||||
stageBundledPluginRuntime({ repoRoot });
|
||||
|
||||
expect(fs.existsSync(staleRuntimeDir)).toBe(false);
|
||||
});
|
||||
|
||||
it("removes dist-runtime when the built bundled plugin tree is absent", () => {
|
||||
const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-missing-");
|
||||
const runtimeRoot = path.join(repoRoot, "dist-runtime", "extensions", "diffs");
|
||||
fs.mkdirSync(runtimeRoot, { recursive: true });
|
||||
|
||||
stageBundledPluginRuntime({ repoRoot });
|
||||
|
||||
expect(fs.existsSync(path.join(repoRoot, "dist-runtime"))).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user