History

Apps console: source management UI and e2e coverage (#1365)

* Add console support for custom app tools * Only claim unambiguous git URLs in connect-dialog detection * Add custom tools console recording * Omit the required key when schema projection empties it * Assert projection output stays JSON-encodable without JSON.parse * Derive app tool schemas via Standard Schema * Cover zod schemas in custom tools e2e * Pass the required target option to Standard Schema converters * Make app sources top-level integrations * Limit local directory app sources to local host * Add local directory picker endpoint * Update local app dependency lockfile * Cover local directory picker in local e2e * Fix CLI node ambient types * Cover custom tools on cloud target * Complete local custom tools picker e2e * Require auth in custom tools git e2e * Exercise custom tools run tab * Cover custom tools collect failure recovery * Handle cloud auth status in custom tools e2e * Show local app source shape in directory picker * Tighten directory picker e2e tag assertion * Remove implicit GitHub connection token from git sources * Package workerd for local binaries * Trust bundled root certificates in workerd outbound TLS workerd's built-in internet service cannot read the Windows system trust store, so npm installs inside the publish worker failed TLS verification. Declare the internet service explicitly with the runtime's bundled Mozilla roots, surface workerd stderr in startup failures, and fail the bundle when a dependency install warning is present instead of publishing a broken bundle. * Add packed-binary custom tools e2e scenarios The new CLI VM scenario proves the compiled executor binary can publish and invoke local-directory custom tools inside macOS, Linux, and Windows guests. The desktop VM scenario proves the packaged sidecar daemon does the same on desktop bundles, and these manual-run VM projects remain outside the default CI chain by design.

Rhys Sullivan committed 4 months ago

Commit 28fdd311 parent f59577164 files · +4946 −290Browse files

Showing the changes to apps/cli/src (2 of 64 files). Show all

Unified · Split

2 files
  1. apps/cli/src/build.ts +92 −1
  2. apps/cli/src/native-bindings.ts +19 −5
apps/cli/src/build.ts+92−1
@@ -2,7 +2,7 @@ import { cp, mkdir, rm, writeFile, chmod } from "node:fs/promises"; · 1 unmodified line
2import { existsSync } from "node:fs";2import { existsSync } from "node:fs";
3import { createRequire } from "node:module";3import { createRequire } from "node:module";
4import { resolve, join, dirname } from "node:path";4import { resolve, join, dirname } from "node:path";
5import { fileURLToPath } from "node:url";5import { fileURLToPath, pathToFileURL } from "node:url";
6import { parseArgs } from "node:util";6import { parseArgs } from "node:util";
7import { $ } from "bun";7import { $ } from "bun";
8 8
@@ -11,6 +11,8 @@ const cliRoot = resolve(repoRoot, "apps/cli"); · 2 unmodified lines
11const webRoot = resolve(repoRoot, "apps/local");11const webRoot = resolve(repoRoot, "apps/local");
12const distDir = resolve(cliRoot, "dist");12const distDir = resolve(cliRoot, "dist");
13const ONEPASSWORD_CORE_WASM_FILENAME = "onepassword-core_bg.wasm";13const ONEPASSWORD_CORE_WASM_FILENAME = "onepassword-core_bg.wasm";
14const WORKERD_VERSION = "1.20260708.1";
15const WORKER_BUNDLER_DIRNAME = "worker-bundler";
14 16
15const resolveQuickJsWasmPath = (): string => {17const resolveQuickJsWasmPath = (): string => {
16 const req = createRequire(join(repoRoot, "packages/kernel/runtime-quickjs/package.json"));18 const req = createRequire(join(repoRoot, "packages/kernel/runtime-quickjs/package.json"));
@@ -33,6 +35,34 @@ const resolveOnePasswordCoreWasmPath = (): string => { · 16 unmodified lines
33 return wasmPath;35 return wasmPath;
34};36};
35 37
38const resolveWorkerBundlerDistPath = (): string => {
39 const req = createRequire(join(repoRoot, "packages/plugins/apps/package.json"));
40 const pkgJson = req.resolve("@cloudflare/worker-bundler/package.json");
41 const distPath = join(dirname(pkgJson), "dist");
42 if (!existsSync(join(distPath, "index.js")) || !existsSync(join(distPath, "esbuild.wasm"))) {
43 throw new Error(`@cloudflare/worker-bundler dist files not found at ${distPath}`);
44 }
45 return distPath;
46};
47
48const createPackedWorkerBundlerSource = async (distPath: string): Promise<string> => {
49 const esbuildPath = join(repoRoot, "node_modules/.bun/node_modules/esbuild/lib/main.js");
50 if (!existsSync(esbuildPath)) throw new Error(`esbuild not found at ${esbuildPath}`);
51 const { build } = await import(pathToFileURL(esbuildPath).href);
52 const result = await build({
53 entryPoints: [join(distPath, "index.js")],
54 bundle: true,
55 format: "esm",
56 platform: "browser",
57 external: ["./esbuild.wasm"],
58 logLevel: "silent",
59 write: false,
60 });
61 const source = result.outputFiles[0]?.text;
62 if (source === undefined) throw new Error("failed to bundle @cloudflare/worker-bundler");
63 return source;
64};
65
36// ---------------------------------------------------------------------------66// ---------------------------------------------------------------------------
37// Metadata67// Metadata
38// ---------------------------------------------------------------------------68// ---------------------------------------------------------------------------
@@ -119,6 +149,7 @@ const bunTarget = (t: Target): Bun.Build.CompileTarget => { · 80 unmodified lines
119};149};
120 150
121const binaryName = (t: Target) => (t.os === "win32" ? "executor.exe" : "executor");151const binaryName = (t: Target) => (t.os === "win32" ? "executor.exe" : "executor");
152const workerdBinaryName = (t: Target) => (t.os === "win32" ? "workerd.exe" : "workerd");
122 153
123const isCurrentPlatform = (t: Target) =>154const isCurrentPlatform = (t: Target) =>
124 t.os === process.platform && t.arch === process.arch && !t.abi;155 t.os === process.platform && t.arch === process.arch && !t.abi;
@@ -211,6 +242,41 @@ const resolveLibsqlNative = (t: Target): string | null => { · 86 unmodified lines
211 }242 }
212};243};
213 244
245const resolveWorkerdBinary = (t: Target): string | null => {
246 const platformMap: Record<string, string> = {
247 "darwin-arm64": "@cloudflare/workerd-darwin-arm64",
248 "darwin-x64": "@cloudflare/workerd-darwin-64",
249 "linux-arm64": "@cloudflare/workerd-linux-arm64",
250 "linux-x64": "@cloudflare/workerd-linux-64",
251 "win32-x64": "@cloudflare/workerd-windows-64",
252 };
253 const key = [t.os, t.arch, t.abi].filter(Boolean).join("-");
254 const pkg = platformMap[key];
255 if (!pkg) return null;
256 const binary = workerdBinaryName(t);
257 try {
258 const req = createRequire(
259 join(repoRoot, "packages/kernel/runtime-workerd-subprocess/package.json"),
260 );
261 const pkgJson = req.resolve(`${pkg}/package.json`);
262 for (const candidate of [
263 join(dirname(pkgJson), "bin", binary),
264 join(dirname(pkgJson), binary),
265 ]) {
266 if (existsSync(candidate)) return candidate;
267 }
268 } catch {
269 const packageRoot = join(
270 repoRoot,
271 `node_modules/.bun/${pkg.replace("/", "+")}@${WORKERD_VERSION}/node_modules/${pkg}`,
272 );
273 for (const candidate of [join(packageRoot, "bin", binary), join(packageRoot, binary)]) {
274 if (existsSync(candidate)) return candidate;
275 }
276 }
277 return null;
278};
279
214// ---------------------------------------------------------------------------280// ---------------------------------------------------------------------------
215// Build mode281// Build mode
216// ---------------------------------------------------------------------------282// ---------------------------------------------------------------------------
@@ -333,6 +399,8 @@ const buildBinaries = async (targets: Target[], mode: BuildMode) => { · 116 unmodified lines
333 399
334 const quickJsWasmPath = resolveQuickJsWasmPath();400 const quickJsWasmPath = resolveQuickJsWasmPath();
335 const onePasswordCoreWasmPath = resolveOnePasswordCoreWasmPath();401 const onePasswordCoreWasmPath = resolveOnePasswordCoreWasmPath();
402 const workerBundlerDistPath = resolveWorkerBundlerDistPath();
403 const packedWorkerBundlerSource = await createPackedWorkerBundlerSource(workerBundlerDistPath);
336 404
337 try {405 try {
338 for (const target of targets) {406 for (const target of targets) {
@@ -375,12 +443,35 @@ const buildBinaries = async (targets: Target[], mode: BuildMode) => { · 36 unmodified lines
375 await cp(libsqlNative, join(binDir, "libsql.node"));443 await cp(libsqlNative, join(binDir, "libsql.node"));
376 }444 }
377 445
446 const workerdBinary = resolveWorkerdBinary(target);
447 if (workerdBinary && existsSync(workerdBinary)) {
448 await cp(workerdBinary, join(binDir, workerdBinaryName(target)));
449 if (target.os !== "win32") await chmod(join(binDir, workerdBinaryName(target)), 0o755);
450 }
451
452 await cp(workerBundlerDistPath, join(binDir, WORKER_BUNDLER_DIRNAME, "dist"), {
453 recursive: true,
454 });
455 await writeFile(
456 join(binDir, WORKER_BUNDLER_DIRNAME, "dist", "index.bundled.js"),
457 packedWorkerBundlerSource,
458 );
459
378 // Smoke test on current platform460 // Smoke test on current platform
379 if (isCurrentPlatform(target)) {461 if (isCurrentPlatform(target)) {
380 const bin = join(binDir, binaryName(target));462 const bin = join(binDir, binaryName(target));
381 console.log(` Smoke test: ${bin} --version`);463 console.log(` Smoke test: ${bin} --version`);
382 const version = await $`${bin} --version`.text();464 const version = await $`${bin} --version`.text();
383 console.log(` OK: ${version.trim()}`);465 console.log(` OK: ${version.trim()}`);
466 console.log(` Smoke test: packed apps source sync and invoke`);
467 const smoke = Bun.spawn(
468 ["bun", "run", join(cliRoot, "scripts/smoke-packed-apps.ts"), bin],
469 {
470 cwd: repoRoot,
471 stdio: ["ignore", "inherit", "inherit"],
472 },
473 );
474 if ((await smoke.exited) !== 0) throw new Error("packed apps smoke failed");
384 }475 }
385 476
386 // Variant package.json. All variants publish to the SAME npm package477 // Variant package.json. All variants publish to the SAME npm package
apps/cli/src/native-bindings.ts+19−5
@@ -1,11 +1,10 @@
1// ---------------------------------------------------------------------------1// ---------------------------------------------------------------------------
2// Native-binding bootstrap for the `bun build --compile` binary.2// Native-binding bootstrap for the `bun build --compile` binary.
3//3//
4// `bun --compile` bundles JS into bunfs but does NOT include `.node` native4// `bun --compile` bundles JS into bunfs but does NOT include native binaries,
5// addons, so a dynamic `require('@libsql/<platform>')` / keyring walk inside5// wasm sidecars, or runtime-read package files. build.ts copies those artifacts
6// the binary fails. build.ts copies each platform's `.node` next to the6// next to the executable; here we publish their on-disk paths via env vars the
7// executable (`libsql.node`, `keyring.node`); here we publish their on-disk7// loaders read.
8// paths via env vars the loaders read.
9//8//
10// This MUST be the FIRST import in main.ts. ES modules evaluate every import9// This MUST be the FIRST import in main.ts. ES modules evaluate every import
11// before the importer's own body, and libSQL resolves its native addon EAGERLY10// before the importer's own body, and libSQL resolves its native addon EAGERLY
@@ -43,3 +42,18 @@ if ( · 31 unmodified lines
43) {42) {
44 process.env.EXECUTOR_KEYRING_NATIVE_PATH = keyringNodeOnDisk;43 process.env.EXECUTOR_KEYRING_NATIVE_PATH = keyringNodeOnDisk;
45}44}
45
46const workerdOnDisk = join(execDir, process.platform === "win32" ? "workerd.exe" : "workerd");
47if (typeof Bun !== "undefined" && !process.env.EXECUTOR_WORKERD_BIN && existsSync(workerdOnDisk)) {
48 process.env.EXECUTOR_WORKERD_BIN = workerdOnDisk;
49}
50
51const workerBundlerOnDisk = join(execDir, "worker-bundler");
52if (
53 typeof Bun !== "undefined" &&
54 !process.env.EXECUTOR_WORKER_BUNDLER_DIR &&
55 existsSync(join(workerBundlerOnDisk, "dist", "index.js")) &&
56 existsSync(join(workerBundlerOnDisk, "dist", "esbuild.wasm"))
57) {
58 process.env.EXECUTOR_WORKER_BUNDLER_DIR = workerBundlerOnDisk;
59}

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