mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-21 01:51:39 -06:00
3801331d22
* fix(gateway): admit recovering workers during startup * fix(gateway): admit recovering nodes during startup * fix(crabbox): bind worker desktop to XFCE session * fix(workers): reuse Git base during workspace transfer large clean/stale worktrees were downloading every tracked file after the verified base pack, crossing transfer authority; selectively checkout desired base-index paths, preserving deletions and symlink confinement. * fix(workers): clone reachable stale workspace commits tip-only origin detection forced published ancestor commits through heavyweight Gateway transfer; the existing exact checkout and manifest verification safely own reachability/fallback. * perf(workers): use blobless origin clones * fix(workers): bundle undici in worker deploy artifact
111 lines
5.0 KiB
TypeScript
111 lines
5.0 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import {
|
|
createWorkerDeployBuildPlugin,
|
|
WORKER_DEPLOY_OPTIONAL_NATIVE_MODULE_ID,
|
|
} from "../../scripts/lib/worker-deploy-build-plugin.mts";
|
|
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
|
|
|
|
const fail = (message: string): never => {
|
|
throw new Error(message);
|
|
};
|
|
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
|
|
|
describe("worker deploy build plugin", () => {
|
|
it("replaces optional host-native modules with a failing virtual module", () => {
|
|
const plugin = createWorkerDeployBuildPlugin();
|
|
|
|
expect(plugin.load(WORKER_DEPLOY_OPTIONAL_NATIVE_MODULE_ID)).toContain(
|
|
"optional host-native dependency unavailable",
|
|
);
|
|
});
|
|
|
|
it("composes the bundled Browser runtime only in the deploy build", () => {
|
|
const bridgePath = path.resolve("src/worker/worker-deploy-browser-runtime.ts");
|
|
const source = fs.readFileSync(bridgePath, "utf8");
|
|
const plugin = createWorkerDeployBuildPlugin();
|
|
|
|
const transformed = plugin.transform.call({ error: fail }, source, bridgePath);
|
|
|
|
expect(transformed).toContain(
|
|
'import { createAttachedBrowserToolRuntime } from "../../extensions/browser/runtime-api.js";',
|
|
);
|
|
expect(transformed).toContain("export default { createAttachedBrowserToolRuntime };");
|
|
expect(transformed).not.toContain("was not composed by the build");
|
|
});
|
|
|
|
it("bundles the undici dispatcher dependency without a worker runtime require", () => {
|
|
const dispatcherPath = path.resolve("src/infra/net/undici-dispatcher-options.ts");
|
|
const source = fs.readFileSync(dispatcherPath, "utf8");
|
|
const plugin = createWorkerDeployBuildPlugin();
|
|
|
|
const transformed = plugin.transform.call({ error: fail }, source, dispatcherPath);
|
|
|
|
expect(transformed).toContain('import * as bundledUndici from "undici";');
|
|
expect(transformed).toContain("return bundledUndici;");
|
|
expect(transformed).toContain('return override as typeof import("undici");');
|
|
expect(transformed).not.toContain('import { createRequire } from "node:module";');
|
|
expect(transformed).not.toContain("const requireUndici = createRequire(import.meta.url);");
|
|
expect(transformed).not.toContain('requireUndici("undici")');
|
|
expect(plugin.transform.call({ error: fail }, transformed!, dispatcherPath)).toBe(transformed);
|
|
});
|
|
|
|
it("fails closed when the undici dispatcher bootstrap shape changes", () => {
|
|
const dispatcherPath = path.resolve("src/infra/net/undici-dispatcher-options.ts");
|
|
const source = fs.readFileSync(dispatcherPath, "utf8");
|
|
const plugin = createWorkerDeployBuildPlugin();
|
|
|
|
expect(() =>
|
|
plugin.transform.call(
|
|
{ error: fail },
|
|
source.replace('return requireUndici("undici")', 'return changedUndici("undici")'),
|
|
dispatcherPath,
|
|
),
|
|
).toThrow("undici dispatcher bootstrap changed");
|
|
});
|
|
|
|
it("inlines Playwright package identity without a runtime manifest read", () => {
|
|
const coreBundlePath = path.resolve("node_modules/playwright-core/lib/coreBundle.js");
|
|
const source = fs.readFileSync(coreBundlePath, "utf8");
|
|
const plugin = createWorkerDeployBuildPlugin();
|
|
|
|
const transformed = plugin.transform.call({ error: fail }, source, coreBundlePath);
|
|
|
|
expect(transformed).toContain('packageJSON = {"name":"playwright-core","version":"1.62.1"};');
|
|
expect(transformed).not.toContain(
|
|
'packageJSON = require(import_path9.default.join(packageRoot, "package.json"));',
|
|
);
|
|
expect(transformed).toContain(
|
|
'registry = new Registry({"comment":"Do not edit this file, use utils/roll_browser.js"',
|
|
);
|
|
expect(transformed).not.toContain(
|
|
'registry = new Registry(require(import_path20.default.join(packageRoot, "browsers.json")));',
|
|
);
|
|
});
|
|
|
|
it("matches the canonical dependency path behind a pnpm-style symlink", () => {
|
|
const sourceRoot = path.resolve("node_modules/playwright-core");
|
|
const source = fs.readFileSync(path.join(sourceRoot, "lib/coreBundle.js"), "utf8");
|
|
const tempRoot = tempDirs.make("openclaw-worker-build-plugin-");
|
|
const linkedRoot = path.join(tempRoot, "node_modules", "playwright-core");
|
|
fs.mkdirSync(path.dirname(linkedRoot), { recursive: true });
|
|
fs.symlinkSync(sourceRoot, linkedRoot, process.platform === "win32" ? "junction" : "dir");
|
|
const plugin = createWorkerDeployBuildPlugin(tempRoot);
|
|
const resolvedId = fs.realpathSync(path.join(linkedRoot, "lib/coreBundle.js"));
|
|
|
|
const transformed = plugin.transform.call({ error: fail }, source, resolvedId);
|
|
|
|
expect(transformed).toContain('packageJSON = {"name":"playwright-core","version":"1.62.1"};');
|
|
});
|
|
|
|
it("fails closed when the dependency-owned bootstrap shape changes", () => {
|
|
const coreBundlePath = path.resolve("node_modules/playwright-core/lib/coreBundle.js");
|
|
const plugin = createWorkerDeployBuildPlugin();
|
|
|
|
expect(() =>
|
|
plugin.transform.call({ error: fail }, "changed upstream source", coreBundlePath),
|
|
).toThrow("playwright-core package bootstrap changed");
|
|
});
|
|
});
|