ci(qa): slim release transport startup

This commit is contained in:
Peter Steinberger
2026-04-29 05:25:27 +01:00
parent 806a0119f3
commit 5eb9b3da34
7 changed files with 120 additions and 6 deletions

View File

@@ -667,8 +667,8 @@ jobs:
--repo-root . \
--output-dir "${output_dir}" \
--provider-mode mock-openai \
--model "${OPENCLAW_CI_OPENAI_MODEL}" \
--alt-model openai/gpt-5.4-alt \
--model mock-openai/gpt-5.5 \
--alt-model mock-openai/gpt-5.5-alt \
--profile fast \
--fast
)
@@ -755,8 +755,8 @@ jobs:
--repo-root . \
--output-dir "${output_dir}" \
--provider-mode mock-openai \
--model "${OPENCLAW_CI_OPENAI_MODEL}" \
--alt-model openai/gpt-5.4-alt \
--model mock-openai/gpt-5.5 \
--alt-model mock-openai/gpt-5.5-alt \
--fast \
--credential-source convex \
--credential-role ci

View File

@@ -87,6 +87,7 @@ describe("buildQaRuntimeEnv", () => {
expect(env.OPENCLAW_TEST_FAST).toBe("1");
expect(env.OPENCLAW_QA_ALLOW_LOCAL_IMAGE_PROVIDER).toBe("1");
expect(env.OPENCLAW_ALLOW_SLOW_REPLY_TESTS).toBe("1");
expect(env.OPENCLAW_SKIP_STARTUP_MODEL_PREWARM).toBe("1");
expect(env.OPENCLAW_BUNDLED_PLUGINS_DIR).toBe("/tmp/openclaw-qa/bundled-plugins");
expect(env.OPENCLAW_COMPATIBILITY_HOST_VERSION).toBe("2026.4.8");
});

View File

