mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:10:42 +00:00
fix(build): harden bundled plugin runtime staging
Copy bundled plugin skill trees into dist-runtime, broaden Windows symlink-copy fallbacks, and harden runtime-deps fingerprinting.
This commit is contained in:
@@ -332,6 +332,64 @@ describe("stageBundledPluginRuntimeDeps", () => {
|
||||
).toBe("module.exports = 'second';\n");
|
||||
});
|
||||
|
||||
it("fingerprints regular files when readdir reports symlink-like entries", () => {
|
||||
const { pluginDir, repoRoot } = createBundledPluginFixture({
|
||||
packageJson: {
|
||||
name: "@openclaw/fixture-plugin",
|
||||
version: "1.0.0",
|
||||
dependencies: { direct: "1.0.0" },
|
||||
openclaw: { bundle: { stageRuntimeDependencies: true } },
|
||||
},
|
||||
});
|
||||
const directDir = path.join(repoRoot, "node_modules", "direct");
|
||||
fs.mkdirSync(directDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(directDir, "package.json"),
|
||||
'{ "name": "direct", "version": "1.0.0" }\n',
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(path.join(directDir, "index.js"), "module.exports = 'direct';\n", "utf8");
|
||||
|
||||
const realReaddirSync = fs.readdirSync.bind(fs);
|
||||
vi.spyOn(fs, "readdirSync").mockImplementation(((target, options) => {
|
||||
const result = realReaddirSync(target, options as never);
|
||||
if (
|
||||
String(target) !== directDir ||
|
||||
typeof options !== "object" ||
|
||||
options === null ||
|
||||
!("withFileTypes" in options) ||
|
||||
options.withFileTypes !== true
|
||||
) {
|
||||
return result;
|
||||
}
|
||||
return (result as fs.Dirent[]).map((entry) => {
|
||||
if (entry.name !== "package.json") {
|
||||
return entry;
|
||||
}
|
||||
return {
|
||||
...entry,
|
||||
isSymbolicLink: () => true,
|
||||
isDirectory: () => false,
|
||||
isFile: () => false,
|
||||
} as fs.Dirent;
|
||||
}) as never;
|
||||
}) as typeof fs.readdirSync);
|
||||
|
||||
let installCount = 0;
|
||||
stageBundledPluginRuntimeDeps({
|
||||
cwd: repoRoot,
|
||||
installPluginRuntimeDepsImpl: () => {
|
||||
installCount += 1;
|
||||
throw new Error("unexpected fallback install");
|
||||
},
|
||||
});
|
||||
|
||||
expect(installCount).toBe(0);
|
||||
expect(
|
||||
fs.readFileSync(path.join(pluginDir, "node_modules", "direct", "index.js"), "utf8"),
|
||||
).toBe("module.exports = 'direct';\n");
|
||||
});
|
||||
|
||||
it("refuses to replace a symlinked plugin node_modules directory", () => {
|
||||
const { pluginDir, repoRoot } = createBundledPluginFixture({
|
||||
packageJson: {
|
||||
|
||||
@@ -20,17 +20,9 @@ describe("stageBundledPluginRuntime", () => {
|
||||
|
||||
it("copies files when Windows rejects runtime overlay symlinks", async () => {
|
||||
await withTempDir(async (repoRoot) => {
|
||||
const sourceFile = path.join(
|
||||
repoRoot,
|
||||
"dist",
|
||||
"extensions",
|
||||
"acpx",
|
||||
"skills",
|
||||
"acp-router",
|
||||
"fixture.txt",
|
||||
);
|
||||
const sourceFile = path.join(repoRoot, "dist", "extensions", "acpx", "assets", "fixture.txt");
|
||||
await fs.promises.mkdir(path.dirname(sourceFile), { recursive: true });
|
||||
await fs.promises.writeFile(sourceFile, "skill-body\n", "utf8");
|
||||
await fs.promises.writeFile(sourceFile, "asset-body\n", "utf8");
|
||||
|
||||
vi.spyOn(process, "platform", "get").mockReturnValue("win32");
|
||||
const symlinkSpy = vi
|
||||
@@ -54,11 +46,10 @@ describe("stageBundledPluginRuntime", () => {
|
||||
"dist-runtime",
|
||||
"extensions",
|
||||
"acpx",
|
||||
"skills",
|
||||
"acp-router",
|
||||
"assets",
|
||||
"fixture.txt",
|
||||
);
|
||||
expect(await fs.promises.readFile(runtimeFile, "utf8")).toBe("skill-body\n");
|
||||
expect(await fs.promises.readFile(runtimeFile, "utf8")).toBe("asset-body\n");
|
||||
expect(fs.lstatSync(runtimeFile).isSymbolicLink()).toBe(false);
|
||||
expect(symlinkSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user