| ... | @@ -4,6 +4,9 @@ import * as ConfigProvider from "effect/ConfigProvider"; | ... | @@ -4,6 +4,9 @@ import * as ConfigProvider from "effect/ConfigProvider"; |
| 4 | import * as Effect from "effect/Effect"; | 4 | import * as Effect from "effect/Effect"; |
| 5 | import * as Layer from "effect/Layer"; | 5 | import * as Layer from "effect/Layer"; |
| 6 | import * as Option from "effect/Option"; | 6 | import * as Option from "effect/Option"; |
| | 7 | import * as Sink from "effect/Sink"; |
| | 8 | import * as Stream from "effect/Stream"; |
| | 9 | import { ChildProcessSpawner } from "effect/unstable/process"; |
| 7 | | 10 | |
| 8 | import { | 11 | import { |
| 9 | BuildScriptError, | 12 | BuildScriptError, |
| ... | @@ -14,6 +17,7 @@ import { | ... | @@ -14,6 +17,7 @@ import { |
| 14 | InvalidMacPasskeyRpDomainError, | 17 | InvalidMacPasskeyRpDomainError, |
| 15 | InvalidMacPasskeyPublishableKeyError, | 18 | InvalidMacPasskeyPublishableKeyError, |
| 16 | isMacPasskeySigningConfigurationError, | 19 | isMacPasskeySigningConfigurationError, |
| | 20 | LinuxIconResizeError, |
| 17 | MissingMacPasskeyProvisioningProfileError, | 21 | MissingMacPasskeyProvisioningProfileError, |
| 18 | renderMacPasskeyEntitlements, | 22 | renderMacPasskeyEntitlements, |
| 19 | resolveClerkPasskeyNativeArtifacts, | 23 | resolveClerkPasskeyNativeArtifacts, |
| ... | @@ -27,11 +31,49 @@ import { | ... | @@ -27,11 +31,49 @@ import { |
| 27 | resolveGitHubPublishConfig, | 31 | resolveGitHubPublishConfig, |
| 28 | resolveMockUpdateServerPort, | 32 | resolveMockUpdateServerPort, |
| 29 | resolveMockUpdateServerUrl, | 33 | resolveMockUpdateServerUrl, |
| | 34 | stageLinuxIconSize, |
| 30 | STAGE_INSTALL_ARGS, | 35 | STAGE_INSTALL_ARGS, |
| 31 | } from "./build-desktop-artifact.ts"; | 36 | } from "./build-desktop-artifact.ts"; |
| 32 | import { BRAND_ASSET_PATHS } from "./lib/brand-assets.ts"; | 37 | import { BRAND_ASSET_PATHS } from "./lib/brand-assets.ts"; |
| 33 | import { HostProcessArchitecture, HostProcessPlatform } from "@t3tools/shared/hostProcess"; | 38 | import { HostProcessArchitecture, HostProcessPlatform } from "@t3tools/shared/hostProcess"; |
| 34 | | 39 | |
| | 40 | function mockProcess(exitCode: number) { |
| | 41 | return ChildProcessSpawner.makeHandle({ |
| | 42 | pid: ChildProcessSpawner.ProcessId(1), |
| | 43 | exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(exitCode)), |
| | 44 | isRunning: Effect.succeed(false), |
| | 45 | kill: () => Effect.void, |
| | 46 | unref: Effect.succeed(Effect.void), |
| | 47 | stdin: Sink.drain, |
| | 48 | stdout: Stream.empty, |
| | 49 | stderr: Stream.empty, |
| | 50 | all: Stream.empty, |
| | 51 | getInputFd: () => Sink.drain, |
| | 52 | getOutputFd: () => Stream.empty, |
| | 53 | }); |
| | 54 | } |
| | 55 | |
| | 56 | function iconResizeSpawnerLayer( |
| | 57 | commands: Array<{ readonly command: string; readonly args: ReadonlyArray<string> }>, |
| | 58 | exitCodes: ReadonlyArray<number>, |
| | 59 | ) { |
| | 60 | let commandIndex = 0; |
| | 61 | return Layer.succeed( |
| | 62 | ChildProcessSpawner.ChildProcessSpawner, |
| | 63 | ChildProcessSpawner.make((command) => { |
| | 64 | const childProcess = command as unknown as { |
| | 65 | readonly command: string; |
| | 66 | readonly args: ReadonlyArray<string>; |
| | 67 | }; |
| | 68 | commands.push({ |
| | 69 | command: childProcess.command, |
| | 70 | args: childProcess.args, |
| | 71 | }); |
| | 72 | return Effect.succeed(mockProcess(exitCodes[commandIndex++] ?? 0)); |
| | 73 | }), |
| | 74 | ); |
| | 75 | } |
| | 76 | |
| 35 | it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | 77 | it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 36 | it("resolves the dedicated nightly updater channel from nightly versions", () => { | 78 | it("resolves the dedicated nightly updater channel from nightly versions", () => { |
| 37 | assert.equal(resolveDesktopUpdateChannel("0.0.17-nightly.20260413.42"), "nightly"); | 79 | assert.equal(resolveDesktopUpdateChannel("0.0.17-nightly.20260413.42"), "nightly"); |
| ... | @@ -184,6 +226,40 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -184,6 +226,40 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 184 | assert.deepStrictEqual(DESKTOP_ASAR_UNPACK, ["node_modules/@ff-labs/fff-bin-*/**/*"]); | 226 | assert.deepStrictEqual(DESKTOP_ASAR_UNPACK, ["node_modules/@ff-labs/fff-bin-*/**/*"]); |
| 185 | }); | 227 | }); |
| 186 | | 228 | |
| | 229 | it.effect("preserves both Linux icon resize failures with structural context", () => { |
| | 230 | const commands: Array<{ readonly command: string; readonly args: ReadonlyArray<string> }> = []; |
| | 231 | |
| | 232 | return Effect.gen(function* () { |
| | 233 | const error = yield* stageLinuxIconSize("source.png", "target.png", 512, false).pipe( |
| | 234 | Effect.provide(iconResizeSpawnerLayer(commands, [1, 2])), |
| | 235 | Effect.flip, |
| | 236 | ); |
| | 237 | |
| | 238 | assert.instanceOf(error, LinuxIconResizeError); |
| | 239 | assert.equal(error.operation, "resize"); |
| | 240 | assert.equal(error.iconSize, 512); |
| | 241 | assert.equal(error.primaryTool, "magick"); |
| | 242 | assert.equal(error.fallbackTool, "convert"); |
| | 243 | assert.include(error.message, "512x512"); |
| | 244 | assert.include(error.message, "`magick`"); |
| | 245 | assert.include(error.message, "`convert`"); |
| | 246 | assert.notInclude(error.message, "non-zero exit code"); |
| | 247 | |
| | 248 | assert.instanceOf(error.cause, AggregateError); |
| | 249 | const aggregateCause = error.cause as AggregateError; |
| | 250 | assert.lengthOf(aggregateCause.errors, 2); |
| | 251 | assert.strictEqual(aggregateCause.cause, aggregateCause.errors[0]); |
| | 252 | assert.instanceOf(aggregateCause.errors[0], BuildScriptError); |
| | 253 | assert.instanceOf(aggregateCause.errors[1], BuildScriptError); |
| | 254 | assert.include((aggregateCause.errors[0] as BuildScriptError).message, "magick linux icon"); |
| | 255 | assert.include((aggregateCause.errors[1] as BuildScriptError).message, "convert linux icon"); |
| | 256 | assert.deepStrictEqual( |
| | 257 | commands.map(({ command }) => command), |
| | 258 | ["magick", "convert"], |
| | 259 | ); |
| | 260 | }); |
| | 261 | }); |
| | 262 | |
| 187 | it("derives macOS passkey signing configuration from the Clerk publishable key", () => { | 263 | it("derives macOS passkey signing configuration from the Clerk publishable key", () => { |
| 188 | const configuration = resolveMacPasskeySigningConfiguration({ | 264 | const configuration = resolveMacPasskeySigningConfiguration({ |
| 189 | T3CODE_APPLE_TEAM_ID: "abc1234567", | 265 | T3CODE_APPLE_TEAM_ID: "abc1234567", |