mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
perf(secrets): scope compat migration scans
This commit is contained in:
@@ -34,6 +34,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityMigrationPaths": ["models.bedrockDiscovery"]
|
||||
},
|
||||
"uiHints": {
|
||||
"discovery": {
|
||||
"label": "Model Discovery",
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
"contracts": {
|
||||
"speechProviders": ["elevenlabs"]
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityMigrationPaths": [
|
||||
"talk.voiceId",
|
||||
"talk.voiceAliases",
|
||||
"talk.modelId",
|
||||
"talk.outputFormat",
|
||||
"talk.apiKey"
|
||||
]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -170,5 +170,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityMigrationPaths": ["plugins.entries.memory-wiki.config.bridge.readMemoryCore"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,5 +711,8 @@
|
||||
"minimum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityMigrationPaths": ["plugins.entries.voice-call.config"]
|
||||
}
|
||||
}
|
||||
|
||||
73
src/commands/doctor/shared/channel-doctor.test.ts
Normal file
73
src/commands/doctor/shared/channel-doctor.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
listChannelPlugins: vi.fn(),
|
||||
listBundledChannelPlugins: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../../channels/plugins/registry.js", () => ({
|
||||
listChannelPlugins: (...args: Parameters<typeof mocks.listChannelPlugins>) =>
|
||||
mocks.listChannelPlugins(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../../../channels/plugins/bundled.js", () => ({
|
||||
listBundledChannelPlugins: (...args: Parameters<typeof mocks.listBundledChannelPlugins>) =>
|
||||
mocks.listBundledChannelPlugins(...args),
|
||||
}));
|
||||
|
||||
let collectChannelDoctorCompatibilityMutations: typeof import("./channel-doctor.js").collectChannelDoctorCompatibilityMutations;
|
||||
|
||||
describe("channel doctor compatibility mutations", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ collectChannelDoctorCompatibilityMutations } = await import("./channel-doctor.js"));
|
||||
mocks.listChannelPlugins.mockReset();
|
||||
mocks.listBundledChannelPlugins.mockReset();
|
||||
mocks.listChannelPlugins.mockReturnValue([]);
|
||||
mocks.listBundledChannelPlugins.mockReturnValue([]);
|
||||
});
|
||||
|
||||
it("skips plugin discovery when no channels are configured", () => {
|
||||
const result = collectChannelDoctorCompatibilityMutations({} as never);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(mocks.listChannelPlugins).not.toHaveBeenCalled();
|
||||
expect(mocks.listBundledChannelPlugins).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("only evaluates configured channel ids", () => {
|
||||
const normalizeCompatibilityConfig = vi.fn(({ cfg }: { cfg: unknown }) => ({
|
||||
config: cfg,
|
||||
changes: ["matrix"],
|
||||
}));
|
||||
mocks.listBundledChannelPlugins.mockReturnValue([
|
||||
{
|
||||
id: "matrix",
|
||||
doctor: { normalizeCompatibilityConfig },
|
||||
},
|
||||
{
|
||||
id: "discord",
|
||||
doctor: {
|
||||
normalizeCompatibilityConfig: vi.fn(() => ({
|
||||
config: {},
|
||||
changes: ["discord"],
|
||||
})),
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
matrix: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = collectChannelDoctorCompatibilityMutations(cfg as never);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(normalizeCompatibilityConfig).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.listBundledChannelPlugins).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,19 @@ type ChannelDoctorEntry = {
|
||||
doctor: ChannelDoctorAdapter;
|
||||
};
|
||||
|
||||
function collectConfiguredChannelIds(cfg: OpenClawConfig): string[] {
|
||||
const channels =
|
||||
cfg.channels && typeof cfg.channels === "object" && !Array.isArray(cfg.channels)
|
||||
? cfg.channels
|
||||
: null;
|
||||
if (!channels) {
|
||||
return [];
|
||||
}
|
||||
return Object.keys(channels)
|
||||
.filter((channelId) => channelId !== "defaults")
|
||||
.toSorted();
|
||||
}
|
||||
|
||||
function safeListActiveChannelPlugins() {
|
||||
try {
|
||||
return listChannelPlugins();
|
||||
@@ -29,9 +42,13 @@ function safeListBundledChannelPlugins() {
|
||||
}
|
||||
}
|
||||
|
||||
function listChannelDoctorEntries(): ChannelDoctorEntry[] {
|
||||
function listChannelDoctorEntries(channelIds?: readonly string[]): ChannelDoctorEntry[] {
|
||||
const byId = new Map<string, ChannelDoctorEntry>();
|
||||
const selectedIds = channelIds ? new Set(channelIds) : null;
|
||||
for (const plugin of [...safeListActiveChannelPlugins(), ...safeListBundledChannelPlugins()]) {
|
||||
if (selectedIds && !selectedIds.has(plugin.id)) {
|
||||
continue;
|
||||
}
|
||||
if (!plugin.doctor) {
|
||||
continue;
|
||||
}
|
||||
@@ -64,9 +81,13 @@ export async function runChannelDoctorConfigSequences(params: {
|
||||
export function collectChannelDoctorCompatibilityMutations(
|
||||
cfg: OpenClawConfig,
|
||||
): ChannelDoctorConfigMutation[] {
|
||||
const channelIds = collectConfiguredChannelIds(cfg);
|
||||
if (channelIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const mutations: ChannelDoctorConfigMutation[] = [];
|
||||
let nextCfg = cfg;
|
||||
for (const entry of listChannelDoctorEntries()) {
|
||||
for (const entry of listChannelDoctorEntries(channelIds)) {
|
||||
const mutation = entry.doctor.normalizeCompatibilityConfig?.({ cfg: nextCfg });
|
||||
if (!mutation || mutation.changes.length === 0) {
|
||||
continue;
|
||||
|
||||
57
src/config/defaults.test.ts
Normal file
57
src/config/defaults.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
applyProviderConfigDefaultsWithPlugin: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/provider-runtime.js", () => ({
|
||||
applyProviderConfigDefaultsWithPlugin: (
|
||||
...args: Parameters<typeof mocks.applyProviderConfigDefaultsWithPlugin>
|
||||
) => mocks.applyProviderConfigDefaultsWithPlugin(...args),
|
||||
}));
|
||||
|
||||
let applyContextPruningDefaults: typeof import("./defaults.js").applyContextPruningDefaults;
|
||||
|
||||
describe("config defaults", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ applyContextPruningDefaults } = await import("./defaults.js"));
|
||||
mocks.applyProviderConfigDefaultsWithPlugin.mockReset();
|
||||
});
|
||||
|
||||
it("skips provider defaults when agent defaults are absent", () => {
|
||||
const cfg = {
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
api: "openai-completions",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(applyContextPruningDefaults(cfg as never)).toBe(cfg);
|
||||
expect(mocks.applyProviderConfigDefaultsWithPlugin).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses anthropic provider defaults when agent defaults exist", () => {
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: {},
|
||||
},
|
||||
};
|
||||
const nextCfg = {
|
||||
agents: {
|
||||
defaults: {
|
||||
contextPruning: {
|
||||
mode: "cache-ttl",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
mocks.applyProviderConfigDefaultsWithPlugin.mockReturnValue(nextCfg);
|
||||
|
||||
expect(applyContextPruningDefaults(cfg as never)).toBe(nextCfg);
|
||||
expect(mocks.applyProviderConfigDefaultsWithPlugin).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -335,6 +335,9 @@ export function applyLoggingDefaults(cfg: OpenClawConfig): OpenClawConfig {
|
||||
}
|
||||
|
||||
export function applyContextPruningDefaults(cfg: OpenClawConfig): OpenClawConfig {
|
||||
if (!cfg.agents?.defaults) {
|
||||
return cfg;
|
||||
}
|
||||
return (
|
||||
applyProviderConfigDefaultsWithPlugin({
|
||||
provider: "anthropic",
|
||||
|
||||
@@ -529,6 +529,7 @@ describe("loadPluginManifestRegistry", () => {
|
||||
id: "acpx",
|
||||
configSchema: { type: "object" },
|
||||
configContracts: {
|
||||
compatibilityMigrationPaths: ["models.bedrockDiscovery"],
|
||||
dangerousFlags: [{ path: "permissionMode", equals: "approve-all" }],
|
||||
secretInputs: {
|
||||
bundledDefaultEnabled: false,
|
||||
@@ -544,6 +545,7 @@ describe("loadPluginManifestRegistry", () => {
|
||||
});
|
||||
|
||||
expect(registry.plugins[0]?.configContracts).toEqual({
|
||||
compatibilityMigrationPaths: ["models.bedrockDiscovery"],
|
||||
dangerousFlags: [{ path: "permissionMode", equals: "approve-all" }],
|
||||
secretInputs: {
|
||||
bundledDefaultEnabled: false,
|
||||
|
||||
@@ -66,6 +66,13 @@ export type PluginManifestSecretInputContracts = {
|
||||
};
|
||||
|
||||
export type PluginManifestConfigContracts = {
|
||||
/**
|
||||
* Root-relative config paths that indicate this plugin's setup-time
|
||||
* compatibility migrations might apply. Use this to keep generic runtime
|
||||
* config reads from loading every plugin setup surface when the config does
|
||||
* not reference the plugin at all.
|
||||
*/
|
||||
compatibilityMigrationPaths?: string[];
|
||||
dangerousFlags?: PluginManifestDangerousConfigFlag[];
|
||||
secretInputs?: PluginManifestSecretInputContracts;
|
||||
};
|
||||
@@ -277,6 +284,7 @@ function normalizeManifestConfigContracts(
|
||||
if (!isRecord(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const compatibilityMigrationPaths = normalizeTrimmedStringList(value.compatibilityMigrationPaths);
|
||||
const rawSecretInputs = isRecord(value.secretInputs) ? value.secretInputs : undefined;
|
||||
const dangerousFlags = normalizeManifestDangerousConfigFlags(value.dangerousFlags);
|
||||
const secretInputPaths = rawSecretInputs
|
||||
@@ -294,6 +302,7 @@ function normalizeManifestConfigContracts(
|
||||
} satisfies PluginManifestSecretInputContracts)
|
||||
: undefined;
|
||||
const configContracts = {
|
||||
...(compatibilityMigrationPaths.length > 0 ? { compatibilityMigrationPaths } : {}),
|
||||
...(dangerousFlags ? { dangerousFlags } : {}),
|
||||
...(secretInputs ? { secretInputs } : {}),
|
||||
} satisfies PluginManifestConfigContracts;
|
||||
|
||||
@@ -27,6 +27,7 @@ vi.mock("./manifest-registry.js", () => ({
|
||||
|
||||
let clearPluginSetupRegistryCache: typeof import("./setup-registry.js").clearPluginSetupRegistryCache;
|
||||
let resolvePluginSetupRegistry: typeof import("./setup-registry.js").resolvePluginSetupRegistry;
|
||||
let runPluginSetupConfigMigrations: typeof import("./setup-registry.js").runPluginSetupConfigMigrations;
|
||||
|
||||
function makeTempDir(): string {
|
||||
return makeTrackedTempDir("openclaw-setup-registry", tempDirs);
|
||||
@@ -39,7 +40,7 @@ afterEach(() => {
|
||||
describe("setup-registry getJiti", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ clearPluginSetupRegistryCache, resolvePluginSetupRegistry } =
|
||||
({ clearPluginSetupRegistryCache, resolvePluginSetupRegistry, runPluginSetupConfigMigrations } =
|
||||
await import("./setup-registry.js"));
|
||||
clearPluginSetupRegistryCache();
|
||||
mocks.createJiti.mockReset();
|
||||
@@ -82,4 +83,140 @@ describe("setup-registry getJiti", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("skips setup-api loading when config has no relevant migration triggers", () => {
|
||||
const pluginRoot = makeTempDir();
|
||||
fs.writeFileSync(path.join(pluginRoot, "setup-api.js"), "export default {};\n", "utf-8");
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [
|
||||
{
|
||||
id: "amazon-bedrock",
|
||||
rootDir: pluginRoot,
|
||||
configContracts: {
|
||||
compatibilityMigrationPaths: ["models.bedrockDiscovery"],
|
||||
},
|
||||
},
|
||||
],
|
||||
diagnostics: [],
|
||||
});
|
||||
mocks.createJiti.mockImplementation(() => {
|
||||
return () => ({
|
||||
default: {
|
||||
register(api: {
|
||||
registerConfigMigration: (migrate: (config: unknown) => unknown) => void;
|
||||
}) {
|
||||
api.registerConfigMigration((config) => ({ config, changes: ["unexpected"] }));
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const result = runPluginSetupConfigMigrations({
|
||||
config: {
|
||||
models: {
|
||||
providers: {
|
||||
openai: { baseUrl: "https://api.openai.com/v1" },
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(result.changes).toEqual([]);
|
||||
expect(mocks.createJiti).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("loads only plugins whose manifest migration triggers match the config", () => {
|
||||
const bedrockRoot = makeTempDir();
|
||||
const voiceCallRoot = makeTempDir();
|
||||
fs.writeFileSync(path.join(bedrockRoot, "setup-api.js"), "export default {};\n", "utf-8");
|
||||
fs.writeFileSync(path.join(voiceCallRoot, "setup-api.js"), "export default {};\n", "utf-8");
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [
|
||||
{
|
||||
id: "amazon-bedrock",
|
||||
rootDir: bedrockRoot,
|
||||
configContracts: {
|
||||
compatibilityMigrationPaths: ["models.bedrockDiscovery"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "voice-call",
|
||||
rootDir: voiceCallRoot,
|
||||
configContracts: {
|
||||
compatibilityMigrationPaths: ["plugins.entries.voice-call.config"],
|
||||
},
|
||||
},
|
||||
],
|
||||
diagnostics: [],
|
||||
});
|
||||
mocks.createJiti.mockImplementation((modulePath: string) => {
|
||||
const pluginId = modulePath.includes(bedrockRoot) ? "amazon-bedrock" : "voice-call";
|
||||
return () => ({
|
||||
default: {
|
||||
register(api: {
|
||||
registerConfigMigration: (migrate: (config: unknown) => unknown) => void;
|
||||
}) {
|
||||
api.registerConfigMigration((config) => ({
|
||||
config,
|
||||
changes: [pluginId],
|
||||
}));
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const result = runPluginSetupConfigMigrations({
|
||||
config: {
|
||||
models: {
|
||||
bedrockDiscovery: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(result.changes).toEqual(["amazon-bedrock"]);
|
||||
expect(mocks.createJiti).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.createJiti.mock.calls[0]?.[0]).toBe(path.join(bedrockRoot, "setup-api.js"));
|
||||
});
|
||||
|
||||
it("still loads explicitly configured plugin entries without manifest trigger metadata", () => {
|
||||
const pluginRoot = makeTempDir();
|
||||
fs.writeFileSync(path.join(pluginRoot, "setup-api.js"), "export default {};\n", "utf-8");
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [{ id: "voice-call", rootDir: pluginRoot }],
|
||||
diagnostics: [],
|
||||
});
|
||||
mocks.createJiti.mockImplementation(() => {
|
||||
return () => ({
|
||||
default: {
|
||||
register(api: {
|
||||
registerConfigMigration: (migrate: (config: unknown) => unknown) => void;
|
||||
}) {
|
||||
api.registerConfigMigration((config) => ({ config, changes: ["voice-call"] }));
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const result = runPluginSetupConfigMigrations({
|
||||
config: {
|
||||
plugins: {
|
||||
entries: {
|
||||
"voice-call": {
|
||||
config: {
|
||||
provider: "log",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(result.changes).toEqual(["voice-call"]);
|
||||
expect(mocks.createJiti).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createJiti } from "jiti";
|
||||
import { normalizeProviderId } from "../agents/provider-id.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { buildPluginApi } from "./api-builder.js";
|
||||
import { collectPluginConfigContractMatches } from "./config-contracts.js";
|
||||
import { discoverOpenClawPlugins } from "./discovery.js";
|
||||
import { loadPluginManifestRegistry } from "./manifest-registry.js";
|
||||
import { resolvePluginCacheInputs } from "./roots.js";
|
||||
@@ -99,6 +100,7 @@ function getJiti(modulePath: string) {
|
||||
function buildSetupRegistryCacheKey(params: {
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
pluginIds?: readonly string[];
|
||||
}): string {
|
||||
const { roots, loadPaths } = resolvePluginCacheInputs({
|
||||
workspaceDir: params.workspaceDir,
|
||||
@@ -107,6 +109,7 @@ function buildSetupRegistryCacheKey(params: {
|
||||
return JSON.stringify({
|
||||
roots,
|
||||
loadPaths,
|
||||
pluginIds: params.pluginIds ? [...new Set(params.pluginIds)].toSorted() : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -160,6 +163,48 @@ function resolveSetupApiPath(rootDir: string): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function collectConfiguredPluginEntryIds(config: OpenClawConfig): string[] {
|
||||
const entries = config.plugins?.entries;
|
||||
if (!entries || typeof entries !== "object") {
|
||||
return [];
|
||||
}
|
||||
return Object.keys(entries)
|
||||
.map((pluginId) => pluginId.trim())
|
||||
.filter(Boolean)
|
||||
.toSorted();
|
||||
}
|
||||
|
||||
function resolveRelevantSetupMigrationPluginIds(params: {
|
||||
config: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): string[] {
|
||||
const ids = new Set<string>(collectConfiguredPluginEntryIds(params.config));
|
||||
const registry = loadPluginManifestRegistry({
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
cache: true,
|
||||
});
|
||||
for (const plugin of registry.plugins) {
|
||||
const paths = plugin.configContracts?.compatibilityMigrationPaths;
|
||||
if (!paths?.length) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
paths.some(
|
||||
(pathPattern) =>
|
||||
collectPluginConfigContractMatches({
|
||||
root: params.config,
|
||||
pathPattern,
|
||||
}).length > 0,
|
||||
)
|
||||
) {
|
||||
ids.add(plugin.id);
|
||||
}
|
||||
}
|
||||
return [...ids].toSorted();
|
||||
}
|
||||
|
||||
function resolveRegister(mod: OpenClawPluginModule): {
|
||||
definition?: { id?: string };
|
||||
register?: (api: ReturnType<typeof buildPluginApi>) => void | Promise<void>;
|
||||
@@ -189,17 +234,33 @@ function matchesProvider(provider: ProviderPlugin, providerId: string): boolean
|
||||
export function resolvePluginSetupRegistry(params?: {
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
pluginIds?: readonly string[];
|
||||
}): PluginSetupRegistry {
|
||||
const env = params?.env ?? process.env;
|
||||
const cacheKey = buildSetupRegistryCacheKey({
|
||||
workspaceDir: params?.workspaceDir,
|
||||
env,
|
||||
pluginIds: params?.pluginIds,
|
||||
});
|
||||
const cached = setupRegistryCache.get(cacheKey);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const selectedPluginIds = params?.pluginIds
|
||||
? new Set(params.pluginIds.map((pluginId) => pluginId.trim()).filter(Boolean))
|
||||
: null;
|
||||
if (selectedPluginIds && selectedPluginIds.size === 0) {
|
||||
const empty = {
|
||||
providers: [],
|
||||
cliBackends: [],
|
||||
configMigrations: [],
|
||||
autoEnableProbes: [],
|
||||
} satisfies PluginSetupRegistry;
|
||||
setupRegistryCache.set(cacheKey, empty);
|
||||
return empty;
|
||||
}
|
||||
|
||||
const providers: SetupProviderEntry[] = [];
|
||||
const cliBackends: SetupCliBackendEntry[] = [];
|
||||
const configMigrations: SetupConfigMigrationEntry[] = [];
|
||||
@@ -221,6 +282,9 @@ export function resolvePluginSetupRegistry(params?: {
|
||||
});
|
||||
|
||||
for (const record of manifestRegistry.plugins) {
|
||||
if (selectedPluginIds && !selectedPluginIds.has(record.id)) {
|
||||
continue;
|
||||
}
|
||||
const setupSource = record.setupSource ?? resolveSetupApiPath(record.rootDir);
|
||||
if (!setupSource) {
|
||||
continue;
|
||||
@@ -516,8 +580,16 @@ export function runPluginSetupConfigMigrations(params: {
|
||||
} {
|
||||
let next = params.config;
|
||||
const changes: string[] = [];
|
||||
const pluginIds = resolveRelevantSetupMigrationPluginIds(params);
|
||||
if (pluginIds.length === 0) {
|
||||
return { config: next, changes };
|
||||
}
|
||||
|
||||
for (const entry of resolvePluginSetupRegistry(params).configMigrations) {
|
||||
for (const entry of resolvePluginSetupRegistry({
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
pluginIds,
|
||||
}).configMigrations) {
|
||||
const migration = entry.migrate(next);
|
||||
if (!migration || migration.changes.length === 0) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user