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
28 changes: 21 additions & 7 deletions apps/server/src/device/DeviceHubOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ const HubActionResult = Schema.Struct({
),
});

// Hub diagnostics can carry a whole stderr dump.
const boundDiagnostics = (text: string) =>
text.length > 2_000 ? `${text.slice(0, 1_999)}…` : text;

// The hub's `error` is a util.inspect of Node's exec error; the stack and the
// inspected fields follow the first frame, so keep the lines before it.
const errorHead = (error: string) => {
const lines = error.split(/\r?\n/);
const firstFrame = lines.findIndex((line) => /^\s+at /.test(line));
return (firstFrame === -1 ? lines : lines.slice(0, firstFrame))
.map((line) => line.trim())
.filter(Boolean)
.join(" ");
};

const vendorPrefix = (platform: DevicePlatform) =>
platform === "ios" ? "/vendor/serve-sim" : "/vendor/serve-emu";

Expand Down Expand Up @@ -109,7 +124,11 @@ export const makeDeviceHubOperations = (options: {
/(?:^Error:\s*ENOENT:|\bcode:\s*['"]ENOENT['"])/.test(error.error ?? "")
),
);
const details = warnings?.map((error) => error.message) ?? [];
const details =
warnings?.map((error) => {
const head = errorHead(error.error ?? "");
return boundDiagnostics(head ? `${error.message} ${head}` : error.message);
}) ?? [];
let androidDiscoveryIncomplete = false;
if (androidAvailable) {
const avds = yield* run("emulator", ["-list-avds"]);
Expand Down Expand Up @@ -167,12 +186,7 @@ export const makeDeviceHubOperations = (options: {
: /timed? out|timeout/i.test(diagnostics)
? "timeout"
: "launch_failed",
...(diagnostics
? {
diagnostics:
diagnostics.length > 2_000 ? `${diagnostics.slice(0, 1_999)}…` : diagnostics,
}
: {}),
...(diagnostics ? { diagnostics: boundDiagnostics(diagnostics) } : {}),
cause: result,
});
}
Expand Down
51 changes: 51 additions & 0 deletions apps/server/src/device/DeviceService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,57 @@ it.effect("hides Android discovery failures when the host has no Android SDK", (
}).pipe(Effect.scoped),
);

// The hub reports `error` as a util.inspect of Node's exec error.
const simctlMessage = "[apple-utils] Failed to run `xcrun simctl list devices --json`:";
const simctlCause =
"An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=60): Unable to connect to CoreSimulatorService [xcrun shim fail mode]";
const simctlError = `Error: Command failed: xcrun simctl list devices --json
${simctlCause}

at genericNodeError (node:internal/errors:986:15)
at wrappedFn (node:internal/errors:540:14)
at ChildProcess.exithandler (node:child_process:417:12)
at ChildProcess._handle.onexit (node:internal/child_process:335:5) {
code: 1,
killed: false,
signal: null,
cmd: 'xcrun simctl list devices --json',
stdout: '',
stderr: '${simctlCause}\\n'
}`;
const avdmanager = "/Users/test/Library/Android/sdk/cmdline-tools/latest/bin/avdmanager";
const avdmanagerError = `Error: spawn ${avdmanager} ENOENT
at ChildProcess._handle.onexit (node:internal/child_process:316:19)
at onErrorNT (node:internal/child_process:524:16) {
errno: -2,
code: 'ENOENT',
syscall: 'spawn ${avdmanager}',
path: '${avdmanager}',
spawnargs: [ 'list', 'avd' ],
cmd: '${avdmanager} list avd',
stdout: '',
stderr: ''
}`;

it.effect("keeps the cause from the hub's error in discovery warnings, without the stack", () =>
Effect.gen(function* () {
const { service, discoveryErrors } = yield* fixture();
yield* Ref.set(discoveryErrors, [
{ message: simctlMessage, error: simctlError },
{ message: "[android-utils] Failed to run `avdmanager list avd`:", error: avdmanagerError },
]);
const state = yield* service.configure({ enabled: true });
expect(state.hostStatusDetail).toBe(
[
`${simctlMessage} Error: Command failed: xcrun simctl list devices --json ${simctlCause}`,
`[android-utils] Failed to run \`avdmanager list avd\`: Error: spawn ${avdmanager} ENOENT`,
].join("\n"),
);
expect(state.hostStatusDetail).not.toContain(" at ");
expect(state.hostStatusDetail).not.toContain("stderr:");
}).pipe(Effect.scoped),
);

it.effect.each([
{
diagnostic: "Insufficient disk space at /private/user/path",
Expand Down
Loading