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
30 changes: 30 additions & 0 deletions apps/desktop/src/backend/DesktopBackendConfiguration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ describe("DesktopBackendConfiguration", () => {
archiveHash: "b".repeat(64),
wsl: () => ({
prepareRuntime: () => ({ ok: false, reason: "archive is corrupt" }),
invalidateRuntime: () => Effect.die("failed staging must not invalidate a runtime"),
ensureNodePty: (_distro, root) => {
observedNodePtyRoots.push(root);
return { ok: true, nodePath: "/usr/bin/node", resolvedPath: "/usr/bin:/bin" };
Expand Down Expand Up @@ -615,6 +616,35 @@ describe("DesktopBackendConfiguration", () => {
);
});

it.effect("resolveWsl preserves the staging error when the mounted fallback is fatal", () =>
withPackagedWslHarness(
{
archiveHash: "b".repeat(64),
wsl: () => ({
prepareRuntime: () => ({
ok: false,
reason:
"t3: error while loading shared libraries: libatomic.so.1: cannot open shared object file",
}),
ensureNodePty: () => ({
ok: false,
reason: "the packaged Linux node-pty binary was not included",
fatal: true,
}),
}),
},
() =>
Effect.gen(function* () {
const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;
const config = yield* configuration.resolveWsl({ port: 5000, distro: "Ubuntu" });
const failure = Option.getOrThrow(config.preflightFailure);

assert.isTrue(failure.fatal);
assert.include(failure.reason, "libatomic.so.1");
}),
),
);

it.effect("resolveWsl keeps the staged runtime when the mounted tree fails too", () => {
const stagedAppRoot = "/home/test/.t3/wsl-runtime/cache";
const invalidatedRuntimeIds: string[] = [];
Expand Down
5 changes: 3 additions & 2 deletions apps/desktop/src/backend/DesktopBackendConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ const runWslPreflight = Effect.fn("desktop.backendConfiguration.wslPreflight")(f
: ({ ok: true, windowsEntryPath, linuxAppRoot: mountedAppRoot.value } as const);
});

let stagedFailure: { readonly runtimeId: string; readonly reason: string } | undefined;
let stagedFailure: { readonly runtimeId?: string; readonly reason: string } | undefined;
const failedStaged = (failure: { readonly reason: string }) =>
({
_tag: "Failed",
Expand Down Expand Up @@ -417,6 +417,7 @@ const runWslPreflight = Effect.fn("desktop.backendConfiguration.wslPreflight")(f
);
stagedFailure = { runtimeId: input.runtimeArchive.runtimeId, reason: stagedProbe.reason };
} else {
stagedFailure = { reason: runtime.reason };
yield* Effect.logWarning(
"Could not stage the WSL runtime; launching from the mounted server tree instead.",
{ reason: runtime.reason },
Expand Down Expand Up @@ -446,7 +447,7 @@ const runWslPreflight = Effect.fn("desktop.backendConfiguration.wslPreflight")(f
: failedNodePty(nodePtyResult);
}

if (stagedFailure) {
if (stagedFailure?.runtimeId) {
yield* wslEnv.invalidateRuntime(runningDistro, stagedFailure.runtimeId);
}

Expand Down
20 changes: 16 additions & 4 deletions apps/desktop/src/wsl/DesktopWslEnvironment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ describe("WSL runtime cache", () => {

expect(script).toContain('runtime_parent="$HOME/.t3/wsl-runtime"');
expect(script).toContain(' [ -f "$ready_marker" ] &&');
expect(script).toContain(' runtime_entry_runs "$runtime_root" &&');
expect(script).toContain(' runtime_entry_runs "$runtime_root" 2>/dev/null &&');
expect(script).toContain("if runtime_is_ready; then");
expect(script).not.toContain("bin.mjs");
expect(script).not.toContain("node-pty");
Expand Down Expand Up @@ -262,7 +262,7 @@ describe("WSL runtime cache", () => {
"b".repeat(64),
);

expect(script).toContain(' [ -x "$1/t3" ] && "$1/t3" --version >/dev/null 2>&1');
expect(script).toContain(' [ -x "$1/t3" ] && "$1/t3" --version >/dev/null');

const entryCheckDefined = script.indexOf("runtime_entry_runs() {");
const readinessDefined = script.indexOf("runtime_is_ready() {");
Expand Down Expand Up @@ -374,14 +374,14 @@ describe.skipIf(posixShellRunner === null)("WSL runtime install script (executed
fixtures.length = 0;
});

const createFixture = () => {
const createFixture = (serverEntry = SERVER_ENTRY_SOURCE) => {
const result = runShell(
[
"set -eu",
"work=$(mktemp -d)",
'stage="$work/stage/t3-0.0.0-linux-x64"',
'mkdir -p "$stage/node_modules/node-pty/build/Release" "$work/home"',
`printf '%s' ${sh(SERVER_ENTRY_SOURCE)} > "$stage/t3"`,
`printf '%s' ${sh(serverEntry)} > "$stage/t3"`,
'chmod +x "$stage/t3"',
`printf '%s' 'pty-native-payload' > "$stage/node_modules/node-pty/build/Release/pty.node"`,
`tar -czf "$work/wsl-runtime.tar.gz" -C "$work/stage" t3-0.0.0-linux-x64`,
Expand Down Expand Up @@ -495,6 +495,18 @@ describe.skipIf(posixShellRunner === null)("WSL runtime install script (executed
expect(parseWslRuntimeRoot(warm.stdout)).toBe(fixture.runtimeRoot);
});

it("retains the loader error when a bundled executable needs a missing system library", () => {
const fixture = createFixture(
'#!/bin/sh\nprintf "error while loading shared libraries: libatomic.so.1: cannot open shared object file\\n" >&2\nexit 127\n',
);

const result = fixture.install();

expect(result.status).not.toBe(0);
expect(result.stderr).toContain("libatomic.so.1");
expect(parseWslRuntimeRoot(result.stdout)).toBeNull();
});

it("reinstalls a cache whose executable was truncated", () => {
const fixture = createFixture();
expect(fixture.install().status).toBe(0);
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/wsl/DesktopWslEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export const buildWslRuntimeInstallScript = (
`runtime_root="$runtime_parent/${safeRuntimeId}"`,
`ready_marker="$runtime_root/${WSL_RUNTIME_READY_MARKER}"`,
"runtime_entry_runs() {",
' [ -x "$1/t3" ] && "$1/t3" --version >/dev/null 2>&1',
' [ -x "$1/t3" ] && "$1/t3" --version >/dev/null',
"}",
// Hashing the entry is what tells a working cache from one whose `t3` was
// swapped or half-written after install: the file is still there and may
Expand All @@ -285,7 +285,7 @@ export const buildWslRuntimeInstallScript = (
"}",
"runtime_is_ready() {",
' [ -f "$ready_marker" ] &&',
' runtime_entry_runs "$runtime_root" &&',
' runtime_entry_runs "$runtime_root" 2>/dev/null &&',
` recorded_entry_digest=$(tr -d '[:space:]' < "$ready_marker" 2>/dev/null) &&`,
' [ -n "$recorded_entry_digest" ] &&',
' [ "$recorded_entry_digest" = "$(runtime_server_entry_digest "$runtime_root")" ]',
Expand Down
Loading