fix(doctor): resolve macOS cloud paths through existing ancestors (#124299)

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Mohammed Alkindi
2026-08-25 12:46:44 +04:00
committed by GitHub
parent c697cf27da
commit 55cd6fe931
2 changed files with 63 additions and 10 deletions
@@ -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;
+7 -9
View File
@@ -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 };
}
}