mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 13:40:42 +00:00
refactor(qa): split Matrix QA into optional plugin (#66723)
Merged via squash.
Prepared head SHA: 27241bd089
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
committed by
GitHub
parent
3425823dfb
commit
82a2db71e8
161
src/plugin-sdk/qa-runner-runtime.ts
Normal file
161
src/plugin-sdk/qa-runner-runtime.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import type { Command } from "commander";
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import { listBundledQaRunnerCatalog } from "../plugins/qa-runner-catalog.js";
|
||||
import { tryLoadActivatedBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js";
|
||||
|
||||
export type QaRunnerCliRegistration = {
|
||||
commandName: string;
|
||||
register(qa: Command): void;
|
||||
};
|
||||
|
||||
type QaRunnerRuntimeSurface = {
|
||||
qaRunnerCliRegistrations?: readonly QaRunnerCliRegistration[];
|
||||
};
|
||||
|
||||
export type QaRunnerCliContribution =
|
||||
| {
|
||||
pluginId: string;
|
||||
commandName: string;
|
||||
description?: string;
|
||||
status: "available";
|
||||
registration: QaRunnerCliRegistration;
|
||||
}
|
||||
| {
|
||||
pluginId: string;
|
||||
commandName: string;
|
||||
description?: string;
|
||||
status: "blocked";
|
||||
}
|
||||
| {
|
||||
pluginId: string;
|
||||
commandName: string;
|
||||
description?: string;
|
||||
status: "missing";
|
||||
npmSpec: string;
|
||||
};
|
||||
|
||||
function listDeclaredQaRunnerPlugins(): Array<
|
||||
PluginManifestRecord & {
|
||||
qaRunners: NonNullable<PluginManifestRecord["qaRunners"]>;
|
||||
}
|
||||
> {
|
||||
return loadPluginManifestRegistry({ cache: true })
|
||||
.plugins.filter(
|
||||
(
|
||||
plugin,
|
||||
): plugin is PluginManifestRecord & {
|
||||
qaRunners: NonNullable<PluginManifestRecord["qaRunners"]>;
|
||||
} => Array.isArray(plugin.qaRunners) && plugin.qaRunners.length > 0,
|
||||
)
|
||||
.toSorted((left, right) => {
|
||||
const idCompare = left.id.localeCompare(right.id);
|
||||
if (idCompare !== 0) {
|
||||
return idCompare;
|
||||
}
|
||||
return left.rootDir.localeCompare(right.rootDir);
|
||||
});
|
||||
}
|
||||
|
||||
function indexRuntimeRegistrations(
|
||||
pluginId: string,
|
||||
surface: QaRunnerRuntimeSurface,
|
||||
): ReadonlyMap<string, QaRunnerCliRegistration> {
|
||||
const registrations = surface.qaRunnerCliRegistrations ?? [];
|
||||
const registrationByCommandName = new Map<string, QaRunnerCliRegistration>();
|
||||
for (const registration of registrations) {
|
||||
if (!registration?.commandName || typeof registration.register !== "function") {
|
||||
throw new Error(`QA runner plugin "${pluginId}" exported an invalid CLI registration`);
|
||||
}
|
||||
if (registrationByCommandName.has(registration.commandName)) {
|
||||
throw new Error(
|
||||
`QA runner plugin "${pluginId}" exported duplicate CLI registration "${registration.commandName}"`,
|
||||
);
|
||||
}
|
||||
registrationByCommandName.set(registration.commandName, registration);
|
||||
}
|
||||
return registrationByCommandName;
|
||||
}
|
||||
|
||||
function buildKnownQaRunnerCatalog(): readonly QaRunnerCliContribution[] {
|
||||
const knownRunners = listBundledQaRunnerCatalog();
|
||||
const seenCommandNames = new Map<string, string>();
|
||||
return knownRunners.map((runner) => {
|
||||
const previousOwner = seenCommandNames.get(runner.commandName);
|
||||
if (previousOwner) {
|
||||
throw new Error(
|
||||
`QA runner command "${runner.commandName}" declared by both "${previousOwner}" and "${runner.pluginId}"`,
|
||||
);
|
||||
}
|
||||
seenCommandNames.set(runner.commandName, runner.pluginId);
|
||||
return {
|
||||
pluginId: runner.pluginId,
|
||||
commandName: runner.commandName,
|
||||
...(runner.description ? { description: runner.description } : {}),
|
||||
status: "missing" as const,
|
||||
npmSpec: runner.npmSpec,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function listQaRunnerCliContributions(): readonly QaRunnerCliContribution[] {
|
||||
const contributions = new Map<string, QaRunnerCliContribution>();
|
||||
|
||||
for (const runner of buildKnownQaRunnerCatalog()) {
|
||||
contributions.set(runner.commandName, runner);
|
||||
}
|
||||
|
||||
for (const plugin of listDeclaredQaRunnerPlugins()) {
|
||||
const runtimeSurface =
|
||||
tryLoadActivatedBundledPluginPublicSurfaceModuleSync<QaRunnerRuntimeSurface>({
|
||||
dirName: plugin.id,
|
||||
artifactBasename: "runtime-api.js",
|
||||
});
|
||||
const runtimeRegistrationByCommandName = runtimeSurface
|
||||
? indexRuntimeRegistrations(plugin.id, runtimeSurface)
|
||||
: null;
|
||||
const declaredCommandNames = new Set(plugin.qaRunners.map((runner) => runner.commandName));
|
||||
|
||||
for (const runner of plugin.qaRunners) {
|
||||
const previous = contributions.get(runner.commandName);
|
||||
if (previous && previous.pluginId !== plugin.id) {
|
||||
throw new Error(
|
||||
`QA runner command "${runner.commandName}" declared by both "${previous.pluginId}" and "${plugin.id}"`,
|
||||
);
|
||||
}
|
||||
|
||||
const registration = runtimeRegistrationByCommandName?.get(runner.commandName);
|
||||
if (!runtimeSurface) {
|
||||
contributions.set(runner.commandName, {
|
||||
pluginId: plugin.id,
|
||||
commandName: runner.commandName,
|
||||
...(runner.description ? { description: runner.description } : {}),
|
||||
status: "blocked",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (!registration) {
|
||||
throw new Error(
|
||||
`QA runner plugin "${plugin.id}" declared "${runner.commandName}" in openclaw.plugin.json but did not export a matching CLI registration`,
|
||||
);
|
||||
}
|
||||
contributions.set(runner.commandName, {
|
||||
pluginId: plugin.id,
|
||||
commandName: runner.commandName,
|
||||
...(runner.description ? { description: runner.description } : {}),
|
||||
status: "available",
|
||||
registration,
|
||||
});
|
||||
}
|
||||
|
||||
for (const commandName of runtimeRegistrationByCommandName?.keys() ?? []) {
|
||||
if (!declaredCommandNames.has(commandName)) {
|
||||
throw new Error(
|
||||
`QA runner plugin "${plugin.id}" exported "${commandName}" from runtime-api.js but did not declare it in openclaw.plugin.json`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...contributions.values()];
|
||||
}
|
||||
Reference in New Issue
Block a user