authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-20 10:41:55-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-20 10:41:55-07:00
log515303edf2f83ab6ba15ebdae01ea2d5765756fc
tree7acb9fb7a7cfe7b5eebed4384f1801e4d45a725d
parent20734d4a78e4acb1bc1fff8083df5f71165cc545
signature Signed by PGP key B5690EEEBB952194

[codex] Preserve desktop user-data probe failures (#3304)

Co-authored-by: codex <codex@users.noreply.github.com>

2 files changed, 57 insertions(+), 6 deletions(-)

apps/desktop/src/app/DesktopAppIdentity.test.ts+34-1
......@@ -4,6 +4,7 @@ import * as Effect from "effect/Effect";
44import * as FileSystem from "effect/FileSystem";
55import * as Layer from "effect/Layer";
66import * as Option from "effect/Option";
7import * as PlatformError from "effect/PlatformError";
78
89import type * as Electron from "electron";
910
......@@ -105,6 +106,7 @@ const withIdentity = <A, E, R>(
105106 readonly calls?: ElectronAppCalls;
106107 readonly environment?: TestEnvironmentInput;
107108 readonly legacyPathExists?: boolean;
109 readonly legacyPathProbeError?: PlatformError.PlatformError;
108110 readonly packageJson?: string;
109111 readonly pngIconPath?: Option.Option<string>;
110112 } = {},
......@@ -121,7 +123,11 @@ const withIdentity = <A, E, R>(
121123 Layer.provideMerge(
122124 FileSystem.layerNoop({
123125 exists: (path) =>
124 Effect.succeed(input.legacyPathExists === true && path.includes("T3 Code (Alpha)")),
126 input.legacyPathProbeError
127 ? Effect.fail(input.legacyPathProbeError)
128 : Effect.succeed(
129 input.legacyPathExists === true && path.includes("T3 Code (Alpha)"),
130 ),
125131 readFileString: () =>
126132 Effect.succeed(input.packageJson ?? '{"t3codeCommitHash":"abcdef1234567890"}'),
127133 }),
......@@ -147,6 +153,33 @@ describe("DesktopAppIdentity", () => {
147153 ),
148154 );
149155
156 it.effect("preserves failures while inspecting the legacy userData path", () => {
157 const legacyPath = "/Users/alice/Library/Application Support/T3 Code (Alpha)";
158 const cause = PlatformError.systemError({
159 _tag: "PermissionDenied",
160 module: "FileSystem",
161 method: "exists",
162 description: "permission denied",
163 pathOrDescriptor: legacyPath,
164 });
165
166 return withIdentity(
167 Effect.gen(function* () {
168 const identity = yield* DesktopAppIdentity.DesktopAppIdentity;
169 const error = yield* identity.resolveUserDataPath.pipe(Effect.flip);
170
171 assert.instanceOf(error, DesktopAppIdentity.DesktopUserDataPathResolutionError);
172 assert.equal(error.legacyPath, legacyPath);
173 assert.strictEqual(error.cause, cause);
174 assert.equal(
175 error.message,
176 `Failed to inspect legacy desktop user-data path at "${legacyPath}".`,
177 );
178 }),
179 { legacyPathProbeError: cause },
180 );
181 });
182
150183 it.effect("configures app identity from the environment commit override", () => {
151184 const calls: ElectronAppCalls = {
152185 setAboutPanelOptions: [],
apps/desktop/src/app/DesktopAppIdentity.ts+23-5
......@@ -18,10 +18,22 @@ const AppPackageMetadata = Schema.Struct({
1818});
1919const decodeAppPackageMetadata = Schema.decodeEffect(Schema.fromJsonString(AppPackageMetadata));
2020
21export class DesktopUserDataPathResolutionError extends Schema.TaggedErrorClass<DesktopUserDataPathResolutionError>()(
22 "DesktopUserDataPathResolutionError",
23 {
24 legacyPath: Schema.String,
25 cause: Schema.Defect(),
26 },
27) {
28 override get message(): string {
29 return `Failed to inspect legacy desktop user-data path at "${this.legacyPath}".`;
30 }
31}
32
2133export class DesktopAppIdentity extends Context.Service<
2234 DesktopAppIdentity,
2335 {
24 readonly resolveUserDataPath: Effect.Effect<string>;
36 readonly resolveUserDataPath: Effect.Effect<string, DesktopUserDataPathResolutionError>;
2537 readonly configure: Effect.Effect<void>;
2638 }
2739>()("@t3tools/desktop/app/DesktopAppIdentity") {}
......@@ -33,7 +45,7 @@ const normalizeCommitHash = (value: string): Option.Option<string> => {
3345 : Option.none();
3446};
3547
36const make = Effect.gen(function* () {
48export const make = Effect.gen(function* () {
3749 const assets = yield* DesktopAssets.DesktopAssets;
3850 const electronApp = yield* ElectronApp.ElectronApp;
3951 const environment = yield* DesktopEnvironment.DesktopEnvironment;
......@@ -83,9 +95,15 @@ const make = Effect.gen(function* () {
8395 environment.appDataDirectory,
8496 environment.legacyUserDataDirName,
8597 );
86 const legacyPathExists = yield* fileSystem
87 .exists(legacyPath)
88 .pipe(Effect.orElseSucceed(() => false));
98 const legacyPathExists = yield* fileSystem.exists(legacyPath).pipe(
99 Effect.mapError(
100 (cause) =>
101 new DesktopUserDataPathResolutionError({
102 legacyPath,
103 cause,
104 }),
105 ),
106 );
89107 return legacyPathExists
90108 ? legacyPath
91109 : environment.path.join(environment.appDataDirectory, environment.userDataDirName);