Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/desktop/src/app/DesktopEnvironment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe("DesktopEnvironment", () => {
assert.equal(environment.browserArtifactsDir, "/tmp/t3/userdata/browser-artifacts");
assert.equal(environment.rootDir, "/repo");
assert.equal(environment.appRoot, "/repo");
assert.equal(environment.serverRoot, "/repo");
assert.equal(environment.backendEntryPath, "/repo/apps/server/dist/bin.mjs");
assert.equal(environment.backendCwd, "/repo");
assert.equal(environment.appUserModelId, "com.t3tools.t3code.dev");
Expand Down Expand Up @@ -98,6 +99,24 @@ describe("DesktopEnvironment", () => {
}),
);

it.effect("uses the packaged Windows server sidecar as the backend root", () =>
Effect.gen(function* () {
const environment = yield* makeEnvironment({
platform: "win32",
isPackaged: true,
appPath: "/install/resources/app.asar",
resourcesPath: "/install/resources",
});

assert.equal(environment.appRoot, "/install/resources/app.asar");
assert.equal(environment.serverRoot, "/install/resources/server.asar");
assert.equal(
environment.backendEntryPath,
"/install/resources/server.asar/apps/server/dist/bin.mjs",
);
}),
);

it.effect("keeps implicit development state separate from production state", () =>
Effect.gen(function* () {
const development = yield* makeEnvironment(
Expand Down
19 changes: 14 additions & 5 deletions apps/desktop/src/app/DesktopEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type {
DesktopRuntimeArch,
DesktopRuntimeInfo,
} from "@t3tools/contracts";
import * as NodePath from "@effect/platform-node/NodePath";
import * as Config from "effect/Config";
import * as Context from "effect/Context";
import * as Effect from "effect/Effect";
Expand Down Expand Up @@ -53,6 +52,13 @@ export class DesktopEnvironment extends Context.Service<
readonly browserArtifactsDir: string;
readonly rootDir: string;
readonly appRoot: string;
// Root of the tree containing apps/server/dist and node_modules for the
// backend. Equals appRoot everywhere except packaged Windows, where the
// server tree ships as the resources/server.asar sidecar (see
// scripts/build-desktop-artifact.ts) that the asar-aware
// ELECTRON_RUN_AS_NODE primary reads in place and the WSL backend
// extracts on demand (see DesktopWslServerTree).
readonly serverRoot: string;
readonly backendEntryPath: string;
readonly backendCwd: string;
readonly rendererRootPath: string;
Expand Down Expand Up @@ -163,6 +169,10 @@ const make = Effect.fn("desktop.environment.make")(function* (
});
const rootDir = path.resolve(input.dirname, "../../..");
const appRoot = input.isPackaged ? input.appPath : rootDir;
const serverRoot =
input.isPackaged && input.platform === "win32"
? path.join(input.resourcesPath, "server.asar")
: appRoot;
const branding = resolveDesktopAppBranding({
isDevelopment,
appVersion: input.appVersion,
Expand Down Expand Up @@ -212,7 +222,8 @@ const make = Effect.fn("desktop.environment.make")(function* (
browserArtifactsDir: path.join(stateDir, "browser-artifacts"),
rootDir,
appRoot,
backendEntryPath: path.join(appRoot, "apps/server/dist/bin.mjs"),
serverRoot,
backendEntryPath: path.join(serverRoot, "apps/server/dist/bin.mjs"),
backendCwd: input.isPackaged ? homeDirectory : appRoot,
rendererRootPath: path.join(appRoot, "apps/server/dist/client"),
preloadPath: path.join(input.dirname, "preload.cjs"),
Expand Down Expand Up @@ -276,6 +287,4 @@ const make = Effect.fn("desktop.environment.make")(function* (
});

export const layer = (input: MakeDesktopEnvironmentInput) =>
Layer.effect(DesktopEnvironment, make(input)).pipe(
Layer.provide(input.platform === "win32" ? NodePath.layerWin32 : NodePath.layerPosix),
);
Layer.effect(DesktopEnvironment, make(input));
148 changes: 114 additions & 34 deletions apps/desktop/src/backend/DesktopBackendConfiguration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as DesktopConfig from "../app/DesktopConfig.ts";
import * as DesktopServerExposure from "./DesktopServerExposure.ts";
import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts";
import * as DesktopWslEnvironment from "../wsl/DesktopWslEnvironment.ts";
import * as DesktopWslServerTree from "../wsl/DesktopWslServerTree.ts";

const PersistedServerObservabilitySettingsDocument = Schema.Struct({
observability: Schema.Struct({
Expand Down Expand Up @@ -115,6 +116,7 @@ const withHarness = <A, E, R>(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(makeEnvironmentLayer(baseDir)),
),
),
Expand Down Expand Up @@ -153,6 +155,53 @@ describe("DesktopBackendConfiguration", () => {
),
);

it.effect("resolvePrimary starts from server.asar without materializing the WSL tree", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-desktop-backend-config-test-",
});
const resourcesPath = `${baseDir}/resources`;

const result = yield* Effect.gen(function* () {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;
const config = yield* configuration.resolvePrimary;
return { config, environment };
}).pipe(
Effect.provide(
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest()),
Layer.provideMerge(
Layer.succeed(
DesktopWslServerTree.DesktopWslServerTree,
DesktopWslServerTree.DesktopWslServerTree.of({
ensure: Effect.die("Windows primary must not extract the WSL server tree"),
}),
),
),
Layer.provideMerge(
makeEnvironmentLayer(baseDir, {
appPath: `${resourcesPath}/app.asar`,
platform: "win32",
resourcesPath,
}),
),
),
),
);

assert.equal(result.config.entryPath, result.environment.backendEntryPath);
assert.equal(
result.environment.serverRoot,
result.environment.path.join(resourcesPath, "server.asar"),
);
assert.equal(result.config.env.ELECTRON_RUN_AS_NODE, "1");
}).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),
);

it.effect("resolveWsl reuses the primary's bootstrap token", () =>
withHarness(
Effect.gen(function* () {
Expand All @@ -169,35 +218,24 @@ describe("DesktopBackendConfiguration", () => {
it.effect("resolveWsl pins a default-tracking run to the concrete default distro", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-desktop-backend-config-test-",
});
const entryPath = path.join(baseDir, "apps/server/dist/bin.mjs");
yield* fileSystem.makeDirectory(path.dirname(entryPath), { recursive: true });
yield* fileSystem.writeFileString(entryPath, "");

const observedDistros: Array<string | null> = [];
const config = yield* Effect.gen(function* () {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
const wslAppRoot = environment.path.join(environment.resourcesPath, "app.asar.unpacked");
const entryPath = environment.path.join(wslAppRoot, "apps/server/dist/bin.mjs");
yield* Effect.addFinalizer(() =>
Effect.all([
fileSystem.remove(entryPath, { force: true }),
fileSystem.remove(environment.path.dirname(entryPath), {
recursive: true,
force: true,
}),
fileSystem.remove(wslAppRoot, { recursive: true, force: true }),
]).pipe(Effect.orElseSucceed(() => undefined)),
);
yield* fileSystem.makeDirectory(environment.path.dirname(entryPath), { recursive: true });
yield* fileSystem.writeFileString(entryPath, "export {};\n");

const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;
return yield* configuration.resolveWsl({ port: 5000, distro: null });
}).pipe(
Effect.provide(
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
Expand Down Expand Up @@ -242,40 +280,27 @@ describe("DesktopBackendConfiguration", () => {
() =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-desktop-backend-config-test-",
});
const entryPath = path.join(baseDir, "apps/server/dist/bin.mjs");
yield* fileSystem.makeDirectory(path.dirname(entryPath), { recursive: true });
yield* fileSystem.writeFileString(entryPath, "");

const nodePath = "/home/test user's/.nvm/versions/node/v22.0.0/bin/node";
const linuxEntryPath = "/tmp/t3 code's launch/entry file.mjs";
const resolvedPath = "/home/test user/bin:/opt/test's tools/bin:/usr/bin:/bin";
const devServerUrl = "http://127.0.0.1:5733/dev%20assets/?label=hello%20world";
const config = yield* Effect.gen(function* () {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
const wslAppRoot = environment.path.join(environment.resourcesPath, "app.asar.unpacked");
const entryPath = environment.path.join(wslAppRoot, "apps/server/dist/bin.mjs");
yield* Effect.addFinalizer(() =>
Effect.all([
fileSystem.remove(entryPath, { force: true }),
fileSystem.remove(environment.path.dirname(entryPath), {
recursive: true,
force: true,
}),
fileSystem.remove(wslAppRoot, { recursive: true, force: true }),
]).pipe(Effect.orElseSucceed(() => undefined)),
);
yield* fileSystem.makeDirectory(environment.path.dirname(entryPath), {
recursive: true,
});
yield* fileSystem.writeFileString(entryPath, "export {};\n");

const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;
return yield* configuration.resolveWsl({ port: 5000, distro: "Ubuntu" });
}).pipe(
Effect.provide(
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
Expand Down Expand Up @@ -412,6 +437,7 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest()),
Layer.provideMerge(makeEnvironmentLayer(baseDir)),
Layer.provideMerge(failingFileSystemLayer),
Expand Down Expand Up @@ -453,6 +479,7 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest()),
Layer.provideMerge(
makeEnvironmentLayer(baseDir, {
Expand Down Expand Up @@ -512,6 +539,7 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
Expand Down Expand Up @@ -562,6 +590,7 @@ describe("DesktopBackendConfiguration", () => {
wslOnly: true,
}),
),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: false })),
Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })),
),
Expand Down Expand Up @@ -599,6 +628,7 @@ describe("DesktopBackendConfiguration", () => {
wslDistro: "Removed-Distro",
}),
),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
Expand Down Expand Up @@ -632,6 +662,7 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
Expand Down Expand Up @@ -666,6 +697,49 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
distros: [{ name: "Ubuntu", isDefault: true, version: 2 }],
}),
),
Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })),
),
),
);
}).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),
);

