mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-29 04:57:09 +02:00
640 lines
21 KiB
JavaScript
640 lines
21 KiB
JavaScript
// Docker E2E scenario catalog.
|
|
// Keep lane names, commands, image kind, timeout, resources, and release chunks
|
|
// here. Planning and execution live in separate modules.
|
|
|
|
const BUNDLED_UPDATE_NO_OUTPUT_TIMEOUT_MS = 4 * 60 * 1000;
|
|
const BUNDLED_UPDATE_TIMEOUT_MS = 6 * 60 * 1000;
|
|
export const DEFAULT_LIVE_RETRIES = 1;
|
|
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,
|
|
/overloaded/i,
|
|
/capacity/i,
|
|
/rate.?limit/i,
|
|
/gateway closed \(1000 normal closure\)/i,
|
|
/ECONNRESET|ETIMEDOUT|ENOTFOUND/i,
|
|
];
|
|
|
|
const bundledChannelLaneCommand =
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO=0 pnpm test:docker:bundled-channel-deps";
|
|
|
|
function liveDockerScriptCommand(script, envPrefix = "") {
|
|
const prefix = envPrefix ? `${envPrefix} ` : "";
|
|
return `${prefix}OPENCLAW_SKIP_DOCKER_BUILD=1 bash -c 'harness="\${OPENCLAW_DOCKER_E2E_TRUSTED_HARNESS_DIR:-}"; if [ -z "$harness" ]; then if [ -d .release-harness/scripts ]; then harness=.release-harness; else harness=.; fi; fi; OPENCLAW_LIVE_DOCKER_REPO_ROOT="\${OPENCLAW_DOCKER_E2E_REPO_ROOT:-$PWD}" bash "$harness/scripts/${script}"'`;
|
|
}
|
|
|
|
function lane(name, command, options = {}) {
|
|
return {
|
|
cacheKey: options.cacheKey,
|
|
command,
|
|
e2eImageKind:
|
|
options.e2eImageKind === false
|
|
? undefined
|
|
: (options.e2eImageKind ?? (options.live ? undefined : "functional")),
|
|
estimateSeconds: options.estimateSeconds,
|
|
live: options.live === true,
|
|
noOutputTimeoutMs: options.noOutputTimeoutMs,
|
|
name,
|
|
retryPatterns: options.retryPatterns ?? [],
|
|
retries: options.retries ?? 0,
|
|
resources: options.resources ?? [],
|
|
stateScenario: options.stateScenario,
|
|
timeoutMs: options.timeoutMs,
|
|
weight: options.weight ?? 1,
|
|
};
|
|
}
|
|
|
|
function liveProviderResource(provider) {
|
|
if (!provider) {
|
|
return undefined;
|
|
}
|
|
if (provider === "claude-cli" || provider === "claude") {
|
|
return "live:claude";
|
|
}
|
|
if (provider === "codex-cli" || provider === "codex") {
|
|
return "live:codex";
|
|
}
|
|
if (provider === "droid") {
|
|
return "live:droid";
|
|
}
|
|
if (provider === "google-gemini-cli" || provider === "gemini") {
|
|
return "live:gemini";
|
|
}
|
|
if (provider === "opencode") {
|
|
return "live:opencode";
|
|
}
|
|
if (provider === "openai") {
|
|
return "live:openai";
|
|
}
|
|
return `live:${provider}`;
|
|
}
|
|
|
|
function liveProviderResources(options) {
|
|
const providers = options.providers ?? (options.provider ? [options.provider] : []);
|
|
return providers.map(liveProviderResource).filter(Boolean);
|
|
}
|
|
|
|
function liveLane(name, command, options = {}) {
|
|
return lane(name, command, {
|
|
...options,
|
|
live: true,
|
|
resources: ["live", ...liveProviderResources(options), ...(options.resources ?? [])],
|
|
retryPatterns: options.retryPatterns ?? LIVE_RETRY_PATTERNS,
|
|
retries: options.retries ?? DEFAULT_LIVE_RETRIES,
|
|
weight: options.weight ?? 3,
|
|
});
|
|
}
|
|
|
|
function npmLane(name, command, options = {}) {
|
|
return lane(name, command, {
|
|
...options,
|
|
e2eImageKind: options.e2eImageKind ?? "bare",
|
|
resources: ["npm", ...(options.resources ?? [])],
|
|
weight: options.weight ?? 2,
|
|
});
|
|
}
|
|
|
|
function serviceLane(name, command, options = {}) {
|
|
return lane(name, command, {
|
|
...options,
|
|
resources: ["service", ...(options.resources ?? [])],
|
|
weight: options.weight ?? 2,
|
|
});
|
|
}
|
|
|
|
function bundledChannelScenarioLane(name, env, options = {}) {
|
|
return npmLane(
|
|
name,
|
|
`${env} OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:bundled-channel-deps`,
|
|
options,
|
|
);
|
|
}
|
|
|
|
const bundledChannelSmokeLanes = ["telegram", "discord", "slack", "feishu", "memory-lancedb"].map(
|
|
(channel) =>
|
|
npmLane(
|
|
`bundled-channel-${channel}`,
|
|
`OPENCLAW_BUNDLED_CHANNELS=${channel} ${bundledChannelLaneCommand}`,
|
|
{ stateScenario: "empty" },
|
|
),
|
|
);
|
|
|
|
const bundledChannelUpdateLanes = [
|
|
"telegram",
|
|
"discord",
|
|
"slack",
|
|
"feishu",
|
|
"memory-lancedb",
|
|
"acpx",
|
|
].map((target) =>
|
|
bundledChannelScenarioLane(
|
|
`bundled-channel-update-${target}`,
|
|
`OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=1 OPENCLAW_BUNDLED_CHANNEL_UPDATE_TARGETS=${target} OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO=0`,
|
|
{
|
|
noOutputTimeoutMs: BUNDLED_UPDATE_NO_OUTPUT_TIMEOUT_MS,
|
|
retryPatterns: LIVE_RETRY_PATTERNS,
|
|
retries: 1,
|
|
stateScenario: "empty",
|
|
timeoutMs: BUNDLED_UPDATE_TIMEOUT_MS,
|
|
},
|
|
),
|
|
);
|
|
|
|
const bundledChannelContractLanes = [
|
|
bundledChannelScenarioLane(
|
|
"bundled-channel-root-owned",
|
|
"OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=1 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO=0",
|
|
),
|
|
bundledChannelScenarioLane(
|
|
"bundled-channel-setup-entry",
|
|
"OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=1 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO=0",
|
|
{ stateScenario: "empty" },
|
|
),
|
|
bundledChannelScenarioLane(
|
|
"bundled-channel-load-failure",
|
|
"OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=1 OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO=0",
|
|
{ stateScenario: "empty" },
|
|
),
|
|
bundledChannelScenarioLane(
|
|
"bundled-channel-disabled-config",
|
|
"OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO=1",
|
|
{ stateScenario: "empty" },
|
|
),
|
|
];
|
|
|
|
const bundledScenarioLanes = [
|
|
...bundledChannelSmokeLanes,
|
|
...bundledChannelUpdateLanes,
|
|
...bundledChannelContractLanes,
|
|
];
|
|
|
|
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`,
|
|
{
|
|
estimateSeconds: 280,
|
|
resources: ["npm"],
|
|
stateScenario: "empty",
|
|
weight: 1,
|
|
},
|
|
),
|
|
);
|
|
|
|
export const mainLanes = [
|
|
liveLane("live-models", liveDockerScriptCommand("test-live-models-docker.sh"), {
|
|
providers: ["claude-cli", "codex-cli", "google-gemini-cli"],
|
|
timeoutMs: LIVE_PROFILE_TIMEOUT_MS,
|
|
weight: 4,
|
|
}),
|
|
liveLane("live-gateway", liveDockerScriptCommand("test-live-gateway-models-docker.sh"), {
|
|
providers: ["claude-cli", "codex-cli", "google-gemini-cli"],
|
|
timeoutMs: LIVE_PROFILE_TIMEOUT_MS,
|
|
weight: 4,
|
|
}),
|
|
liveLane(
|
|
"live-cli-backend-claude",
|
|
liveDockerScriptCommand(
|
|
"test-live-cli-backend-docker.sh",
|
|
"OPENCLAW_LIVE_CLI_BACKEND_MODEL=claude-cli/claude-sonnet-4-6",
|
|
),
|
|
{
|
|
cacheKey: "cli-backend-claude",
|
|
provider: "claude-cli",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_CLI_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
liveLane(
|
|
"live-cli-backend-gemini",
|
|
liveDockerScriptCommand(
|
|
"test-live-cli-backend-docker.sh",
|
|
"OPENCLAW_LIVE_CLI_BACKEND_MODEL=google-gemini-cli/gemini-3-flash-preview",
|
|
),
|
|
{
|
|
cacheKey: "cli-backend-gemini",
|
|
provider: "google-gemini-cli",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_CLI_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
serviceLane("openwebui", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openwebui", {
|
|
timeoutMs: OPENWEBUI_TIMEOUT_MS,
|
|
weight: 5,
|
|
}),
|
|
serviceLane("onboard", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:onboard", {
|
|
stateScenario: "empty",
|
|
weight: 2,
|
|
}),
|
|
npmLane(
|
|
"npm-onboard-channel-agent",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:npm-onboard-channel-agent",
|
|
{ resources: ["service"], stateScenario: "empty", weight: 3 },
|
|
),
|
|
serviceLane("gateway-network", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:gateway-network"),
|
|
serviceLane(
|
|
"agents-delete-shared-workspace",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:agents-delete-shared-workspace",
|
|
{ stateScenario: "empty" },
|
|
),
|
|
serviceLane("mcp-channels", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels", {
|
|
resources: ["npm"],
|
|
stateScenario: "empty",
|
|
weight: 3,
|
|
}),
|
|
lane("pi-bundle-mcp-tools", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:pi-bundle-mcp-tools", {
|
|
stateScenario: "empty",
|
|
}),
|
|
lane("crestodian-rescue", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-rescue", {
|
|
stateScenario: "empty",
|
|
}),
|
|
lane("crestodian-planner", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-planner", {
|
|
stateScenario: "empty",
|
|
}),
|
|
serviceLane(
|
|
"cron-mcp-cleanup",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup",
|
|
{ resources: ["npm"], stateScenario: "empty", weight: 3 },
|
|
),
|
|
npmLane("doctor-switch", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:doctor-switch", {
|
|
stateScenario: "empty",
|
|
weight: 3,
|
|
}),
|
|
npmLane(
|
|
"update-channel-switch",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:update-channel-switch",
|
|
{
|
|
stateScenario: "update-stable",
|
|
timeoutMs: 30 * 60 * 1000,
|
|
weight: 3,
|
|
},
|
|
),
|
|
lane("plugins", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugins", {
|
|
resources: ["npm", "service"],
|
|
stateScenario: "empty",
|
|
weight: 6,
|
|
}),
|
|
lane("kitchen-sink-plugin", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:kitchen-sink-plugin", {
|
|
resources: ["npm"],
|
|
stateScenario: "empty",
|
|
weight: 3,
|
|
}),
|
|
...bundledPluginInstallUninstallLanes,
|
|
lane(
|
|
"plugins-offline",
|
|
"OPENCLAW_PLUGINS_E2E_CLAWHUB=0 OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugins",
|
|
{
|
|
resources: ["npm", "service"],
|
|
stateScenario: "empty",
|
|
weight: 6,
|
|
},
|
|
),
|
|
npmLane(
|
|
"bundled-channel-deps-compat",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:bundled-channel-deps:fast",
|
|
{ resources: ["service"], stateScenario: "empty", weight: 3 },
|
|
),
|
|
npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update", {
|
|
stateScenario: "empty",
|
|
}),
|
|
serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload", {
|
|
stateScenario: "empty",
|
|
}),
|
|
...bundledScenarioLanes,
|
|
lane("openai-image-auth", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-image-auth", {
|
|
stateScenario: "empty",
|
|
}),
|
|
lane(
|
|
"crestodian-first-run",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-first-run",
|
|
{ stateScenario: "empty" },
|
|
),
|
|
lane(
|
|
"session-runtime-context",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:session-runtime-context",
|
|
),
|
|
lane("qr", "pnpm test:docker:qr"),
|
|
];
|
|
|
|
export const tailLanes = [
|
|
serviceLane(
|
|
"openai-web-search-minimal",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-web-search-minimal",
|
|
{ stateScenario: "empty", timeoutMs: 8 * 60 * 1000 },
|
|
),
|
|
liveLane("live-codex-harness", liveDockerScriptCommand("test-live-codex-harness-docker.sh"), {
|
|
cacheKey: "codex-harness",
|
|
provider: "codex-cli",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
|
weight: 3,
|
|
}),
|
|
liveLane(
|
|
"live-codex-bind",
|
|
liveDockerScriptCommand(
|
|
"test-live-codex-harness-docker.sh",
|
|
"OPENCLAW_LIVE_CODEX_BIND=1 OPENCLAW_LIVE_CODEX_TEST_FILES=src/gateway/gateway-codex-bind.live.test.ts",
|
|
),
|
|
{
|
|
cacheKey: "codex-harness",
|
|
provider: "codex-cli",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
liveLane(
|
|
"live-cli-backend-codex",
|
|
liveDockerScriptCommand(
|
|
"test-live-cli-backend-docker.sh",
|
|
"OPENCLAW_LIVE_CLI_BACKEND_MODEL=codex-cli/gpt-5.5",
|
|
),
|
|
{
|
|
cacheKey: "cli-backend-codex",
|
|
provider: "codex-cli",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_CLI_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
liveLane(
|
|
"live-acp-bind-claude",
|
|
liveDockerScriptCommand("test-live-acp-bind-docker.sh", "OPENCLAW_LIVE_ACP_BIND_AGENT=claude"),
|
|
{
|
|
cacheKey: "acp-bind-claude",
|
|
provider: "claude-cli",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
liveLane(
|
|
"live-acp-bind-codex",
|
|
liveDockerScriptCommand("test-live-acp-bind-docker.sh", "OPENCLAW_LIVE_ACP_BIND_AGENT=codex"),
|
|
{
|
|
cacheKey: "acp-bind-codex",
|
|
provider: "codex-cli",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
liveLane(
|
|
"live-acp-bind-droid",
|
|
liveDockerScriptCommand(
|
|
"test-live-acp-bind-docker.sh",
|
|
"OPENCLAW_LIVE_ACP_BIND_AGENT=droid OPENCLAW_LIVE_ACP_BIND_REQUIRE_TRANSCRIPT=1",
|
|
),
|
|
{
|
|
cacheKey: "acp-bind-droid",
|
|
provider: "droid",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
liveLane(
|
|
"live-acp-bind-gemini",
|
|
liveDockerScriptCommand("test-live-acp-bind-docker.sh", "OPENCLAW_LIVE_ACP_BIND_AGENT=gemini"),
|
|
{
|
|
cacheKey: "acp-bind-gemini",
|
|
provider: "google-gemini-cli",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
liveLane(
|
|
"live-acp-bind-opencode",
|
|
liveDockerScriptCommand(
|
|
"test-live-acp-bind-docker.sh",
|
|
"OPENCLAW_LIVE_ACP_BIND_AGENT=opencode OPENCLAW_LIVE_ACP_BIND_REQUIRE_TRANSCRIPT=1",
|
|
),
|
|
{
|
|
cacheKey: "acp-bind-opencode",
|
|
provider: "opencode",
|
|
resources: ["npm"],
|
|
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
|
weight: 3,
|
|
},
|
|
),
|
|
];
|
|
|
|
const releasePathPluginRuntimeLanes = [
|
|
lane("plugins", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugins", {
|
|
resources: ["npm", "service"],
|
|
stateScenario: "empty",
|
|
weight: 6,
|
|
}),
|
|
...bundledPluginInstallUninstallLanes,
|
|
serviceLane(
|
|
"cron-mcp-cleanup",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup",
|
|
{
|
|
resources: ["npm"],
|
|
stateScenario: "empty",
|
|
weight: 3,
|
|
},
|
|
),
|
|
serviceLane(
|
|
"openai-web-search-minimal",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-web-search-minimal",
|
|
{ stateScenario: "empty", timeoutMs: 8 * 60 * 1000 },
|
|
),
|
|
];
|
|
|
|
const releasePathPluginRuntimePluginLanes = [
|
|
lane("plugins", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugins", {
|
|
resources: ["npm", "service"],
|
|
stateScenario: "empty",
|
|
weight: 6,
|
|
}),
|
|
];
|
|
|
|
const releasePathPluginRuntimeServiceLanes = [
|
|
serviceLane(
|
|
"cron-mcp-cleanup",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup",
|
|
{
|
|
resources: ["npm"],
|
|
stateScenario: "empty",
|
|
weight: 3,
|
|
},
|
|
),
|
|
serviceLane(
|
|
"openai-web-search-minimal",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-web-search-minimal",
|
|
{ stateScenario: "empty", timeoutMs: 8 * 60 * 1000 },
|
|
),
|
|
];
|
|
|
|
const releasePathPluginRuntimeCoreLanes = [
|
|
...releasePathPluginRuntimePluginLanes,
|
|
...releasePathPluginRuntimeServiceLanes,
|
|
];
|
|
|
|
const releasePathBundledChannelLanes = [
|
|
npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update", {
|
|
stateScenario: "empty",
|
|
}),
|
|
...bundledScenarioLanes,
|
|
];
|
|
|
|
const releasePathPackageInstallOpenAiLanes = [
|
|
npmLane(
|
|
"install-e2e-openai",
|
|
"OPENCLAW_INSTALL_TAG=beta OPENCLAW_E2E_MODELS=openai OPENCLAW_INSTALL_E2E_IMAGE=openclaw-install-e2e-openai:local pnpm test:install:e2e",
|
|
{
|
|
resources: ["service"],
|
|
weight: 3,
|
|
},
|
|
),
|
|
];
|
|
|
|
const releasePathPackageInstallAnthropicLanes = [
|
|
npmLane(
|
|
"install-e2e-anthropic",
|
|
"OPENCLAW_INSTALL_TAG=beta OPENCLAW_E2E_MODELS=anthropic OPENCLAW_INSTALL_E2E_IMAGE=openclaw-install-e2e-anthropic:local pnpm test:install:e2e",
|
|
{
|
|
resources: ["service"],
|
|
weight: 3,
|
|
},
|
|
),
|
|
];
|
|
|
|
const releasePathPackageUpdateCoreLanes = [
|
|
npmLane(
|
|
"npm-onboard-channel-agent",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:npm-onboard-channel-agent",
|
|
{ resources: ["service"], stateScenario: "empty", weight: 3 },
|
|
),
|
|
npmLane("doctor-switch", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:doctor-switch", {
|
|
stateScenario: "empty",
|
|
weight: 3,
|
|
}),
|
|
npmLane(
|
|
"update-channel-switch",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:update-channel-switch",
|
|
{
|
|
stateScenario: "update-stable",
|
|
timeoutMs: 30 * 60 * 1000,
|
|
weight: 3,
|
|
},
|
|
),
|
|
];
|
|
|
|
const primaryReleasePathChunks = {
|
|
core: [
|
|
lane("qr", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:qr"),
|
|
serviceLane("onboard", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:onboard", {
|
|
stateScenario: "empty",
|
|
weight: 2,
|
|
}),
|
|
serviceLane("gateway-network", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:gateway-network"),
|
|
serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload", {
|
|
stateScenario: "empty",
|
|
}),
|
|
lane(
|
|
"session-runtime-context",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:session-runtime-context",
|
|
),
|
|
lane(
|
|
"pi-bundle-mcp-tools",
|
|
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:pi-bundle-mcp-tools",
|
|
{ stateScenario: "empty" },
|
|
),
|
|
serviceLane("mcp-channels", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels", {
|
|
resources: ["npm"],
|
|
stateScenario: "empty",
|
|
weight: 3,
|
|
}),
|
|
],
|
|
"package-update-openai": releasePathPackageInstallOpenAiLanes,
|
|
"package-update-anthropic": releasePathPackageInstallAnthropicLanes,
|
|
"package-update-core": releasePathPackageUpdateCoreLanes,
|
|
"plugins-runtime-plugins": releasePathPluginRuntimePluginLanes,
|
|
"plugins-runtime-services": releasePathPluginRuntimeServiceLanes,
|
|
"plugins-runtime-install-a": bundledPluginInstallUninstallLanes.slice(0, 2),
|
|
"plugins-runtime-install-b": bundledPluginInstallUninstallLanes.slice(2, 4),
|
|
"plugins-runtime-install-c": bundledPluginInstallUninstallLanes.slice(4, 6),
|
|
"plugins-runtime-install-d": bundledPluginInstallUninstallLanes.slice(6),
|
|
"bundled-channels-core": [releasePathBundledChannelLanes[0], ...bundledChannelSmokeLanes],
|
|
"bundled-channels-update-a": [bundledChannelUpdateLanes[0], bundledChannelUpdateLanes[4]],
|
|
"bundled-channels-update-discord": [bundledChannelUpdateLanes[1]],
|
|
"bundled-channels-update-b": [
|
|
bundledChannelUpdateLanes[2],
|
|
bundledChannelUpdateLanes[3],
|
|
bundledChannelUpdateLanes[5],
|
|
],
|
|
"bundled-channels-contracts": bundledChannelContractLanes,
|
|
openwebui: [],
|
|
};
|
|
|
|
const legacyReleasePathChunks = {
|
|
"package-update": [
|
|
...releasePathPackageInstallOpenAiLanes,
|
|
...releasePathPackageInstallAnthropicLanes,
|
|
...releasePathPackageUpdateCoreLanes,
|
|
],
|
|
"plugins-runtime-core": releasePathPluginRuntimeCoreLanes,
|
|
"plugins-runtime": releasePathPluginRuntimeLanes,
|
|
"plugins-integrations": [...releasePathPluginRuntimeLanes, ...releasePathBundledChannelLanes],
|
|
"bundled-channels": releasePathBundledChannelLanes,
|
|
"bundled-channels-update-a-legacy": [
|
|
bundledChannelUpdateLanes[0],
|
|
bundledChannelUpdateLanes[1],
|
|
bundledChannelUpdateLanes[4],
|
|
],
|
|
};
|
|
|
|
function openWebUILane() {
|
|
return serviceLane("openwebui", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openwebui", {
|
|
timeoutMs: OPENWEBUI_TIMEOUT_MS,
|
|
weight: 5,
|
|
});
|
|
}
|
|
|
|
export function releasePathChunkLanes(chunk, options = {}) {
|
|
const base = primaryReleasePathChunks[chunk] ?? legacyReleasePathChunks[chunk];
|
|
if (!base) {
|
|
throw new Error(
|
|
`OPENCLAW_DOCKER_ALL_CHUNK must be one of: ${[
|
|
...Object.keys(primaryReleasePathChunks),
|
|
...Object.keys(legacyReleasePathChunks),
|
|
].join(", ")}. Got: ${JSON.stringify(chunk)}`,
|
|
);
|
|
}
|
|
if (chunk === "openwebui") {
|
|
return options.includeOpenWebUI ? [openWebUILane()] : [];
|
|
}
|
|
if (
|
|
(chunk !== "plugins-runtime-services" &&
|
|
chunk !== "plugins-runtime-core" &&
|
|
chunk !== "plugins-runtime" &&
|
|
chunk !== "plugins-integrations") ||
|
|
!options.includeOpenWebUI
|
|
) {
|
|
return base;
|
|
}
|
|
return [...base, openWebUILane()];
|
|
}
|
|
|
|
export function allReleasePathLanes(options = {}) {
|
|
return Object.keys(primaryReleasePathChunks)
|
|
.filter((chunk) => chunk !== "openwebui")
|
|
.flatMap((chunk) =>
|
|
releasePathChunkLanes(chunk, {
|
|
includeOpenWebUI: options.includeOpenWebUI,
|
|
}),
|
|
);
|
|
}
|