mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-17 00:00:48 +00:00
fix: restore ci gates on main
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../config/config.js";
|
||||
import {
|
||||
@@ -15,20 +16,32 @@ import {
|
||||
} from "./test-helpers/temp-plugin-extension-fixtures.js";
|
||||
|
||||
const originalBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
||||
const originalDisableBundledPlugins = process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS;
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
function createTempDir(): string {
|
||||
return createTempPluginDir(tempDirs, "openclaw-codex-ext-");
|
||||
}
|
||||
|
||||
function createBundledTempDir(): string {
|
||||
delete process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS;
|
||||
return createTempPluginDir(tempDirs, "openclaw-codex-ext-", {
|
||||
parentDir: path.join(process.cwd(), "dist-runtime", "extensions"),
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
clearRuntimeConfigSnapshot();
|
||||
cleanupTempPluginTestEnvironment(tempDirs, originalBundledPluginsDir);
|
||||
cleanupTempPluginTestEnvironment(
|
||||
tempDirs,
|
||||
originalBundledPluginsDir,
|
||||
originalDisableBundledPlugins,
|
||||
);
|
||||
});
|
||||
|
||||
describe("agent tool result middleware", () => {
|
||||
it("includes plugin-registered middleware and restores it from cache", async () => {
|
||||
const tmp = createTempDir();
|
||||
const tmp = createBundledTempDir();
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tmp;
|
||||
|
||||
writeTempPlugin({
|
||||
@@ -81,7 +94,7 @@ describe("agent tool result middleware", () => {
|
||||
});
|
||||
|
||||
it("rejects middleware when the manifest omits the runtime contract", () => {
|
||||
const tmp = createTempDir();
|
||||
const tmp = createBundledTempDir();
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tmp;
|
||||
|
||||
writeTempPlugin({
|
||||
@@ -158,7 +171,7 @@ describe("agent tool result middleware", () => {
|
||||
});
|
||||
|
||||
it("merges runtimes when a plugin registers the same middleware function twice", () => {
|
||||
const tmp = createTempDir();
|
||||
const tmp = createBundledTempDir();
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tmp;
|
||||
|
||||
writeTempPlugin({
|
||||
@@ -194,7 +207,7 @@ export default { id: "tool-result-middleware", register(api) {
|
||||
});
|
||||
|
||||
it("lazily loads bundled middleware owners from manifest contracts", async () => {
|
||||
const tmp = createTempDir();
|
||||
const tmp = createBundledTempDir();
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tmp;
|
||||
|
||||
writeTempPlugin({
|
||||
@@ -246,7 +259,7 @@ export default { id: "tool-result-middleware", register(api) {
|
||||
|
||||
describe("Codex app-server extension factories", () => {
|
||||
it("includes plugin-registered Codex app-server extension factories and restores them from cache", async () => {
|
||||
const tmp = createTempDir();
|
||||
const tmp = createBundledTempDir();
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tmp;
|
||||
|
||||
writeTempPlugin({
|
||||
@@ -337,7 +350,7 @@ describe("Codex app-server extension factories", () => {
|
||||
});
|
||||
|
||||
it("rejects bundled plugins that omit the Codex app-server extension contract", () => {
|
||||
const tmp = createTempDir();
|
||||
const tmp = createBundledTempDir();
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tmp;
|
||||
|
||||
writeTempPlugin({
|
||||
@@ -373,7 +386,7 @@ describe("Codex app-server extension factories", () => {
|
||||
});
|
||||
|
||||
it("rejects non-function Codex app-server extension factories from bundled plugins", () => {
|
||||
const tmp = createTempDir();
|
||||
const tmp = createBundledTempDir();
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tmp;
|
||||
|
||||
writeTempPlugin({
|
||||
|
||||
@@ -7,8 +7,14 @@ import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
|
||||
const EMPTY_PLUGIN_SCHEMA = { type: "object", additionalProperties: false, properties: {} };
|
||||
|
||||
export function createTempPluginDir(tempDirs: string[], prefix: string): string {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
export function createTempPluginDir(
|
||||
tempDirs: string[],
|
||||
prefix: string,
|
||||
options?: { parentDir?: string },
|
||||
): string {
|
||||
const parentDir = options?.parentDir ?? os.tmpdir();
|
||||
fs.mkdirSync(parentDir, { recursive: true });
|
||||
const dir = fs.mkdtempSync(path.join(parentDir, prefix));
|
||||
tempDirs.push(dir);
|
||||
return dir;
|
||||
}
|
||||
@@ -43,6 +49,7 @@ export function writeTempPlugin(params: {
|
||||
export function cleanupTempPluginTestEnvironment(
|
||||
tempDirs: string[],
|
||||
originalBundledPluginsDir: string | undefined,
|
||||
originalDisableBundledPlugins?: string,
|
||||
) {
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
@@ -54,6 +61,11 @@ export function cleanupTempPluginTestEnvironment(
|
||||
} else {
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = originalBundledPluginsDir;
|
||||
}
|
||||
if (originalDisableBundledPlugins === undefined) {
|
||||
delete process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS;
|
||||
} else {
|
||||
process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS = originalDisableBundledPlugins;
|
||||
}
|
||||
}
|
||||
|
||||
export function resetActivePluginRegistryForTest() {
|
||||
|
||||
@@ -15,6 +15,20 @@ function makeTempDir() {
|
||||
return makeTrackedTempDir("openclaw-doctor-plugin-manifests", tempDirs);
|
||||
}
|
||||
|
||||
function makePluginWorkspace() {
|
||||
const workspaceDir = makeTempDir();
|
||||
return {
|
||||
workspaceDir,
|
||||
pluginsRoot: path.join(workspaceDir, ".openclaw", "extensions"),
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1",
|
||||
OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE: "1",
|
||||
OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE: "1",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function writeManifest(dir: string, manifest: Record<string, unknown>) {
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "openclaw.plugin.json"),
|
||||
@@ -76,7 +90,7 @@ describe("doctor plugin manifest legacy contract repair", () => {
|
||||
});
|
||||
|
||||
it("collects legacy top-level capability keys for migration", () => {
|
||||
const pluginsRoot = makeTempDir();
|
||||
const { pluginsRoot } = makePluginWorkspace();
|
||||
const root = path.join(pluginsRoot, "openai");
|
||||
fs.mkdirSync(root, { recursive: true });
|
||||
writePackageJson(root);
|
||||
@@ -101,7 +115,7 @@ describe("doctor plugin manifest legacy contract repair", () => {
|
||||
});
|
||||
|
||||
it("rewrites legacy top-level capability keys into contracts", async () => {
|
||||
const pluginsRoot = makeTempDir();
|
||||
const { pluginsRoot } = makePluginWorkspace();
|
||||
const root = path.join(pluginsRoot, "openai");
|
||||
fs.mkdirSync(root, { recursive: true });
|
||||
writePackageJson(root);
|
||||
@@ -141,7 +155,7 @@ describe("doctor plugin manifest legacy contract repair", () => {
|
||||
});
|
||||
|
||||
it("ignores non-object contracts payloads when collecting migrations", () => {
|
||||
const pluginsRoot = makeTempDir();
|
||||
const { pluginsRoot } = makePluginWorkspace();
|
||||
const root = path.join(pluginsRoot, "openai");
|
||||
fs.mkdirSync(root, { recursive: true });
|
||||
writePackageJson(root);
|
||||
|
||||
@@ -83,6 +83,7 @@ function buildLegacyManifestContractMigration(params: {
|
||||
export function collectLegacyPluginManifestContractMigrations(params?: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
manifestRoots?: string[];
|
||||
workspaceDir?: string;
|
||||
}): LegacyManifestContractMigration[] {
|
||||
const seen = new Set<string>();
|
||||
const migrations: LegacyManifestContractMigration[] = [];
|
||||
@@ -114,6 +115,7 @@ export function collectLegacyPluginManifestContractMigrations(params?: {
|
||||
for (const plugin of loadPluginManifestRegistry({
|
||||
cache: false,
|
||||
...(params?.env ? { env: params.env } : {}),
|
||||
...(params?.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
|
||||
}).plugins) {
|
||||
if (seen.has(plugin.manifestPath)) {
|
||||
continue;
|
||||
@@ -138,6 +140,7 @@ export function collectLegacyPluginManifestContractMigrations(params?: {
|
||||
export async function maybeRepairLegacyPluginManifestContracts(params: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
manifestRoots?: string[];
|
||||
workspaceDir?: string;
|
||||
runtime: RuntimeEnv;
|
||||
prompter: DoctorPrompter;
|
||||
note?: typeof note;
|
||||
@@ -145,6 +148,7 @@ export async function maybeRepairLegacyPluginManifestContracts(params: {
|
||||
const migrations = collectLegacyPluginManifestContractMigrations({
|
||||
...(params.env ? { env: params.env } : {}),
|
||||
...(params.manifestRoots ? { manifestRoots: params.manifestRoots } : {}),
|
||||
...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
|
||||
});
|
||||
if (migrations.length === 0) {
|
||||
return;
|
||||
|
||||
@@ -35,6 +35,7 @@ const GATEWAY_TEST_ENV_KEYS = [
|
||||
"OPENCLAW_SKIP_BROWSER_CONTROL_SERVER",
|
||||
"OPENCLAW_SKIP_PROVIDERS",
|
||||
"OPENCLAW_BUNDLED_PLUGINS_DIR",
|
||||
"OPENCLAW_DISABLE_BUNDLED_PLUGINS",
|
||||
] as const;
|
||||
|
||||
function nextGatewayId(prefix: string): string {
|
||||
@@ -110,6 +111,7 @@ async function setupGatewayTempHome(params: { prefix: string; minimalGateway?: b
|
||||
const workspaceDir = path.join(tempHome, "openclaw");
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = await createEmptyBundledPluginsDir(tempHome);
|
||||
process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS = "1";
|
||||
return { envSnapshot, tempHome, workspaceDir };
|
||||
}
|
||||
|
||||
@@ -318,6 +320,7 @@ module.exports = {
|
||||
"OPENCLAW_SKIP_BROWSER_CONTROL_SERVER",
|
||||
"OPENCLAW_SKIP_PROVIDERS",
|
||||
"OPENCLAW_BUNDLED_PLUGINS_DIR",
|
||||
"OPENCLAW_DISABLE_BUNDLED_PLUGINS",
|
||||
"OPENCLAW_TEST_MINIMAL_GATEWAY",
|
||||
]);
|
||||
|
||||
@@ -333,6 +336,7 @@ module.exports = {
|
||||
const tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-wizard-home-"));
|
||||
process.env.HOME = tempHome;
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = await createEmptyBundledPluginsDir(tempHome);
|
||||
process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS = "1";
|
||||
delete process.env.OPENCLAW_STATE_DIR;
|
||||
delete process.env.OPENCLAW_CONFIG_PATH;
|
||||
|
||||
|
||||
@@ -815,11 +815,18 @@ describe("gateway server misc", () => {
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
await withEnvAsync({ OPENCLAW_TEST_MINIMAL_GATEWAY: undefined }, async () => {
|
||||
const autoPort = await getFreePort();
|
||||
const autoServer = await startGatewayServer(autoPort);
|
||||
await autoServer.close();
|
||||
});
|
||||
await withEnvAsync(
|
||||
{
|
||||
OPENCLAW_TEST_MINIMAL_GATEWAY: undefined,
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS: undefined,
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: path.resolve("extensions"),
|
||||
},
|
||||
async () => {
|
||||
const autoPort = await getFreePort();
|
||||
const autoServer = await startGatewayServer(autoPort);
|
||||
await autoServer.close();
|
||||
},
|
||||
);
|
||||
|
||||
const updated = JSON.parse(await fs.readFile(configPath, "utf-8")) as Record<string, unknown>;
|
||||
const channels = updated.channels as Record<string, unknown> | undefined;
|
||||
|
||||
@@ -70,6 +70,7 @@ const GATEWAY_TEST_ENV_KEYS = [
|
||||
"OPENCLAW_SKIP_GMAIL_WATCHER",
|
||||
"OPENCLAW_SKIP_CANVAS_HOST",
|
||||
"OPENCLAW_BUNDLED_PLUGINS_DIR",
|
||||
"OPENCLAW_DISABLE_BUNDLED_PLUGINS",
|
||||
"OPENCLAW_SKIP_CHANNELS",
|
||||
"OPENCLAW_SKIP_PROVIDERS",
|
||||
"OPENCLAW_SKIP_CRON",
|
||||
@@ -235,6 +236,7 @@ function applyGatewaySkipEnv() {
|
||||
process.env.OPENCLAW_SKIP_PROVIDERS = "1";
|
||||
process.env.OPENCLAW_SKIP_CRON = "1";
|
||||
process.env.OPENCLAW_TEST_MINIMAL_GATEWAY = "1";
|
||||
process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS = "1";
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tempHome
|
||||
? path.join(tempHome, "openclaw-test-no-bundled-extensions")
|
||||
: "openclaw-test-no-bundled-extensions";
|
||||
|
||||
@@ -203,6 +203,23 @@ describe("resolveBundledRuntimeDepsNpmRunner", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the Node-adjacent POSIX npm shim when npm-cli.js is unavailable", () => {
|
||||
const execPath = "/opt/node/bin/node";
|
||||
const npmPath = "/opt/node/bin/npm";
|
||||
const runner = resolveBundledRuntimeDepsNpmRunner({
|
||||
env: {},
|
||||
execPath,
|
||||
existsSync: (candidate) => candidate === npmPath,
|
||||
npmArgs: ["install", "acpx@0.5.3"],
|
||||
platform: "linux",
|
||||
});
|
||||
|
||||
expect(runner).toEqual({
|
||||
command: npmPath,
|
||||
args: ["install", "acpx@0.5.3"],
|
||||
});
|
||||
});
|
||||
|
||||
it("refuses Windows shell fallback when no safe npm executable is available", () => {
|
||||
expect(() =>
|
||||
resolveBundledRuntimeDepsNpmRunner({
|
||||
@@ -222,7 +239,7 @@ describe("resolveBundledRuntimeDepsNpmRunner", () => {
|
||||
PATH: "/repo/evil/bin:/usr/bin:/bin",
|
||||
},
|
||||
execPath: "/opt/node/bin/node",
|
||||
existsSync: (candidate) => candidate === "/opt/node/bin/npm",
|
||||
existsSync: (candidate) => candidate === "/usr/bin/npm",
|
||||
npmArgs: ["install"],
|
||||
platform: "linux",
|
||||
}),
|
||||
|
||||
@@ -1325,6 +1325,14 @@ export function resolveBundledRuntimeDepsNpmRunner(params: {
|
||||
throw new Error("Unable to resolve a safe npm executable on Windows");
|
||||
}
|
||||
|
||||
const npmExePath = pathImpl.resolve(nodeDir, "npm");
|
||||
if (existsSync(npmExePath)) {
|
||||
return {
|
||||
command: npmExePath,
|
||||
args: params.npmArgs,
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error("Unable to resolve a safe npm executable");
|
||||
}
|
||||
type BundledPluginRuntimeDepsManifest = {
|
||||
|
||||
@@ -45,6 +45,7 @@ function collectBundledChannelOwnerPluginIds(params: {
|
||||
config: OpenClawConfig;
|
||||
channelIds: readonly string[];
|
||||
env: NodeJS.ProcessEnv;
|
||||
bundledPluginsDir?: string;
|
||||
}): string[] {
|
||||
const plugins = normalizePluginsConfig(params.config.plugins);
|
||||
const channelIds = new Set(
|
||||
@@ -55,7 +56,7 @@ function collectBundledChannelOwnerPluginIds(params: {
|
||||
if (channelIds.size === 0) {
|
||||
return [];
|
||||
}
|
||||
const bundledDir = resolveBundledPluginsDir(params.env);
|
||||
const bundledDir = params.bundledPluginsDir ?? resolveBundledPluginsDir(params.env);
|
||||
if (!bundledDir) {
|
||||
return [];
|
||||
}
|
||||
@@ -126,6 +127,7 @@ export function resolveEffectivePluginIds(params: {
|
||||
config: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
workspaceDir?: string;
|
||||
bundledPluginsDir?: string;
|
||||
}): string[] {
|
||||
const autoEnabled = applyPluginAutoEnable({
|
||||
config: params.config,
|
||||
@@ -150,6 +152,7 @@ export function resolveEffectivePluginIds(params: {
|
||||
config: effectiveConfig,
|
||||
channelIds: configuredChannelIds,
|
||||
env: params.env,
|
||||
...(params.bundledPluginsDir ? { bundledPluginsDir: params.bundledPluginsDir } : {}),
|
||||
})) {
|
||||
ids.add(pluginId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user