mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 19:20:43 +00:00
fix(plugins): harden registry migration guards
This commit is contained in:
@@ -32,6 +32,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const DEFAULT_EXTENSIONS_DIR = join(__dirname, "..", "dist", "extensions");
|
||||
const DEFAULT_PACKAGE_ROOT = join(__dirname, "..");
|
||||
const DISABLE_POSTINSTALL_ENV = "OPENCLAW_DISABLE_BUNDLED_PLUGIN_POSTINSTALL";
|
||||
const DISABLE_PLUGIN_REGISTRY_MIGRATION_ENV = "OPENCLAW_DISABLE_PLUGIN_REGISTRY_MIGRATION";
|
||||
const EAGER_BUNDLED_PLUGIN_DEPS_ENV = "OPENCLAW_EAGER_BUNDLED_PLUGIN_DEPS";
|
||||
const DIST_INVENTORY_PATH = "dist/postinstall-inventory.json";
|
||||
const LEGACY_QA_CHANNEL_DIR = ["qa", "channel"].join("-");
|
||||
@@ -663,6 +664,11 @@ async function importInstalledDistModule(params, distPath) {
|
||||
export async function runPluginRegistryPostinstallMigration(params = {}) {
|
||||
const log = params.log ?? console;
|
||||
const packageRoot = params.packageRoot ?? DEFAULT_PACKAGE_ROOT;
|
||||
const env = params.env ?? process.env;
|
||||
|
||||
if (env[DISABLE_PLUGIN_REGISTRY_MIGRATION_ENV]?.trim()) {
|
||||
return { status: "disabled", migrated: false, reason: "disabled-env" };
|
||||
}
|
||||
|
||||
try {
|
||||
const migrationModule = await importInstalledDistModule(
|
||||
@@ -677,7 +683,7 @@ export async function runPluginRegistryPostinstallMigration(params = {}) {
|
||||
}
|
||||
|
||||
const result = await migrationModule.migratePluginRegistryForInstall({
|
||||
env: params.env ?? process.env,
|
||||
env,
|
||||
packageRoot,
|
||||
});
|
||||
for (const warning of result.preflight?.deprecationWarnings ?? []) {
|
||||
|
||||
@@ -281,6 +281,43 @@ describe("installed plugin index", () => {
|
||||
).toEqual(["demo"]);
|
||||
});
|
||||
|
||||
it("uses runtime plugin id normalization for legacy enablement aliases", () => {
|
||||
const rootDir = makeTempDir();
|
||||
writeRuntimeEntry(rootDir);
|
||||
writePluginManifest(rootDir, {
|
||||
id: "openai",
|
||||
configSchema: { type: "object" },
|
||||
providers: ["openai"],
|
||||
});
|
||||
|
||||
const config = {
|
||||
plugins: {
|
||||
entries: {
|
||||
"openai-codex": {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const index = loadInstalledPluginIndex({
|
||||
candidates: [
|
||||
createPluginCandidate({
|
||||
rootDir,
|
||||
idHint: "openai",
|
||||
origin: "bundled",
|
||||
}),
|
||||
],
|
||||
config,
|
||||
env: hermeticEnv(),
|
||||
});
|
||||
|
||||
expect(index.plugins[0]).toMatchObject({
|
||||
pluginId: "openai",
|
||||
enabled: false,
|
||||
});
|
||||
expect(listEnabledInstalledPluginRecords(index, config)).toEqual([]);
|
||||
});
|
||||
|
||||
it("records the config install ledger separately from package install intent", () => {
|
||||
const fixture = createRichPluginFixture();
|
||||
|
||||
|
||||
@@ -5,10 +5,7 @@ import type { OpenClawConfig } from "../config/types.js";
|
||||
import type { PluginInstallRecord } from "../config/types.plugins.js";
|
||||
import { resolveCompatibilityHostVersion } from "../version.js";
|
||||
import { listPluginCompatRecords, type PluginCompatCode } from "./compat/registry.js";
|
||||
import {
|
||||
normalizePluginsConfigWithResolver,
|
||||
resolveEffectiveEnableState,
|
||||
} from "./config-policy.js";
|
||||
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
|
||||
import { discoverOpenClawPlugins, type PluginCandidate } from "./discovery.js";
|
||||
import {
|
||||
describePluginInstallSource,
|
||||
@@ -353,7 +350,7 @@ function resolveCompatRegistryVersion(): string {
|
||||
}
|
||||
|
||||
function resolvePolicyHash(config: OpenClawConfig | undefined): string {
|
||||
const normalized = normalizePluginsConfigWithResolver(config?.plugins);
|
||||
const normalized = normalizePluginsConfig(config?.plugins);
|
||||
const channelPolicy: Record<string, boolean> = {};
|
||||
const channels = config?.channels;
|
||||
if (channels && typeof channels === "object" && !Array.isArray(channels)) {
|
||||
@@ -404,7 +401,7 @@ function resolveRegistry(params: LoadInstalledPluginIndexParams): {
|
||||
};
|
||||
}
|
||||
|
||||
const normalized = normalizePluginsConfigWithResolver(params.config?.plugins);
|
||||
const normalized = normalizePluginsConfig(params.config?.plugins);
|
||||
const discovery = discoverOpenClawPlugins({
|
||||
workspaceDir: params.workspaceDir,
|
||||
extraPaths: normalized.loadPaths,
|
||||
@@ -430,7 +427,7 @@ function buildInstalledPluginIndex(
|
||||
const env = params.env ?? process.env;
|
||||
const { candidates, registry } = resolveRegistry(params);
|
||||
const candidateByRootDir = buildCandidateLookup(candidates);
|
||||
const normalizedConfig = normalizePluginsConfigWithResolver(params.config?.plugins);
|
||||
const normalizedConfig = normalizePluginsConfig(params.config?.plugins);
|
||||
const diagnostics: PluginDiagnostic[] = [...registry.diagnostics];
|
||||
const generatedAtMs = (params.now?.() ?? new Date()).getTime();
|
||||
const plugins = registry.plugins.map((record): InstalledPluginIndexRecord => {
|
||||
@@ -528,7 +525,7 @@ export function listEnabledInstalledPluginRecords(
|
||||
if (!config) {
|
||||
return index.plugins.filter((plugin) => plugin.enabled);
|
||||
}
|
||||
const normalizedConfig = normalizePluginsConfigWithResolver(config?.plugins);
|
||||
const normalizedConfig = normalizePluginsConfig(config?.plugins);
|
||||
return index.plugins.filter(
|
||||
(plugin) =>
|
||||
resolveEffectiveEnableState({
|
||||
@@ -560,7 +557,7 @@ export function isInstalledPluginEnabled(
|
||||
if (!config) {
|
||||
return record.enabled;
|
||||
}
|
||||
const normalizedConfig = normalizePluginsConfigWithResolver(config?.plugins);
|
||||
const normalizedConfig = normalizePluginsConfig(config?.plugins);
|
||||
return resolveEffectiveEnableState({
|
||||
id: record.pluginId,
|
||||
origin: record.origin,
|
||||
|
||||
@@ -330,22 +330,23 @@ describe("bundled plugin postinstall", () => {
|
||||
});
|
||||
|
||||
it("honors plugin registry postinstall migration disable env", async () => {
|
||||
const migratePluginRegistryForInstall = vi.fn(async () => ({
|
||||
status: "disabled",
|
||||
migrated: false,
|
||||
preflight: {
|
||||
deprecationWarnings: [],
|
||||
},
|
||||
}));
|
||||
const importModule = vi.fn(async () => {
|
||||
throw new Error("dist migration module should not import when migration is disabled");
|
||||
});
|
||||
await expect(
|
||||
runPluginRegistryPostinstallMigration({
|
||||
packageRoot: "/pkg",
|
||||
env: { OPENCLAW_DISABLE_PLUGIN_REGISTRY_MIGRATION: "1" },
|
||||
existsSync: vi.fn(() => true),
|
||||
importModule: vi.fn(async () => ({ migratePluginRegistryForInstall })),
|
||||
importModule,
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
}),
|
||||
).resolves.toMatchObject({ status: "disabled" });
|
||||
).resolves.toMatchObject({
|
||||
status: "disabled",
|
||||
migrated: false,
|
||||
reason: "disabled-env",
|
||||
});
|
||||
expect(importModule).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("prunes stale dist files from packaged installs", async () => {
|
||||
|
||||
Reference in New Issue
Block a user