diff --git a/apps/server/src/environment/ServerEnvironmentLabel.test.ts b/apps/server/src/environment/ServerEnvironmentLabel.test.ts index 6fc06b889db..a831c21f7cf 100644 --- a/apps/server/src/environment/ServerEnvironmentLabel.test.ts +++ b/apps/server/src/environment/ServerEnvironmentLabel.test.ts @@ -7,7 +7,7 @@ import * as PlatformError from "effect/PlatformError"; import * as References from "effect/References"; import * as Schema from "effect/Schema"; import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; -import { HostProcessHostname, HostProcessPlatform } from "@t3tools/shared/hostProcess"; +import { HostProcessEnvironment, HostProcessHostname, HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { vi } from "vite-plus/test"; import * as ProcessRunner from "../processRunner.ts"; @@ -49,11 +49,13 @@ const withHostPlatform = ( layer: Layer.Layer, platform: NodeJS.Platform, hostname: string, + env: NodeJS.ProcessEnv = {}, ) => Layer.mergeAll( layer, Layer.succeed(HostProcessPlatform, platform), Layer.succeed(HostProcessHostname, hostname), + Layer.succeed(HostProcessEnvironment, env), ); afterEach(() => { @@ -282,4 +284,79 @@ describe("resolveServerEnvironmentLabel", () => { expect(result).toBe("t3code"); }), ); + it.effect("appends WSL distro name when WSL_DISTRO_NAME is present", () => + Effect.gen(function* () { + runMock.mockReturnValueOnce( + Effect.succeed({ + stdout: "", + stderr: "", + code: ChildProcessSpawner.ExitCode(1), + timedOut: false, + stdoutTruncated: false, + stderrTruncated: false, + stdoutInvalidUtf8: false, + stderrInvalidUtf8: false, + }), + ); + + const WslLayer = Layer.merge( + ProcessRunnerTest, + FileSystem.layerNoop({ + exists: (path) => Effect.succeed(path === "/proc/sys/kernel/osrelease"), + readFileString: (path) => + path === "/proc/sys/kernel/osrelease" + ? Effect.succeed("5.15.90.1-microsoft-standard-WSL2") + : Effect.succeed(""), + }), + ); + + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ + cwdBaseName: "t3code", + }).pipe( + Effect.provide( + withHostPlatform(WslLayer, "linux", "WINDOWS-HOST", { WSL_DISTRO_NAME: "Ubuntu-22.04" }) + ) + ); + + expect(result).toBe("WINDOWS-HOST (WSL: Ubuntu-22.04)"); + }), + ); + + it.effect("appends generic WSL label when WSL_DISTRO_NAME is absent", () => + Effect.gen(function* () { + runMock.mockReturnValueOnce( + Effect.succeed({ + stdout: "", + stderr: "", + code: ChildProcessSpawner.ExitCode(1), + timedOut: false, + stdoutTruncated: false, + stderrTruncated: false, + stdoutInvalidUtf8: false, + stderrInvalidUtf8: false, + }), + ); + + const WslLayer = Layer.merge( + ProcessRunnerTest, + FileSystem.layerNoop({ + exists: (path) => Effect.succeed(path === "/proc/sys/kernel/osrelease"), + readFileString: (path) => + path === "/proc/sys/kernel/osrelease" + ? Effect.succeed("5.15.90.1-microsoft-standard-WSL2") + : Effect.succeed(""), + }), + ); + + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ + cwdBaseName: "t3code", + }).pipe( + Effect.provide( + withHostPlatform(WslLayer, "linux", "WINDOWS-HOST", {}) + ) + ); + + expect(result).toBe("WINDOWS-HOST (WSL)"); + }), + ); }); diff --git a/apps/server/src/environment/ServerEnvironmentLabel.ts b/apps/server/src/environment/ServerEnvironmentLabel.ts index bd034e0fa26..c270d389900 100644 --- a/apps/server/src/environment/ServerEnvironmentLabel.ts +++ b/apps/server/src/environment/ServerEnvironmentLabel.ts @@ -1,4 +1,4 @@ -import { HostProcessHostname, HostProcessPlatform } from "@t3tools/shared/hostProcess"; +import { HostProcessEnvironment, HostProcessHostname, HostProcessPlatform } from "@t3tools/shared/hostProcess"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Option from "effect/Option"; @@ -179,6 +179,55 @@ const resolveFriendlyHostLabel = Effect.fn("resolveFriendlyHostLabel")(function* return null; }); +const isWslEnvironment = Effect.fn("isWslEnvironment")(function* () { + const platform = yield* HostProcessPlatform; + if (platform !== "linux") { + return false; + } + + const fileSystem = yield* FileSystem.FileSystem; + const osReleasePath = "/proc/sys/kernel/osrelease"; + + const content = yield* fileSystem.exists(osReleasePath).pipe( + Effect.mapError( + (cause) => + new ServerEnvironmentLabelFileError({ + operation: "inspect", + path: osReleasePath, + cause, + }), + ), + Effect.flatMap((exists) => + exists + ? fileSystem.readFileString(osReleasePath).pipe( + Effect.mapError( + (cause) => + new ServerEnvironmentLabelFileError({ + operation: "read", + path: osReleasePath, + cause, + }), + ), + ) + : Effect.succeed("") + ), + Effect.catchTags({ + ServerEnvironmentLabelFileError: (error) => + Effect.logDebug(error.message).pipe( + Effect.annotateLogs({ + operation: error.operation, + path: error.path, + cause: error, + }), + Effect.as(""), + ), + }), + ); + + const lowerContent = content.toLowerCase(); + return lowerContent.includes("microsoft") || lowerContent.includes("wsl"); +}); + export const resolveServerEnvironmentLabel = Effect.fn("resolveServerEnvironmentLabel")(function* ( input: ResolveServerEnvironmentLabelInput, ) { @@ -189,6 +238,15 @@ export const resolveServerEnvironmentLabel = Effect.fn("resolveServerEnvironment const hostname = normalizeLabel(yield* HostProcessHostname); if (hostname) { + const isWsl = yield* isWslEnvironment(); + if (isWsl) { + const env = yield* HostProcessEnvironment; + const distroName = env["WSL_DISTRO_NAME"]; + if (distroName && distroName.trim().length > 0) { + return `${hostname} (WSL: ${distroName.trim()})`; + } + return `${hostname} (WSL)`; + } return hostname; }