fix(desktop): recover from macOS directory permission failures
This commit is contained in:
@@ -6,6 +6,7 @@ import { readDirCache, persistVcs, persistProjectMeta, persistIcon, persistSessi
|
||||
import { normalizePath } from "@/lib/pathNormalization"
|
||||
import { startSessionLoadPerformanceEvent } from "./session-load-performance"
|
||||
import { countSyncPerformance } from "./performance-diagnostics"
|
||||
import { isFilesystemError } from "@/lib/api/files-errors"
|
||||
|
||||
export type DirectoryStore = State & {
|
||||
/** Apply a partial state update */
|
||||
@@ -216,6 +217,7 @@ export type DirectoryBootstrapDemand = {
|
||||
}
|
||||
|
||||
export type DirectoryBootstrapState = "queued" | "running" | "complete" | "failed"
|
||||
export type DirectoryBootstrapFailureReason = "os-permission" | "generic"
|
||||
|
||||
export type DirectoryBootstrapContext = DirectoryBootstrapDemand & {
|
||||
generation: number
|
||||
@@ -296,6 +298,7 @@ export class ChildStoreManager {
|
||||
private readonly bootstrapQueue = new Map<string, QueuedBootstrap>()
|
||||
private readonly runningBootstraps = new Map<string, RunningBootstrap>()
|
||||
private readonly bootstrapStates = new Map<string, DirectoryBootstrapState>()
|
||||
private readonly bootstrapFailures = new Map<string, DirectoryBootstrapFailureReason>()
|
||||
|
||||
private onBootstrap?: (context: DirectoryBootstrapContext) => Promise<void> | void
|
||||
private onDispose?: (directory: string) => void
|
||||
@@ -479,6 +482,11 @@ export class ChildStoreManager {
|
||||
return normalizedDirectory ? this.bootstrapStates.get(normalizedDirectory) : undefined
|
||||
}
|
||||
|
||||
getBootstrapFailure(directory: string): DirectoryBootstrapFailureReason | undefined {
|
||||
const normalizedDirectory = normalizePath(directory)
|
||||
return normalizedDirectory ? this.bootstrapFailures.get(normalizedDirectory) : undefined
|
||||
}
|
||||
|
||||
subscribeBootstrap(listener: () => void): () => void {
|
||||
this.bootstrapSubscribers.add(listener)
|
||||
return () => this.bootstrapSubscribers.delete(listener)
|
||||
@@ -530,6 +538,7 @@ export class ChildStoreManager {
|
||||
if (demand.force) running.rerunRequested = true
|
||||
return false
|
||||
}
|
||||
this.bootstrapFailures.delete(directory)
|
||||
const existing = this.bootstrapQueue.get(directory)
|
||||
const next: QueuedBootstrap = existing
|
||||
? {
|
||||
@@ -603,14 +612,19 @@ export class ChildStoreManager {
|
||||
.then(() => {
|
||||
if (isCurrent()) {
|
||||
this.bootstrapStates.set(next.directory, "complete")
|
||||
this.bootstrapFailures.delete(next.directory)
|
||||
finishPerformanceEvent("complete")
|
||||
} else {
|
||||
finishPerformanceEvent("stale")
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((error) => {
|
||||
if (isCurrent()) {
|
||||
this.bootstrapStates.set(next.directory, "failed")
|
||||
this.bootstrapFailures.set(
|
||||
next.directory,
|
||||
isFilesystemError(error) && error.reason === "os-permission" ? "os-permission" : "generic",
|
||||
)
|
||||
finishPerformanceEvent("error")
|
||||
} else {
|
||||
finishPerformanceEvent("stale")
|
||||
@@ -658,6 +672,7 @@ export class ChildStoreManager {
|
||||
this.bootstrapQueue.delete(directory)
|
||||
this.manualBootstrapDemands.delete(directory)
|
||||
this.bootstrapStates.delete(directory)
|
||||
this.bootstrapFailures.delete(directory)
|
||||
for (const demands of this.bootstrapDemandsByOwner.values()) demands.delete(directory)
|
||||
this.children.delete(directory)
|
||||
this.notifyRegistrySubscribers()
|
||||
@@ -719,6 +734,7 @@ export class ChildStoreManager {
|
||||
this.bootstrapQueue.clear()
|
||||
this.runningBootstraps.clear()
|
||||
this.bootstrapStates.clear()
|
||||
this.bootstrapFailures.clear()
|
||||
this.bootstrapDemandsByOwner.clear()
|
||||
this.manualBootstrapDemands.clear()
|
||||
this.notifyBootstrapSubscribers()
|
||||
|
||||
Reference in New Issue
Block a user