fix(desktop): make local data reset reliable

This commit is contained in:
tianma-if committed 2026-08-18 14:55:21 +08:00
1 parent f250c63aa5
commit bd6d030239
3 files changed
+72 -24

No files matched your search

+41 -19
View File
@@ -819,34 +819,56 @@ app.whenReady().then(async () => {
if (requestedUserDataDirectory) throw new Error("Local data reset is unavailable with a custom user-data directory");
if (localDataResetScheduled) return { scheduled: true };
localDataResetScheduled = true;
isQuitting = true;
shutdownCleanupStarted = true;
if (sidecarRestartTimer) {
clearTimeout(sidecarRestartTimer);
sidecarRestartTimer = null;
}
tray?.destroy();
try {
await stopSidecar();
await Promise.allSettled([
session.defaultSession.clearStorageData(),
session.defaultSession.clearCache(),
]);
scheduleMacLocalDataReset({
await scheduleMacLocalDataReset({
appDataDirectory: app.getPath("appData"),
executablePath: process.execPath,
executablePath: app.getPath("exe"),
parentPid: process.pid,
userDataDirectory: app.getPath("userData"),
});
} catch (error) {
localDataResetScheduled = false;
isQuitting = false;
shutdownCleanupStarted = false;
await writeDiagnostic("local-data-reset.schedule-failed", {
message: error instanceof Error ? error.message : String(error),
});
throw error;
}
// Only begin shutting down once the detached reset helper has definitely
// started. From this point on, exiting lets that helper remove userData and
// relaunch the app, so non-critical cleanup failures must not strand the
// application in a half-stopped state.
localDataResetScheduled = true;
isQuitting = true;
shutdownCleanupStarted = true;
const forcedExitTimer = setTimeout(() => app.exit(0), 5000);
forcedExitTimer.unref();
if (sidecarRestartTimer) {
clearTimeout(sidecarRestartTimer);
sidecarRestartTimer = null;
}
try {
tray?.destroy();
tray = null;
} catch (error) {
void writeDiagnostic("local-data-reset.tray-cleanup-failed", {
message: error instanceof Error ? error.message : String(error),
});
}
await stopSidecar().catch((error) => writeDiagnostic("local-data-reset.sidecar-stop-failed", {
message: error instanceof Error ? error.message : String(error),
}));
const storageResults = await Promise.allSettled([
Promise.resolve().then(() => session.defaultSession.clearStorageData()),
Promise.resolve().then(() => session.defaultSession.clearCache()),
]);
const storageFailure = storageResults.find((result) => result.status === "rejected");
if (storageFailure) {
void writeDiagnostic("local-data-reset.storage-cleanup-failed", {
message: storageFailure.reason instanceof Error ? storageFailure.reason.message : String(storageFailure.reason),
});
}
clearTimeout(forcedExitTimer);
setTimeout(() => app.exit(0), 50).unref();
return { scheduled: true };
});
+6 -2
View File
@@ -6,7 +6,7 @@ parent_pid="$1"
user_data_dir="$2"
application_path="$3"
attempt=0
while /bin/kill -0 "$parent_pid" 2>/dev/null && [ "$attempt" -lt 300 ]; do
while /bin/kill -0 "$parent_pid" 2>/dev/null && [ "$attempt" -lt 3000 ]; do
/bin/sleep 0.1
attempt=$((attempt + 1))
done
@@ -49,7 +49,7 @@ export const macApplicationBundlePath = (executablePath) => {
throw new Error("EdgeEver must be running from a macOS application bundle");
};
export const scheduleMacLocalDataReset = ({
export const scheduleMacLocalDataReset = async ({
appDataDirectory,
executablePath,
parentPid,
@@ -73,6 +73,10 @@ export const scheduleMacLocalDataReset = ({
stdio: "ignore",
},
);
await new Promise((resolveSpawn, rejectSpawn) => {
helper.once("spawn", resolveSpawn);
helper.once("error", rejectSpawn);
});
helper.unref();
return { applicationPath, target };
};
@@ -1,4 +1,5 @@
import { describe, expect, test } from "bun:test";
import { EventEmitter } from "node:events";
import {
macApplicationBundlePath,
managedUserDataDirectory,
@@ -32,16 +33,19 @@ describe("desktop local data reset", () => {
expect(() => macApplicationBundlePath("/usr/local/bin/edgeever")).toThrow();
});
test("passes paths as shell arguments instead of interpolating them", () => {
test("passes paths as shell arguments instead of interpolating them", async () => {
let invocation;
let unrefCalled = false;
const result = scheduleMacLocalDataReset({
const resultPromise = scheduleMacLocalDataReset({
appDataDirectory: "/Users/example/Library/Application Support",
executablePath: "/Applications/EdgeEver.app/Contents/MacOS/EdgeEver",
parentPid: 42,
spawnProcess: (...args) => {
invocation = args;
return { unref: () => { unrefCalled = true; } };
const helper = new EventEmitter();
helper.unref = () => { unrefCalled = true; };
queueMicrotask(() => helper.emit("spawn"));
return helper;
},
userDataDirectory: "/Users/example/Library/Application Support/EdgeEver data",
});
@@ -55,10 +59,28 @@ describe("desktop local data reset", () => {
expect(invocation[1][1]).not.toContain("/Applications/EdgeEver.app");
expect(invocation[1][1]).not.toContain("EdgeEver data");
expect(invocation[2]).toEqual({ detached: true, stdio: "ignore" });
const result = await resultPromise;
expect(unrefCalled).toBe(true);
expect(result).toEqual({
applicationPath: "/Applications/EdgeEver.app",
target: "/Users/example/Library/Application Support/EdgeEver data",
});
});
test("rejects when the reset helper cannot be started", async () => {
const resultPromise = scheduleMacLocalDataReset({
appDataDirectory: "/Users/example/Library/Application Support",
executablePath: "/Applications/EdgeEver.app/Contents/MacOS/EdgeEver",
parentPid: 42,
spawnProcess: () => {
const helper = new EventEmitter();
helper.unref = () => {};
queueMicrotask(() => helper.emit("error", new Error("spawn failed")));
return helper;
},
userDataDirectory: "/Users/example/Library/Application Support/EdgeEver",
});
await expect(resultPromise).rejects.toThrow("spawn failed");
});
});