From cc2db7fe88523b034eb7e768109512100c4ae828 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sat, 20 Jun 2026 05:11:32 -0700 Subject: [PATCH 1/2] Structure server environment label failures Co-authored-by: codex --- .../ServerEnvironmentLabel.test.ts | 142 +++++++++++++++--- .../src/environment/ServerEnvironmentLabel.ts | 96 ++++++++++-- 2 files changed, 205 insertions(+), 33 deletions(-) diff --git a/apps/server/src/environment/ServerEnvironmentLabel.test.ts b/apps/server/src/environment/ServerEnvironmentLabel.test.ts index 4bc9647fba5..f1b66a9d63e 100644 --- a/apps/server/src/environment/ServerEnvironmentLabel.test.ts +++ b/apps/server/src/environment/ServerEnvironmentLabel.test.ts @@ -2,12 +2,28 @@ import { afterEach, describe, expect, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; +import * as Logger from "effect/Logger"; +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 { vi } from "vite-plus/test"; import * as ProcessRunner from "../processRunner.ts"; -import { resolveServerEnvironmentLabel } from "./ServerEnvironmentLabel.ts"; +import * as ServerEnvironmentLabel from "./ServerEnvironmentLabel.ts"; + +const isServerEnvironmentLabelFileError = Schema.is( + ServerEnvironmentLabel.ServerEnvironmentLabelFileError, +); +const isServerEnvironmentLabelCommandError = Schema.is( + ServerEnvironmentLabel.ServerEnvironmentLabelCommandError, +); + +interface CapturedLog { + readonly message: unknown; + readonly annotations: Readonly>; +} const runMock = vi.fn(); @@ -47,7 +63,7 @@ afterEach(() => { describe("resolveServerEnvironmentLabel", () => { it.effect("uses hostname fallback regardless of launch mode", () => Effect.gen(function* () { - const result = yield* resolveServerEnvironmentLabel({ + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ cwdBaseName: "t3code", }).pipe(Effect.provide(withHostPlatform(TestLayer, "win32", "macbook-pro"))); @@ -68,7 +84,7 @@ describe("resolveServerEnvironmentLabel", () => { }), ); - const result = yield* resolveServerEnvironmentLabel({ + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ cwdBaseName: "t3code", }).pipe(Effect.provide(withHostPlatform(TestLayer, "darwin", "macbook-pro"))); @@ -85,7 +101,7 @@ describe("resolveServerEnvironmentLabel", () => { it.effect("prefers Linux PRETTY_HOSTNAME from machine-info", () => Effect.gen(function* () { - const result = yield* resolveServerEnvironmentLabel({ + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ cwdBaseName: "t3code", }).pipe(Effect.provide(withHostPlatform(LinuxMachineInfoLayer, "linux", "buildbox"))); @@ -107,7 +123,7 @@ describe("resolveServerEnvironmentLabel", () => { }), ); - const result = yield* resolveServerEnvironmentLabel({ + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ cwdBaseName: "t3code", }).pipe(Effect.provide(withHostPlatform(TestLayer, "linux", "runner-01"))); @@ -124,7 +140,7 @@ describe("resolveServerEnvironmentLabel", () => { it.effect("falls back to the hostname when friendly labels are unavailable", () => Effect.gen(function* () { - const result = yield* resolveServerEnvironmentLabel({ + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ cwdBaseName: "t3code", }).pipe(Effect.provide(withHostPlatform(TestLayer, "win32", "JULIUS-LAPTOP"))); @@ -132,25 +148,107 @@ describe("resolveServerEnvironmentLabel", () => { }), ); - it.effect("falls back to the hostname when the friendly-label command is missing", () => - Effect.gen(function* () { - runMock.mockReturnValueOnce( - Effect.fail( - new ProcessRunner.ProcessSpawnError({ - command: "scutil", - argumentCount: 2, - cause: new Error("spawn scutil ENOENT"), - }), - ), - ); + it.effect("falls back to the hostname when the friendly-label command is missing", () => { + const logs: CapturedLog[] = []; + const logger = Logger.make(({ fiber, message }) => { + logs.push({ + message, + annotations: fiber.getRef(References.CurrentLogAnnotations), + }); + }); + const spawnCause = new Error("spawn scutil ENOENT"); + const processError = new ProcessRunner.ProcessSpawnError({ + command: "scutil", + argumentCount: 2, + cause: spawnCause, + }); + runMock.mockReturnValueOnce(Effect.fail(processError)); - const result = yield* resolveServerEnvironmentLabel({ + return Effect.gen(function* () { + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ cwdBaseName: "t3code", - }).pipe(Effect.provide(withHostPlatform(TestLayer, "darwin", "macbook-pro"))); + }); expect(result).toBe("macbook-pro"); - }), - ); + expect(logs[0]?.message).toEqual([ + "Failed to run environment-label command 'scutil --get ComputerName'.", + ]); + const error = logs[0]?.annotations.cause; + expect(isServerEnvironmentLabelCommandError(error)).toBe(true); + if (isServerEnvironmentLabelCommandError(error)) { + expect(error.command).toBe("scutil"); + expect(error.args).toEqual(["--get", "ComputerName"]); + expect(error.cause).toBe(processError); + expect(processError.cause).toBe(spawnCause); + } + }).pipe( + Effect.provide( + Layer.mergeAll( + withHostPlatform(TestLayer, "darwin", "macbook-pro"), + Logger.layer([logger], { mergeWithExisting: false }), + Layer.succeed(References.MinimumLogLevel, "Debug"), + ), + ), + ); + }); + + it.effect("continues to hostnamectl after a machine-info inspect failure", () => { + const logs: CapturedLog[] = []; + const logger = Logger.make(({ fiber, message }) => { + logs.push({ + message, + annotations: fiber.getRef(References.CurrentLogAnnotations), + }); + }); + const fileCause = new Error("permission denied"); + const platformError = PlatformError.systemError({ + _tag: "PermissionDenied", + module: "FileSystem", + method: "exists", + pathOrDescriptor: "/etc/machine-info", + cause: fileCause, + }); + const fileSystemLayer = FileSystem.layerNoop({ + exists: () => Effect.fail(platformError), + }); + runMock.mockReturnValueOnce( + Effect.succeed({ + stdout: "CI Runner\n", + stderr: "", + code: ChildProcessSpawner.ExitCode(0), + timedOut: false, + stdoutTruncated: false, + stderrTruncated: false, + }), + ); + + return Effect.gen(function* () { + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ + cwdBaseName: "t3code", + }); + + expect(result).toBe("CI Runner"); + expect(logs[0]?.message).toEqual([ + "Failed to inspect environment-label file at /etc/machine-info.", + ]); + const error = logs[0]?.annotations.cause; + expect(isServerEnvironmentLabelFileError(error)).toBe(true); + if (isServerEnvironmentLabelFileError(error)) { + expect(error.operation).toBe("inspect"); + expect(error.path).toBe("/etc/machine-info"); + expect(error.cause).toBe(platformError); + expect(platformError.cause).toBe(fileCause); + } + }).pipe( + Effect.provide( + Layer.mergeAll( + withHostPlatform(Layer.merge(ProcessRunnerTest, fileSystemLayer), "linux", "buildbox"), + Logger.layer([logger], { mergeWithExisting: false }), + Layer.succeed(References.MinimumLogLevel, "Debug"), + ), + ), + ); + }); it.effect("falls back to the cwd basename when the hostname is blank", () => Effect.gen(function* () { @@ -165,7 +263,7 @@ describe("resolveServerEnvironmentLabel", () => { }), ); - const result = yield* resolveServerEnvironmentLabel({ + const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ cwdBaseName: "t3code", }).pipe(Effect.provide(withHostPlatform(TestLayer, "linux", " "))); diff --git a/apps/server/src/environment/ServerEnvironmentLabel.ts b/apps/server/src/environment/ServerEnvironmentLabel.ts index 83c3b8bad8e..9c9598427ff 100644 --- a/apps/server/src/environment/ServerEnvironmentLabel.ts +++ b/apps/server/src/environment/ServerEnvironmentLabel.ts @@ -2,6 +2,7 @@ import { HostProcessHostname, HostProcessPlatform } from "@t3tools/shared/hostPr import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Option from "effect/Option"; +import * as Schema from "effect/Schema"; import * as ProcessRunner from "../processRunner.ts"; @@ -9,6 +10,32 @@ interface ResolveServerEnvironmentLabelInput { readonly cwdBaseName: string; } +export class ServerEnvironmentLabelFileError extends Schema.TaggedErrorClass()( + "ServerEnvironmentLabelFileError", + { + operation: Schema.Literals(["inspect", "read"]), + path: Schema.String, + cause: Schema.Defect(), + }, +) { + override get message(): string { + return `Failed to ${this.operation} environment-label file at ${this.path}.`; + } +} + +export class ServerEnvironmentLabelCommandError extends Schema.TaggedErrorClass()( + "ServerEnvironmentLabelCommandError", + { + command: Schema.String, + args: Schema.Array(Schema.String), + cause: Schema.Defect(), + }, +) { + override get message(): string { + return `Failed to run environment-label command '${[this.command, ...this.args].join(" ")}'.`; + } +} + function normalizeLabel(value: string | null | undefined): string | null { const trimmed = value?.trim(); return trimmed && trimmed.length > 0 ? trimmed : null; @@ -34,16 +61,42 @@ function parseMachineInfoValue(raw: string, key: string): string | null { const readLinuxMachineInfo = Effect.fn("readLinuxMachineInfo")(function* () { const fileSystem = yield* FileSystem.FileSystem; - const exists = yield* fileSystem - .exists("/etc/machine-info") - .pipe(Effect.orElseSucceed(() => false)); - if (!exists) { - return null; - } - - return yield* fileSystem - .readFileString("/etc/machine-info") - .pipe(Effect.orElseSucceed(() => null)); + const machineInfoPath = "/etc/machine-info"; + return yield* fileSystem.exists(machineInfoPath).pipe( + Effect.mapError( + (cause) => + new ServerEnvironmentLabelFileError({ + operation: "inspect", + path: machineInfoPath, + cause, + }), + ), + Effect.flatMap((exists) => + exists + ? fileSystem.readFileString(machineInfoPath).pipe( + Effect.mapError( + (cause) => + new ServerEnvironmentLabelFileError({ + operation: "read", + path: machineInfoPath, + cause, + }), + ), + ) + : Effect.succeed(null), + ), + Effect.catchTags({ + ServerEnvironmentLabelFileError: (error) => + Effect.logDebug(error.message).pipe( + Effect.annotateLogs({ + operation: error.operation, + path: error.path, + cause: error, + }), + Effect.as(null), + ), + }), + ); }); const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* ( @@ -57,7 +110,28 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* ( args, timeoutBehavior: "timedOutResult", }) - .pipe(Effect.option); + .pipe( + Effect.mapError( + (cause) => + new ServerEnvironmentLabelCommandError({ + command, + args, + cause, + }), + ), + Effect.map(Option.some), + Effect.catchTags({ + ServerEnvironmentLabelCommandError: (error) => + Effect.logDebug(error.message).pipe( + Effect.annotateLogs({ + command: error.command, + args: error.args, + cause: error, + }), + Effect.as(Option.none()), + ), + }), + ); if (Option.isNone(result) || result.value.code !== 0) { return null; From 2ee5facb28d97904139467802fc823cad3849e2c Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sat, 20 Jun 2026 10:02:01 -0700 Subject: [PATCH 2/2] fix(server): sanitize environment-label probe diagnostics Co-authored-by: codex --- .../ServerEnvironmentLabel.test.ts | 10 ++-- .../src/environment/ServerEnvironmentLabel.ts | 48 +++++++++++++------ 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/apps/server/src/environment/ServerEnvironmentLabel.test.ts b/apps/server/src/environment/ServerEnvironmentLabel.test.ts index f1b66a9d63e..b5bb8a8ff1c 100644 --- a/apps/server/src/environment/ServerEnvironmentLabel.test.ts +++ b/apps/server/src/environment/ServerEnvironmentLabel.test.ts @@ -171,13 +171,17 @@ describe("resolveServerEnvironmentLabel", () => { expect(result).toBe("macbook-pro"); expect(logs[0]?.message).toEqual([ - "Failed to run environment-label command 'scutil --get ComputerName'.", + "Failed to run environment-label probe 'macos-computer-name' with scutil.", ]); const error = logs[0]?.annotations.cause; expect(isServerEnvironmentLabelCommandError(error)).toBe(true); if (isServerEnvironmentLabelCommandError(error)) { - expect(error.command).toBe("scutil"); - expect(error.args).toEqual(["--get", "ComputerName"]); + expect(error.probe).toBe("macos-computer-name"); + expect(error.executable).toBe("scutil"); + expect(error.argumentCount).toBe(2); + expect(error).not.toHaveProperty("args"); + expect(error.message).not.toContain("--get"); + expect(error.message).not.toContain("ComputerName"); expect(error.cause).toBe(processError); expect(processError.cause).toBe(spawnCause); } diff --git a/apps/server/src/environment/ServerEnvironmentLabel.ts b/apps/server/src/environment/ServerEnvironmentLabel.ts index 9c9598427ff..bd034e0fa26 100644 --- a/apps/server/src/environment/ServerEnvironmentLabel.ts +++ b/apps/server/src/environment/ServerEnvironmentLabel.ts @@ -10,6 +10,12 @@ interface ResolveServerEnvironmentLabelInput { readonly cwdBaseName: string; } +const ServerEnvironmentLabelCommandProbe = Schema.Literals([ + "macos-computer-name", + "linux-pretty-hostname", +]); +type ServerEnvironmentLabelCommandProbe = typeof ServerEnvironmentLabelCommandProbe.Type; + export class ServerEnvironmentLabelFileError extends Schema.TaggedErrorClass()( "ServerEnvironmentLabelFileError", { @@ -26,13 +32,14 @@ export class ServerEnvironmentLabelFileError extends Schema.TaggedErrorClass()( "ServerEnvironmentLabelCommandError", { - command: Schema.String, - args: Schema.Array(Schema.String), + probe: ServerEnvironmentLabelCommandProbe, + executable: Schema.String, + argumentCount: Schema.Number, cause: Schema.Defect(), }, ) { override get message(): string { - return `Failed to run environment-label command '${[this.command, ...this.args].join(" ")}'.`; + return `Failed to run environment-label probe '${this.probe}' with ${this.executable}.`; } } @@ -99,23 +106,25 @@ const readLinuxMachineInfo = Effect.fn("readLinuxMachineInfo")(function* () { ); }); -const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* ( - command: string, - args: readonly string[], -) { +const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* (input: { + readonly probe: ServerEnvironmentLabelCommandProbe; + readonly command: string; + readonly args: readonly string[]; +}) { const processRunner = yield* ProcessRunner.ProcessRunner; const result = yield* processRunner .run({ - command, - args, + command: input.command, + args: input.args, timeoutBehavior: "timedOutResult", }) .pipe( Effect.mapError( (cause) => new ServerEnvironmentLabelCommandError({ - command, - args, + probe: input.probe, + executable: input.command, + argumentCount: input.args.length, cause, }), ), @@ -124,8 +133,9 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* ( ServerEnvironmentLabelCommandError: (error) => Effect.logDebug(error.message).pipe( Effect.annotateLogs({ - command: error.command, - args: error.args, + probe: error.probe, + executable: error.executable, + argumentCount: error.argumentCount, cause: error, }), Effect.as(Option.none()), @@ -143,7 +153,11 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* ( const resolveFriendlyHostLabel = Effect.fn("resolveFriendlyHostLabel")(function* () { const platform = yield* HostProcessPlatform; if (platform === "darwin") { - return yield* runFriendlyLabelCommand("scutil", ["--get", "ComputerName"]); + return yield* runFriendlyLabelCommand({ + probe: "macos-computer-name", + command: "scutil", + args: ["--get", "ComputerName"], + }); } if (platform === "linux") { @@ -155,7 +169,11 @@ const resolveFriendlyHostLabel = Effect.fn("resolveFriendlyHostLabel")(function* } } - return yield* runFriendlyLabelCommand("hostnamectl", ["--pretty"]); + return yield* runFriendlyLabelCommand({ + probe: "linux-pretty-hostname", + command: "hostnamectl", + args: ["--pretty"], + }); } return null;