History

fix deno detection

Rhys Sullivan committed 8 months ago

Commit b4898091 parent 04294d26 files · +110 −58Browse files

Unified · Split

6 files
  1. .github/workflows/publish-executor-package.yml +5 −0
  2. apps/executor/src/cli/main.ts +53 −50
  3. package.json +1 −0
  4. packages/runtime-deno-subprocess/src/index.test.ts +21 −0
  5. packages/runtime-deno-subprocess/src/index.ts +26 −8
  6. turbo.json +4 −0
.github/workflows/publish-executor-package.yml+5−0
@@ -39,6 +39,11 @@ jobs: · 38 unmodified lines
3939 node-version: 22
4040 registry-url: https://registry.npmjs.org
4141
42+ - name: Setup Deno
43+ uses: denoland/setup-deno@v2
44+ with:
45+ deno-version: v2.x
46+
4247 - name: Install dependencies
4348 run: bun install --frozen-lockfile
4449
apps/executor/src/cli/main.ts+53−50
@@ -538,8 +538,14 @@ type LocalServerStatus = { · 537 unmodified lines
538538 accountId: string;
539539 workspaceId: string;
540540 } | null;
541+ denoVersion: string | null;
541542 };
542543
544+const renderDenoSandboxDetail = (denoVersion: string | null): string =>
545+ denoVersion !== null
546+ ? `deno ${denoVersion}`
547+ : "deno not found (run `executor sandbox` to install)";
548+
543549 const getServerStatus = (baseUrl: string): Effect.Effect<LocalServerStatus, Error, never> =>
544550 Effect.gen(function* () {
545551 const pidRecord = yield* readPidRecord();
@@ -554,6 +560,7 @@ const getServerStatus = (baseUrl: string): Effect.Effect<LocalServerStatus, Erro · 8 unmodified lines
554560 const pid = typeof pidRecord?.pid === "number" ? pidRecord.pid : null;
555561 const pidRunning = pid !== null ? isPidRunning(pid) : false;
556562 const logFile = pidRecord?.logFile ?? DEFAULT_SERVER_LOG_FILE;
563+ const denoVersion = yield* getDenoVersion();
557564
558565 return {
559566 baseUrl,
@@ -566,6 +573,7 @@ const getServerStatus = (baseUrl: string): Effect.Effect<LocalServerStatus, Erro · 6 unmodified lines
566573 webAssetsDir: resolveRuntimeWebAssetsDir(),
567574 migrationsDir: resolveRuntimeMigrationsDir(),
568575 installation,
576+ denoVersion,
569577 } satisfies LocalServerStatus;
570578 });
571579
@@ -581,60 +589,55 @@ const renderStatus = (status: LocalServerStatus): string => · 9 unmodified lines
581589 `webAssetsDir: ${status.webAssetsDir ?? "missing"}`,
582590 `migrationsDir: ${status.migrationsDir ?? "missing"}`,
583591 `workspaceId: ${status.installation?.workspaceId ?? "unavailable"}`,
592+ `denoSandbox: ${renderDenoSandboxDetail(status.denoVersion)}`,
584593 ].join("\n");
585594
586595 const getDoctorReport = (baseUrl: string) =>
587596 getServerStatus(baseUrl).pipe(
588− Effect.flatMap((status) =>
589− getDenoVersion().pipe(
590− Effect.map((denoVersion) => {
591− const checks = {
592− serverReachable: {
593− ok: status.reachable,
594− detail: status.reachable ? `reachable at ${status.baseUrl}` : `not reachable at ${status.baseUrl}`,
595− },
596− pidFile: {
597− ok: status.pid !== null,
598− detail: status.pid !== null ? `pid ${status.pid}` : `missing pid file at ${status.pidFile}`,
599− },
600− process: {
601− ok: status.pidRunning,
602− detail: status.pidRunning ? `pid ${status.pid}` : "no live daemon process recorded",
603− },
604− database: {
605− ok: status.localDataDir.length > 0,
606− detail: status.localDataDir,
607− },
608− webAssets: {
609− ok: status.webAssetsDir !== null,
610− detail: status.webAssetsDir ?? "missing bundled web assets",
611− },
612− migrations: {
613− ok: status.migrationsDir !== null,
614− detail: status.migrationsDir ?? "missing migrations directory",
615− },
616− installation: {
617− ok: status.installation !== null,
618− detail: status.installation
619− ? `workspace ${status.installation.workspaceId}`
620− : "local installation unavailable",
621− },
622− denoSandbox: {
623− ok: denoVersion !== null,
624− detail: denoVersion !== null
625− ? `deno ${denoVersion}`
626− : "deno not found (run `executor sandbox` to install)",
627− },
628− } as const;
629−
630− return {
631− ok: Object.values(checks).every((check) => check.ok),
632− status,
633− checks,
634− };
635− }),
636− ),
637− ),
597+ Effect.map((status) => {
598+ const checks = {
599+ serverReachable: {
600+ ok: status.reachable,
601+ detail: status.reachable ? `reachable at ${status.baseUrl}` : `not reachable at ${status.baseUrl}`,
602+ },
603+ pidFile: {
604+ ok: status.pid !== null,
605+ detail: status.pid !== null ? `pid ${status.pid}` : `missing pid file at ${status.pidFile}`,
606+ },
607+ process: {
608+ ok: status.pidRunning,
609+ detail: status.pidRunning ? `pid ${status.pid}` : "no live daemon process recorded",
610+ },
611+ database: {
612+ ok: status.localDataDir.length > 0,
613+ detail: status.localDataDir,
614+ },
615+ webAssets: {
616+ ok: status.webAssetsDir !== null,
617+ detail: status.webAssetsDir ?? "missing bundled web assets",
618+ },
619+ migrations: {
620+ ok: status.migrationsDir !== null,
621+ detail: status.migrationsDir ?? "missing migrations directory",
622+ },
623+ installation: {
624+ ok: status.installation !== null,
625+ detail: status.installation
626+ ? `workspace ${status.installation.workspaceId}`
627+ : "local installation unavailable",
628+ },
629+ denoSandbox: {
630+ ok: status.denoVersion !== null,
631+ detail: renderDenoSandboxDetail(status.denoVersion),
632+ },
633+ } as const;
634+
635+ return {
636+ ok: Object.values(checks).every((check) => check.ok),
637+ status,
638+ checks,
639+ };
640+ }),
638641 );
639642
640643 const printJson = (value: unknown) =>
package.json+1−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
2020 },
2121 "scripts": {
2222 "dev": "turbo run dev --filter=@executor/web",
23+ "test": "turbo run test",
2324 "executor": "./apps/executor/bin/executor",
2425 "typecheck": "bun run --filter='*' typecheck",
2526 "knip": "knip"
packages/runtime-deno-subprocess/src/index.test.ts+21−0
@@ -43,6 +43,27 @@ const tools = { · 42 unmodified lines
4343 }),
4444 };
4545
46+it("reports unavailable Deno executables", () => {
47+ expect(isDenoAvailable("__executor_missing_deno__")).toBe(false);
48+});
49+
50+it.effect("returns an actionable error when Deno is missing", () =>
51+ Effect.gen(function* () {
52+ const executor = makeDenoSubprocessExecutor({
53+ denoExecutable: "__executor_missing_deno__",
54+ });
55+ const toolInvoker = makeToolInvokerFromTools({ tools });
56+
57+ const output = yield* executor.execute(
58+ "return 1 + 2;",
59+ toolInvoker,
60+ );
61+
62+ expect(output.result).toBeNull();
63+ expect(output.error).toContain("Install Deno or set DENO_BIN");
64+ }),
65+);
66+
4667 const skipUnlessDeno = isDenoAvailable()
4768 ? describe
4869 : describe.skip;
packages/runtime-deno-subprocess/src/index.ts+26−8
@@ -1,3 +1,4 @@
1+import { spawnSync } from "node:child_process";
12 import { existsSync } from "node:fs";
23 import { fileURLToPath } from "node:url";
34
@@ -91,6 +92,21 @@ const defaultDenoExecutable = (): string => { · 87 unmodified lines
9192 return "deno";
9293 };
9394
95+const formatDenoSpawnError = (
96+ cause: unknown,
97+ executable: string,
98+): string => {
99+ const code = typeof cause === "object" && cause !== null && "code" in cause
100+ ? String((cause as { code?: unknown }).code)
101+ : null;
102+
103+ if (code === "ENOENT") {
104+ return `Failed to spawn Deno subprocess: Deno executable "${executable}" was not found. Install Deno or set DENO_BIN.`;
105+ }
106+
107+ return `Failed to spawn Deno subprocess: ${cause instanceof Error ? cause.message : String(cause)}`;
108+};
109+
94110 // --------------------------------------------------------------------------
95111 // Worker script resolution
96112 // --------------------------------------------------------------------------
@@ -294,9 +310,7 @@ const executeInDeno = ( · 197 unmodified lines
294310 stderrBuffer += chunk;
295311 },
296312 onError: (cause) => {
297− fail(
298− `Failed to spawn Deno subprocess: ${cause.message}`,
299− );
313+ fail(formatDenoSpawnError(cause, denoExecutable));
300314 },
301315 onExit: (exitCode, signal) => {
302316 if (settled) {
@@ -310,9 +324,7 @@ const executeInDeno = ( · 7 unmodified lines
310324 },
311325 );
312326 } catch (cause) {
313− fail(
314− `Failed to spawn Deno subprocess: ${cause instanceof Error ? cause.message : String(cause)}`,
315− );
327+ fail(formatDenoSpawnError(cause, denoExecutable));
316328 return;
317329 }
318330
@@ -334,8 +346,14 @@ const executeInDeno = ( · 15 unmodified lines
334346 */
335347 export const isDenoAvailable = (
336348 executable: string = defaultDenoExecutable(),
337−): boolean =>
338− executable.includes("/") ? existsSync(executable) : true;
349+): boolean => {
350+ const result = spawnSync(executable, ["--version"], {
351+ stdio: "ignore",
352+ timeout: 5000,
353+ });
354+
355+ return result.error === undefined && result.status === 0;
356+};
339357
340358 /**
341359 * Create a `CodeExecutor` that runs code in a sandboxed Deno subprocess.
turbo.json+4−0
@@ -5,6 +5,10 @@ · 4 unmodified lines
55 "dependsOn": ["^build"],
66 "outputs": ["dist/**"]
77 },
8+ "test": {
9+ "dependsOn": ["^test"],
10+ "outputs": []
11+ },
812 "typecheck": {
913 "dependsOn": ["^typecheck"],
1014 "outputs": []

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t