ci: shard bundled plugin release sweep

This commit is contained in:
Peter Steinberger
2026-04-27 13:05:11 +01:00
parent 0dfea099d6
commit f68ef1ae7c
5 changed files with 59 additions and 24 deletions

View File

@@ -295,7 +295,11 @@ and keeps a standalone `openwebui` chunk only for OpenWebUI-only dispatches.
The bundled-channel runtime-dependency coverage inside `plugins-integrations`
uses the split `bundled-channel-*` and `bundled-channel-update-*` lanes rather
than the serial `bundled-channel-deps` lane, so failures produce cheap targeted
reruns for the exact channel/update scenario.
reruns for the exact channel/update scenario. The bundled plugin
install/uninstall sweep is also split into
`bundled-plugin-install-uninstall-0` through
`bundled-plugin-install-uninstall-7`; selecting the legacy
`bundled-plugin-install-uninstall` lane expands to all eight shards.
## Package Acceptance

View File

@@ -325,6 +325,9 @@ Release Docker coverage includes:
- OpenWebUI coverage inside the `plugins-integrations` chunk when requested
- split bundled-channel dependency lanes inside `plugins-integrations` instead
of the serial all-in-one bundled-channel lane
- split bundled plugin install/uninstall lanes
`bundled-plugin-install-uninstall-0` through
`bundled-plugin-install-uninstall-7`
- live/E2E provider suites and Docker live model coverage when release checks
include live suites

View File

@@ -2,6 +2,7 @@
// This module turns the scenario catalog plus env-driven inputs into a concrete
// lane plan. It intentionally does not define scenario commands.
import {
BUNDLED_PLUGIN_INSTALL_UNINSTALL_SHARDS,
DEFAULT_LIVE_RETRIES,
allReleasePathLanes,
mainLanes,
@@ -34,14 +35,23 @@ export function parseLaneSelection(raw) {
if (!raw) {
return [];
}
const laneAliases = new Map([["bundled-channel-deps", "bundled-channel-deps-compat"]]);
const laneAliases = new Map([
["bundled-channel-deps", ["bundled-channel-deps-compat"]],
[
"bundled-plugin-install-uninstall",
Array.from(
{ length: BUNDLED_PLUGIN_INSTALL_UNINSTALL_SHARDS },
(_, index) => `bundled-plugin-install-uninstall-${index}`,
),
],
]);
return [
...new Set(
String(raw)
.split(/[,\s]+/u)
.map((token) => token.trim())
.filter(Boolean)
.map((token) => laneAliases.get(token) ?? token),
.flatMap((token) => laneAliases.get(token) ?? [token]),
),
];
}

View File

@@ -8,6 +8,7 @@ const LIVE_ACP_TIMEOUT_MS = 20 * 60 * 1000;
const LIVE_CLI_TIMEOUT_MS = 20 * 60 * 1000;
const LIVE_PROFILE_TIMEOUT_MS = 20 * 60 * 1000;
const OPENWEBUI_TIMEOUT_MS = 20 * 60 * 1000;
export const BUNDLED_PLUGIN_INSTALL_UNINSTALL_SHARDS = 8;
export const LIVE_RETRY_PATTERNS = [
/529\b/i,
@@ -138,6 +139,19 @@ const bundledScenarioLanes = [
),
];
const bundledPluginInstallUninstallLanes = Array.from(
{ length: BUNDLED_PLUGIN_INSTALL_UNINSTALL_SHARDS },
(_, index) =>
lane(
`bundled-plugin-install-uninstall-${index}`,
`OPENCLAW_BUNDLED_PLUGIN_SWEEP_TOTAL=${BUNDLED_PLUGIN_INSTALL_UNINSTALL_SHARDS} OPENCLAW_BUNDLED_PLUGIN_SWEEP_INDEX=${index} OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:bundled-plugin-install-uninstall`,
{
resources: ["npm"],
weight: 2,
},
),
);
export const mainLanes = [
liveLane("live-models", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-models", {
providers: ["claude-cli", "codex-cli", "google-gemini-cli"],
@@ -215,14 +229,7 @@ export const mainLanes = [
resources: ["npm", "service"],
weight: 6,
}),
lane(
"bundled-plugin-install-uninstall",
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:bundled-plugin-install-uninstall",
{
resources: ["npm"],
weight: 4,
},
),
...bundledPluginInstallUninstallLanes,
lane(
"plugins-offline",
"OPENCLAW_PLUGINS_E2E_CLAWHUB=0 OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugins",
@@ -395,14 +402,7 @@ const releasePathChunks = {
resources: ["npm", "service"],
weight: 6,
}),
lane(
"bundled-plugin-install-uninstall",
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:bundled-plugin-install-uninstall",
{
resources: ["npm"],
weight: 4,
},
),
...bundledPluginInstallUninstallLanes,
npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update"),
...bundledScenarioLanes,
serviceLane(

View File

@@ -45,6 +45,9 @@ describe("scripts/lib/docker-e2e-plan", () => {
expect(plan.lanes.map((lane) => lane.name)).toContain("mcp-channels");
expect(plan.lanes.map((lane) => lane.name)).toContain("bundled-channel-feishu");
expect(plan.lanes.map((lane) => lane.name)).toContain("bundled-channel-update-acpx");
expect(plan.lanes.map((lane) => lane.name)).toContain("bundled-plugin-install-uninstall-0");
expect(plan.lanes.map((lane) => lane.name)).toContain("bundled-plugin-install-uninstall-7");
expect(plan.lanes.map((lane) => lane.name)).not.toContain("bundled-plugin-install-uninstall");
expect(plan.lanes.map((lane) => lane.name)).not.toContain("bundled-channel-deps");
expect(plan.lanes.map((lane) => lane.name)).not.toContain("openwebui");
});
@@ -111,17 +114,32 @@ describe("scripts/lib/docker-e2e-plan", () => {
]);
});
it("plans bundled plugin install/uninstall as package-backed plugin coverage", () => {
const plan = planFor({ selectedLaneNames: ["bundled-plugin-install-uninstall"] });
it("maps bundled plugin install/uninstall to package-backed shards", () => {
const selectedLaneNames = parseLaneSelection("bundled-plugin-install-uninstall");
const plan = planFor({ selectedLaneNames });
expect(plan.lanes).toEqual([
expect(selectedLaneNames).toEqual(
Array.from({ length: 8 }, (_, index) => `bundled-plugin-install-uninstall-${index}`),
);
expect(plan.lanes).toHaveLength(8);
expect(plan.lanes[0]).toEqual(
expect.objectContaining({
command: expect.stringContaining("OPENCLAW_BUNDLED_PLUGIN_SWEEP_INDEX=0"),
imageKind: "functional",
live: false,
name: "bundled-plugin-install-uninstall",
name: "bundled-plugin-install-uninstall-0",
resources: expect.arrayContaining(["docker", "npm"]),
}),
]);
);
expect(plan.lanes[7]).toEqual(
expect.objectContaining({
command: expect.stringContaining("OPENCLAW_BUNDLED_PLUGIN_SWEEP_INDEX=7"),
imageKind: "functional",
live: false,
name: "bundled-plugin-install-uninstall-7",
resources: expect.arrayContaining(["docker", "npm"]),
}),
);
expect(plan.needs).toMatchObject({
functionalImage: true,
package: true,