mirror of https://github.com/openclaw/openclaw.git
fix(runtime): make dist-runtime staging idempotent
This commit is contained in:
parent
5822892fee
commit
fd5555d5be
|
|
@ -12,8 +12,30 @@ function relativeSymlinkTarget(sourcePath, targetPath) {
|
|||
return relativeTarget || ".";
|
||||
}
|
||||
|
||||
function ensureSymlink(targetValue, targetPath, type) {
|
||||
try {
|
||||
fs.symlinkSync(targetValue, targetPath, type);
|
||||
return;
|
||||
} catch (error) {
|
||||
if (error?.code !== "EEXIST") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (fs.lstatSync(targetPath).isSymbolicLink() && fs.readlinkSync(targetPath) === targetValue) {
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// Fall through and recreate the target when inspection fails.
|
||||
}
|
||||
|
||||
removePathIfExists(targetPath);
|
||||
fs.symlinkSync(targetValue, targetPath, type);
|
||||
}
|
||||
|
||||
function symlinkPath(sourcePath, targetPath, type) {
|
||||
fs.symlinkSync(relativeSymlinkTarget(sourcePath, targetPath), targetPath, type);
|
||||
ensureSymlink(relativeSymlinkTarget(sourcePath, targetPath), targetPath, type);
|
||||
}
|
||||
|
||||
function shouldWrapRuntimeJsFile(sourcePath) {
|
||||
|
|
@ -63,7 +85,7 @@ function stagePluginRuntimeOverlay(sourceDir, targetDir) {
|
|||
}
|
||||
|
||||
if (dirent.isSymbolicLink()) {
|
||||
fs.symlinkSync(fs.readlinkSync(sourcePath), targetPath);
|
||||
ensureSymlink(fs.readlinkSync(sourcePath), targetPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +113,7 @@ function linkPluginNodeModules(params) {
|
|||
if (!fs.existsSync(params.sourcePluginNodeModulesDir)) {
|
||||
return;
|
||||
}
|
||||
fs.symlinkSync(params.sourcePluginNodeModulesDir, runtimeNodeModulesDir, symlinkType());
|
||||
ensureSymlink(params.sourcePluginNodeModulesDir, runtimeNodeModulesDir, symlinkType());
|
||||
}
|
||||
|
||||
export function stageBundledPluginRuntime(params = {}) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import fs from "node:fs";
|
|||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { stageBundledPluginRuntime } from "../../scripts/stage-bundled-plugin-runtime.mjs";
|
||||
import { discoverOpenClawPlugins } from "./discovery.js";
|
||||
import { loadPluginManifestRegistry } from "./manifest-registry.js";
|
||||
|
|
@ -329,4 +329,40 @@ describe("stageBundledPluginRuntime", () => {
|
|||
|
||||
expect(fs.existsSync(path.join(repoRoot, "dist-runtime"))).toBe(false);
|
||||
});
|
||||
|
||||
it("tolerates EEXIST when an identical runtime symlink is materialized concurrently", () => {
|
||||
const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-eexist-");
|
||||
const distPluginDir = path.join(repoRoot, "dist", "extensions", "feishu");
|
||||
const distSkillDir = path.join(distPluginDir, "skills", "feishu-doc");
|
||||
fs.mkdirSync(distSkillDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(distPluginDir, "index.js"), "export default {}\n", "utf8");
|
||||
fs.writeFileSync(path.join(distSkillDir, "SKILL.md"), "# Feishu Doc\n", "utf8");
|
||||
|
||||
const realSymlinkSync = fs.symlinkSync.bind(fs);
|
||||
const symlinkSpy = vi.spyOn(fs, "symlinkSync").mockImplementation(((target, link, type) => {
|
||||
const linkPath = String(link);
|
||||
if (linkPath.endsWith(path.join("skills", "feishu-doc", "SKILL.md"))) {
|
||||
const err = Object.assign(new Error("file already exists"), { code: "EEXIST" });
|
||||
realSymlinkSync(String(target), linkPath, type);
|
||||
throw err;
|
||||
}
|
||||
return realSymlinkSync(String(target), linkPath, type);
|
||||
}) as typeof fs.symlinkSync);
|
||||
|
||||
expect(() => stageBundledPluginRuntime({ repoRoot })).not.toThrow();
|
||||
|
||||
const runtimeSkillPath = path.join(
|
||||
repoRoot,
|
||||
"dist-runtime",
|
||||
"extensions",
|
||||
"feishu",
|
||||
"skills",
|
||||
"feishu-doc",
|
||||
"SKILL.md",
|
||||
);
|
||||
expect(fs.lstatSync(runtimeSkillPath).isSymbolicLink()).toBe(true);
|
||||
expect(fs.readFileSync(runtimeSkillPath, "utf8")).toBe("# Feishu Doc\n");
|
||||
|
||||
symlinkSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue