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";...@@ -4,6 +4,7 @@ import * as Effect from "effect/Effect";
4import * as FileSystem from "effect/FileSystem";4import * as FileSystem from "effect/FileSystem";
5import * as Layer from "effect/Layer";5import * as Layer from "effect/Layer";
6import * as Option from "effect/Option";6import * as Option from "effect/Option";
7import * as PlatformError from "effect/PlatformError";
78
8import type * as Electron from "electron";9import type * as Electron from "electron";
910
...@@ -105,6 +106,7 @@ const withIdentity = <A, E, R>(...@@ -105,6 +106,7 @@ const withIdentity = <A, E, R>(
105 readonly calls?: ElectronAppCalls;106 readonly calls?: ElectronAppCalls;
106 readonly environment?: TestEnvironmentInput;107 readonly environment?: TestEnvironmentInput;
107 readonly legacyPathExists?: boolean;108 readonly legacyPathExists?: boolean;
109 readonly legacyPathProbeError?: PlatformError.PlatformError;
108 readonly packageJson?: string;110 readonly packageJson?: string;
109 readonly pngIconPath?: Option.Option<string>;111 readonly pngIconPath?: Option.Option<string>;
110 } = {},112 } = {},
...@@ -121,7 +123,11 @@ const withIdentity = <A, E, R>(...@@ -121,7 +123,11 @@ const withIdentity = <A, E, R>(
121 Layer.provideMerge(123 Layer.provideMerge(
122 FileSystem.layerNoop({124 FileSystem.layerNoop({
123 exists: (path) =>125 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 ),
125 readFileString: () =>131 readFileString: () =>
126 Effect.succeed(input.packageJson ?? '{"t3codeCommitHash":"abcdef1234567890"}'),132 Effect.succeed(input.packageJson ?? '{"t3codeCommitHash":"abcdef1234567890"}'),
127 }),133 }),
...@@ -147,6 +153,33 @@ describe("DesktopAppIdentity", () => {...@@ -147,6 +153,33 @@ describe("DesktopAppIdentity", () => {
147 ),153 ),
148 );154 );
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
150 it.effect("configures app identity from the environment commit override", () => {183 it.effect("configures app identity from the environment commit override", () => {
151 const calls: ElectronAppCalls = {184 const calls: ElectronAppCalls = {
152 setAboutPanelOptions: [],185 setAboutPanelOptions: [],
apps/desktop/src/app/DesktopAppIdentity.ts+23-5
...@@ -18,10 +18,22 @@ const AppPackageMetadata = Schema.Struct({...@@ -18,10 +18,22 @@ const AppPackageMetadata = Schema.Struct({
18});18});
19const decodeAppPackageMetadata = Schema.decodeEffect(Schema.fromJsonString(AppPackageMetadata));19const 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
21export class DesktopAppIdentity extends Context.Service<33export class DesktopAppIdentity extends Context.Service<
22 DesktopAppIdentity,34 DesktopAppIdentity,
23 {35 {
24 readonly resolveUserDataPath: Effect.Effect<string>;36 readonly resolveUserDataPath: Effect.Effect<string, DesktopUserDataPathResolutionError>;
25 readonly configure: Effect.Effect<void>;37 readonly configure: Effect.Effect<void>;
26 }38 }
27>()("@t3tools/desktop/app/DesktopAppIdentity") {}39>()("@t3tools/desktop/app/DesktopAppIdentity") {}
...@@ -33,7 +45,7 @@ const normalizeCommitHash = (value: string): Option.Option<string> => {...@@ -33,7 +45,7 @@ const normalizeCommitHash = (value: string): Option.Option<string> => {
33 : Option.none();45 : Option.none();
34};46};
3547
36const make = Effect.gen(function* () {48export const make = Effect.gen(function* () {
37 const assets = yield* DesktopAssets.DesktopAssets;49 const assets = yield* DesktopAssets.DesktopAssets;
38 const electronApp = yield* ElectronApp.ElectronApp;50 const electronApp = yield* ElectronApp.ElectronApp;
39 const environment = yield* DesktopEnvironment.DesktopEnvironment;51 const environment = yield* DesktopEnvironment.DesktopEnvironment;
...@@ -83,9 +95,15 @@ const make = Effect.gen(function* () {...@@ -83,9 +95,15 @@ const make = Effect.gen(function* () {
83 environment.appDataDirectory,95 environment.appDataDirectory,
84 environment.legacyUserDataDirName,96 environment.legacyUserDataDirName,
85 );97 );
86 const legacyPathExists = yield* fileSystem98 const legacyPathExists = yield* fileSystem.exists(legacyPath).pipe(
87 .exists(legacyPath)99 Effect.mapError(
88 .pipe(Effect.orElseSucceed(() => false));100 (cause) =>
101 new DesktopUserDataPathResolutionError({
102 legacyPath,
103 cause,
104 }),
105 ),
106 );
89 return legacyPathExists107 return legacyPathExists
90 ? legacyPath108 ? legacyPath
91 : environment.path.join(environment.appDataDirectory, environment.userDataDirName);109 : environment.path.join(environment.appDataDirectory, environment.userDataDirName);