@@ -212,6 +212,7 @@ export function buildQaRuntimeEnv(params: {
OPENCLAW_SKIP_BROWSER_CONTROL_SERVER: "1",
OPENCLAW_SKIP_GMAIL_WATCHER: "1",
OPENCLAW_SKIP_CANVAS_HOST: "1",
OPENCLAW_SKIP_STARTUP_MODEL_PREWARM: "1",
OPENCLAW_NO_RESPAWN: "1",
OPENCLAW_TEST_FAST: "1",
OPENCLAW_QA_ALLOW_LOCAL_IMAGE_PROVIDER: "1",

View File

@@ -94,6 +94,54 @@ describe("startQaLiveLaneGateway", () => {
expect(mockStop).toHaveBeenCalledTimes(1);
});
it("disables memory search for transport-only live lanes", async () => {
await startQaLiveLaneGateway({
repoRoot: "/tmp/openclaw-repo",
transport: createStubTransport(),
transportBaseUrl: "http://127.0.0.1:43123",
providerMode: "mock-openai",
primaryModel: "mock-openai/gpt-5.5",
alternateModel: "mock-openai/gpt-5.5-alt",
controlUiEnabled: false,
});
const [{ mutateConfig }] = startQaGatewayChild.mock.calls[0] ?? [];
expect(typeof mutateConfig).toBe("function");
const cfg = mutateConfig?.({
plugins: {
allow: ["acpx", "memory-core", "qa-channel"],
entries: {
acpx: { enabled: true },
"memory-core": { enabled: true },
"qa-channel": { enabled: true },
},
},
agents: {
defaults: {
memorySearch: {
enabled: true,
sync: {
onSearch: true,
onSessionStart: true,
watch: true,
},
},
},
},
});
expect(cfg?.plugins?.allow).toEqual(["acpx", "qa-channel"]);
expect(cfg?.plugins?.entries).not.toHaveProperty("memory-core");
expect(cfg?.agents?.defaults?.memorySearch).toMatchObject({
enabled: false,
sync: {
onSearch: false,
onSessionStart: false,
watch: false,
},
});
});
it("forwards gateway stop options to the child harness", async () => {
const harness = await startQaLiveLaneGateway({
repoRoot: "/tmp/openclaw-repo",

View File

@@ -34,6 +34,44 @@ async function stopQaLiveLaneResources(
}
}
function omitMemoryCoreEntry<T extends Record<string, unknown> | undefined>(entries: T): T {
if (!entries || !Object.prototype.hasOwnProperty.call(entries, "memory-core")) {
return entries;
}
const { "memory-core": _memoryCore, ...rest } = entries;
return rest as T;
}
function prepareLiveTransportGatewayConfig(cfg: OpenClawConfig): OpenClawConfig {
const defaults = cfg.agents?.defaults ?? {};
return {
...cfg,
plugins: cfg.plugins
? {
...cfg.plugins,
allow: cfg.plugins.allow?.filter((pluginId) => pluginId !== "memory-core"),
entries: omitMemoryCoreEntry(cfg.plugins.entries),
}
: cfg.plugins,
agents: {
...cfg.agents,
defaults: {
...defaults,
memorySearch: {
...defaults.memorySearch,
enabled: false,
sync: {
...defaults.memorySearch?.sync,
onSearch: false,
onSessionStart: false,
watch: false,
},
},
},
},
};
}
export async function startQaLiveLaneGateway(params: {
repoRoot: string;
command?: QaGatewayChildCommand;
@@ -70,7 +108,8 @@ export async function startQaLiveLaneGateway(params: {
thinkingDefault: params.thinkingDefault,
claudeCliAuthMode: params.claudeCliAuthMode,
controlUiEnabled: params.controlUiEnabled,
mutateConfig: params.mutateConfig,
mutateConfig: (cfg) =>
prepareLiveTransportGatewayConfig(params.mutateConfig ? params.mutateConfig(cfg) : cfg),
});
return {
gateway,

View File

@@ -23,6 +23,7 @@ const ACP_BACKEND_READY_TIMEOUT_MS = 5_000;
const ACP_BACKEND_READY_POLL_MS = 50;
const PRIMARY_MODEL_PREWARM_TIMEOUT_MS = 5_000;
const STARTUP_PROVIDER_DISCOVERY_TIMEOUT_MS = 5_000;
const SKIP_STARTUP_MODEL_PREWARM_ENV = "OPENCLAW_SKIP_STARTUP_MODEL_PREWARM";
type Awaitable<T> = T | Promise<T>;
@@ -43,6 +44,11 @@ function shouldCheckRestartSentinel(env: NodeJS.ProcessEnv = process.env): boole
return !env.VITEST && env.NODE_ENV !== "test";
}
function shouldSkipStartupModelPrewarm(env: NodeJS.ProcessEnv = process.env): boolean {
const raw = env[SKIP_STARTUP_MODEL_PREWARM_ENV]?.trim().toLowerCase();
return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
}
function shouldStartGatewayMemoryBackend(cfg: OpenClawConfig): boolean {
return cfg.memory?.backend === "qmd";
}
@@ -189,6 +195,9 @@ function schedulePrimaryModelPrewarm(
},
prewarm: typeof prewarmConfiguredPrimaryModel = prewarmConfiguredPrimaryModel,
): void {
if (shouldSkipStartupModelPrewarm()) {
return;
}
void measureStartup(params.startupTrace, "sidecars.model-prewarm", () =>
prewarmConfiguredPrimaryModelWithTimeout(
{
@@ -661,4 +670,5 @@ export const __testing = {
prewarmConfiguredPrimaryModel,
prewarmConfiguredPrimaryModelWithTimeout,
schedulePrimaryModelPrewarm,
shouldSkipStartupModelPrewarm,
};

View File

@@ -32,11 +32,12 @@ vi.mock("../agents/pi-embedded-runner/runtime.js", () => ({
}));
let prewarmConfiguredPrimaryModel: typeof import("./server-startup.js").__testing.prewarmConfiguredPrimaryModel;
let shouldSkipStartupModelPrewarm: typeof import("./server-startup.js").__testing.shouldSkipStartupModelPrewarm;
describe("gateway startup primary model warmup", () => {
beforeAll(async () => {
({
__testing: { prewarmConfiguredPrimaryModel },
__testing: { prewarmConfiguredPrimaryModel, shouldSkipStartupModelPrewarm },
} = await import("./server-startup.js"));
});
@@ -84,6 +85,20 @@ describe("gateway startup primary model warmup", () => {
expect(piModelModuleLoadedMock).not.toHaveBeenCalled();
});
it("honors the startup model prewarm skip env", () => {
expect(shouldSkipStartupModelPrewarm({})).toBe(false);
expect(
shouldSkipStartupModelPrewarm({
OPENCLAW_SKIP_STARTUP_MODEL_PREWARM: "1",
}),
).toBe(true);
expect(
shouldSkipStartupModelPrewarm({
OPENCLAW_SKIP_STARTUP_MODEL_PREWARM: "true",
}),
).toBe(true);
});
it("skips static warmup for configured CLI backends", async () => {
await prewarmConfiguredPrimaryModel({
cfg: {