mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
fix(doctor): resolve macOS cloud paths through existing ancestors (#124299)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -1,9 +1,17 @@
|
||||
// Doctor state integrity cloud-storage tests cover macOS cloud-synced state directory detection.
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { createTempDirTracker } from "../../test/helpers/temp-dir.js";
|
||||
import { detectMacCloudSyncedStateDir } from "./doctor-state-integrity.js";
|
||||
|
||||
const tempDirs = createTempDirTracker();
|
||||
|
||||
afterEach(() => {
|
||||
tempDirs.cleanup();
|
||||
});
|
||||
|
||||
describe("detectMacCloudSyncedStateDir", () => {
|
||||
const home = "/Users/tester";
|
||||
|
||||
@@ -85,6 +93,53 @@ describe("detectMacCloudSyncedStateDir", () => {
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("follows a real symlink out of the sync root when the state dir leaf is absent", () => {
|
||||
const sandbox = fs.realpathSync(tempDirs.make("openclaw-cloud-storage-symlink-"));
|
||||
const realHome = path.join(sandbox, "home");
|
||||
const cloudStorage = path.join(realHome, "Library", "CloudStorage");
|
||||
const localTarget = path.join(sandbox, "local-openclaw");
|
||||
fs.mkdirSync(cloudStorage, { recursive: true });
|
||||
fs.mkdirSync(localTarget, { recursive: true });
|
||||
const syncedLink = path.join(cloudStorage, "OneDrive-Personal");
|
||||
fs.symlinkSync(localTarget, syncedLink, process.platform === "win32" ? "junction" : "dir");
|
||||
|
||||
const stateDir = path.join(syncedLink, "OpenClaw", ".openclaw");
|
||||
expect(fs.existsSync(stateDir)).toBe(false);
|
||||
|
||||
expect(
|
||||
detectMacCloudSyncedStateDir(stateDir, {
|
||||
platform: "darwin",
|
||||
homedir: realHome,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("still warns for a real absent leaf that stays inside the sync root", () => {
|
||||
const sandbox = fs.realpathSync(tempDirs.make("openclaw-cloud-storage-real-"));
|
||||
const realHome = path.join(sandbox, "home");
|
||||
const syncedDir = path.join(
|
||||
realHome,
|
||||
"Library",
|
||||
"CloudStorage",
|
||||
"OneDrive-Personal",
|
||||
"OpenClaw",
|
||||
);
|
||||
fs.mkdirSync(syncedDir, { recursive: true });
|
||||
|
||||
const stateDir = path.join(syncedDir, ".openclaw");
|
||||
expect(fs.existsSync(stateDir)).toBe(false);
|
||||
|
||||
expect(
|
||||
detectMacCloudSyncedStateDir(stateDir, {
|
||||
platform: "darwin",
|
||||
homedir: realHome,
|
||||
}),
|
||||
).toEqual({
|
||||
path: path.resolve(stateDir),
|
||||
storage: "CloudStorage provider",
|
||||
});
|
||||
});
|
||||
|
||||
it("anchors cloud detection to OS homedir when OPENCLAW_HOME is overridden", () => {
|
||||
const stateDir = path.join(home, "Library", "CloudStorage", "iCloud Drive", ".openclaw");
|
||||
const originalOpenClawHome = process.env.OPENCLAW_HOME;
|
||||
|
||||
@@ -697,16 +697,14 @@ export function detectMacCloudSyncedStateDir(
|
||||
root: path.join(homedir, "Library", "CloudStorage"),
|
||||
},
|
||||
];
|
||||
const realPath = (deps?.resolveRealPath ?? tryResolveRealPath)(stateDir);
|
||||
// Prefer the resolved target path when available so symlink prefixes do not
|
||||
// misclassify local state dirs as cloud-synced.
|
||||
const candidates = realPath ? [path.resolve(realPath)] : [path.resolve(stateDir)];
|
||||
const resolveRealPath = deps?.resolveRealPath ?? tryResolveRealPath;
|
||||
// Missing state leaves must still follow existing symlink ancestors, like the Linux detectors.
|
||||
const resolvedStatePath =
|
||||
resolvePathThroughExistingAncestor(stateDir, resolveRealPath, path) ?? path.resolve(stateDir);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
for (const { storage, root } of roots) {
|
||||
if (isPathUnderRoot(candidate, root)) {
|
||||
return { path: candidate, storage };
|
||||
}
|
||||
for (const { storage, root } of roots) {
|
||||
if (isPathUnderRoot(resolvedStatePath, root)) {
|
||||
return { path: resolvedStatePath, storage };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user