fix: preserve live runtime deps temp dirs

This commit is contained in:
Peter Steinberger
2026-04-27 08:39:30 +01:00
parent da55212c6e
commit 4514a73170
3 changed files with 120 additions and 2 deletions

View File

@@ -327,6 +327,73 @@ describe("stageBundledPluginRuntimeDeps", () => {
);
});
it("keeps runtime deps temp dirs owned by a live build process", () => {
const { pluginDir, repoRoot } = createBundledPluginFixture({
packageJson: {
name: "@openclaw/fixture-plugin",
version: "1.0.0",
dependencies: { "left-pad": "1.3.0" },
openclaw: { bundle: { stageRuntimeDependencies: true } },
},
});
const activeTempDir = path.join(pluginDir, ".openclaw-runtime-deps-stage-active");
fs.mkdirSync(activeTempDir, { recursive: true });
stageBundledPluginRuntimeDepsTesting.writeRuntimeDepsTempOwner(activeTempDir);
fs.writeFileSync(path.join(activeTempDir, "marker.txt"), "active\n", "utf8");
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
const nodeModulesDir = path.join(pluginDir, "node_modules");
fs.mkdirSync(nodeModulesDir, { recursive: true });
fs.writeFileSync(path.join(nodeModulesDir, "marker.txt"), "installed\n", "utf8");
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
expect(fs.readFileSync(path.join(activeTempDir, "marker.txt"), "utf8")).toBe("active\n");
expect(fs.readFileSync(path.join(pluginDir, "node_modules", "marker.txt"), "utf8")).toBe(
"installed\n",
);
});
it("restores atomically replaced dirs when concurrent cleanup runs during rename failure", () => {
const parentDir = createTempDir("openclaw-runtime-deps-replace-");
const targetPath = path.join(parentDir, "node_modules");
const sourcePath = path.join(parentDir, "source-node_modules");
fs.mkdirSync(targetPath, { recursive: true });
fs.writeFileSync(path.join(targetPath, "marker.txt"), "original\n", "utf8");
fs.mkdirSync(sourcePath, { recursive: true });
fs.writeFileSync(path.join(sourcePath, "marker.txt"), "replacement\n", "utf8");
const realRenameSync = fs.renameSync.bind(fs);
let backupPath: string | null = null;
vi.spyOn(fs, "renameSync").mockImplementation((oldPath, newPath) => {
const oldPathString = String(oldPath);
const newPathString = String(newPath);
if (
oldPathString === targetPath &&
path.basename(newPathString).startsWith(".openclaw-runtime-deps-backup-")
) {
backupPath = newPathString;
return realRenameSync(oldPath, newPath);
}
if (oldPathString === sourcePath && newPathString === targetPath) {
expect(backupPath).not.toBeNull();
stageBundledPluginRuntimeDepsTesting.removeStaleRuntimeDepsTempDirs(parentDir);
expect(fs.existsSync(path.join(backupPath ?? "", "marker.txt"))).toBe(true);
throw new Error("rename failed after backup");
}
return realRenameSync(oldPath, newPath);
});
expect(() =>
stageBundledPluginRuntimeDepsTesting.replaceDirAtomically(targetPath, sourcePath),
).toThrow("rename failed after backup");
expect(fs.readFileSync(path.join(targetPath, "marker.txt"), "utf8")).toBe("original\n");
});
it("restages when installed root runtime dependency contents change", () => {
const { pluginDir, repoRoot } = createBundledPluginFixture({
packageJson: {