fix: canonicalize backup cwd fallback

This commit is contained in:
Gustavo Madeira Santana
2026-03-08 16:01:03 -04:00
parent cc2ce77bd2
commit 4c8c6fbd33
2 changed files with 36 additions and 1 deletions

View File

@@ -278,6 +278,40 @@ describe("backup commands", () => {
await fs.rm(result.archivePath, { force: true });
});
it("falls back to the home directory when cwd is a symlink into a backed-up source tree", async () => {
if (process.platform === "win32") {
return;
}
const stateDir = path.join(tempHome.home, ".openclaw");
const workspaceDir = path.join(stateDir, "workspace");
const linkParent = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-backup-cwd-link-"));
const workspaceLink = path.join(linkParent, "workspace-link");
try {
await fs.writeFile(path.join(stateDir, "openclaw.json"), JSON.stringify({}), "utf8");
await fs.mkdir(workspaceDir, { recursive: true });
await fs.writeFile(path.join(workspaceDir, "SOUL.md"), "# soul\n", "utf8");
await fs.symlink(workspaceDir, workspaceLink);
process.chdir(workspaceLink);
const runtime = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
};
const nowMs = Date.UTC(2026, 2, 9, 1, 3, 4);
const result = await backupCreateCommand(runtime, { nowMs });
expect(result.archivePath).toBe(
path.join(tempHome.home, `${buildBackupArchiveRoot(nowMs)}.tar.gz`),
);
await fs.rm(result.archivePath, { force: true });
} finally {
await fs.rm(linkParent, { recursive: true, force: true });
}
});
it("allows dry-run preview even when the target archive already exists", async () => {
const stateDir = path.join(tempHome.home, ".openclaw");
const existingArchive = path.join(tempHome.home, "existing-backup.tar.gz");

View File

@@ -87,8 +87,9 @@ async function resolveOutputPath(params: {
const rawOutput = params.output?.trim();
if (!rawOutput) {
const cwd = path.resolve(process.cwd());
const canonicalCwd = await fs.realpath(cwd).catch(() => cwd);
const cwdInsideSource = params.includedAssets.some((asset) =>
isPathWithin(cwd, asset.sourcePath),
isPathWithin(canonicalCwd, asset.sourcePath),
);
const defaultDir = cwdInsideSource ? (resolveHomeDir() ?? path.dirname(params.stateDir)) : cwd;
return path.resolve(defaultDir, basename);