it.effect("resolveWsl surfaces sidecar extraction failures through typed preflight", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-desktop-backend-config-test-",
});

yield* Effect.gen(function* () {
const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;
const config = yield* configuration.resolveWsl({ port: 5050, distro: "Ubuntu" });
const failure = Option.getOrThrow(config.preflightFailure);

assert.isFalse(failure.fatal);
assert.equal(failure.retryLimit, 12);
assert.include(failure.reason, "could not be extracted");
}).pipe(
Effect.provide(
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(
DesktopWslServerTree.layerTest({
result: {
ok: false,
reason: "WSL server files could not be extracted",
fatal: false,
},
}),
),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
Expand Down Expand Up @@ -698,6 +772,7 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
Expand Down Expand Up @@ -734,6 +809,7 @@ describe("DesktopBackendConfiguration", () => {
wslDistro: "Ubuntu",
}),
),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: true })),
Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })),
),
Expand Down Expand Up @@ -774,6 +850,7 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest()),
Layer.provideMerge(
makeEnvironmentLayer(baseDir, {
Expand Down Expand Up @@ -819,6 +896,7 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest()),
Layer.provideMerge(
makeEnvironmentLayer(baseDir, {
Expand Down Expand Up @@ -869,6 +947,7 @@ describe("DesktopBackendConfiguration", () => {
wslDistro: "Ubuntu",
}),
),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: false })),
Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })),
),
Expand All @@ -890,6 +969,7 @@ describe("DesktopBackendConfiguration", () => {
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(DesktopWslEnvironment.layer),
// isAvailable on win32 only touches the filesystem, never the spawner,
// so a die-stub is enough to satisfy the layer's deps.
Expand Down
Loading
Loading