diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ecd70ba1a6d5..2314f5863172 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,6 +87,9 @@ jobs: docs_only: ${{ steps.manifest.outputs.docs_only }} docs_changed: ${{ steps.manifest.outputs.docs_changed }} run_node: ${{ steps.manifest.outputs.run_node }} + # docker-seed-e2e-contract-v1: frozen targets without this marker skip the lane. + run_docker_seed_e2e: ${{ steps.manifest.outputs.run_docker_seed_e2e }} + docker_seed_lanes: ${{ steps.manifest.outputs.docker_seed_lanes }} run_macos: ${{ steps.manifest.outputs.run_macos }} run_android: ${{ steps.manifest.outputs.run_android }} run_skills_python: ${{ steps.manifest.outputs.run_skills_python }} @@ -636,6 +639,7 @@ jobs: : ""; const supportsOpenClawKitTests = targetWorkflow.includes("openclawkit-tests-contract-v1"); const supportsCurrentAndroidCi = targetWorkflow.includes("android-ci-contract-v2"); + const supportsDockerSeedE2e = targetWorkflow.includes("docker-seed-e2e-contract-v1"); const useCompatibleAndroidCi = compatibilityTarget && !supportsCurrentAndroidCi; const supportsFormatCheck = targetWorkflow.split("pnpm format:check").length - 1 >= 2; @@ -671,6 +675,13 @@ jobs: } const compactPullRequest = isCanonicalRepository && eventName === "pull_request"; + const dockerSeedLanes = + compactPullRequest && + changedPaths && + supportsDockerSeedE2e && + typeof changedNodeTestPlan.resolveChangedDockerSeedLanes === "function" + ? changedNodeTestPlan.resolveChangedDockerSeedLanes(changedPaths) + : []; // Canonical pushes also use compact bins: 80+ single-group jobs // drain the runner pool for minutes, and per-shard check names on // main have no branch-protection consumers. Dispatch (release @@ -808,6 +819,8 @@ jobs: docs_only: docsOnly, docs_changed: docsChanged, run_node: runNode, + run_docker_seed_e2e: dockerSeedLanes.length > 0, + docker_seed_lanes: dockerSeedLanes.join(" "), run_macos: runMacos, run_android: runAndroid, run_skills_python: runSkillsPython, @@ -4060,6 +4073,33 @@ jobs: ;; esac + docker-seed-e2e: + permissions: + contents: read + name: docker-seed-e2e + needs: [preflight] + if: needs.preflight.outputs.run_docker_seed_e2e == 'true' + runs-on: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' && 'ubuntu-24.04' || (vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' && github.run_attempt > 1) && 'ubuntu-24.04' || github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.event_name == 'pull_request' && (github.run_attempt > 1 || github.event.pull_request.head.repo.full_name != github.repository)) && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || contains(fromJSON('["OWNER","MEMBER","COLLABORATOR","CONTRIBUTOR"]'), github.event.pull_request.author_association)) && 'blacksmith-16vcpu-ubuntu-2404' || 'ubuntu-24.04') }} + timeout-minutes: 60 + steps: + - *linux_node_checkout_step + - name: Setup Node environment + uses: ./.github/actions/setup-node-env + with: + node-version: "24.x" + install-bun: "false" + dependency-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'false' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false') }} + use-actions-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'true' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true') }} + + - name: Run changed Docker seed owner lanes + env: + OPENCLAW_DOCKER_ALL_LANES: ${{ needs.preflight.outputs.docker_seed_lanes }} + OPENCLAW_DOCKER_ALL_LIVE_MODE: skip + OPENCLAW_DOCKER_E2E_ALLOW_UNRELEASED_CHANGELOG: "1" + OPENCLAW_DOCKER_ALL_PARALLELISM: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.head.repo.full_name == github.repository && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR","CONTRIBUTOR"]'), github.event.pull_request.author_association) && 3 || 1 }} + OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.head.repo.full_name == github.repository && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR","CONTRIBUTOR"]'), github.event.pull_request.author_association) && 3 || 1 }} + run: pnpm test:docker:all + ci-gate: permissions: contents: read @@ -4092,6 +4132,7 @@ jobs: - macos-swift - ios-build - android + - docker-seed-e2e if: ${{ always() && (github.event_name != 'pull_request' || !github.event.pull_request.draft) }} runs-on: ubuntu-24.04 timeout-minutes: 5 @@ -4128,6 +4169,7 @@ jobs: macos-swift=${{ needs.macos-swift.result }} ios-build=${{ needs.ios-build.result }} android=${{ needs.android.result }} + docker-seed-e2e=${{ needs.docker-seed-e2e.result }} run: | set -euo pipefail diff --git a/docs/ci.md b/docs/ci.md index 5d6c66795dbf..3cc52aa816d2 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -42,6 +42,7 @@ dispatch. | `checks-fast-contracts-plugins-*` | Two weighted plugin contract shards | Node-relevant changes | | `checks-fast-contracts-channels-*` | Two weighted channel contract shards | Node-relevant changes | | `checks-node-*` | Changed-target Node tests on pull requests; compact integration shards on `main`; metadata-complete compact fallback on broad PRs; full named shards on manual and release runs | Node-relevant changes | +| `docker-seed-e2e` | One Docker scheduler job for the executable `mcp-channels`, `cron-mcp-cleanup`, and `mcp-code-mode-gateway` owner lanes | PR changes to their seed helpers or CI gate owners | | `check-*` | Sharded main local gate equivalent: guards, transient npm-lock validation, bundled-channel config metadata, prod types, lint, dependencies, test types | Node-relevant changes | | `check-additional-*` | Boundary check stripes (including prompt snapshot drift), session accessor/transcript reader/SQLite transaction boundaries, extension lint groups, package boundary compile/canary, and runtime topology architecture; the pure-reporting plugin SDK API diff runs on manual and release dispatches only | Node-relevant changes | | `checks-node-compat-node22` | Node 22 compatibility build and smoke lane | Full Release Validation and manual dispatches only | @@ -57,6 +58,14 @@ dispatch. | `test-performance-agent` | Separate workflow: daily Codex slow-test optimization after trusted activity | Main CI success or manual dispatch | | `openclaw-performance` | Separate workflow: daily/on-demand Kova runtime performance reports with mock-provider, deep-profile, and GPT 5.6 live lanes | Scheduled and manual dispatch | +The rare path-triggered `docker-seed-e2e` job selects only the executable +owners of changed seed helpers and runs them through one scheduler invocation. +Trusted same-repository pull requests use one 16-vCPU Blacksmith runner with +main and tail parallelism set to 3; GitHub-hosted, fork, and retry paths run the +same selected lanes serially. The job is part of `openclaw/ci-gate`. It adds at +most one runner registration during an affected pull-request window and adds no +registrations for unrelated pull requests. + Standalone Periphery workflows enforce zero dead-code findings for the iOS and macOS apps. The shared OpenClawKit workflow scans both consumers in parallel and reports a declaration only when Periphery emits the same Swift USR from both builds. Its generated `OpenClawProtocol/GatewayModels.swift` schema contract is retained as generator-owned code rather than treated as app-local dead code. ## Fail-fast order diff --git a/scripts/lib/ci-changed-node-test-plan.mts b/scripts/lib/ci-changed-node-test-plan.mts index 22ab1b39008d..486cbc333d82 100644 --- a/scripts/lib/ci-changed-node-test-plan.mts +++ b/scripts/lib/ci-changed-node-test-plan.mts @@ -45,6 +45,21 @@ const CHANGED_NODE_TEST_TARGETS_PER_JOB = 12; // integration tests past the global timeout. const SERIAL_CHANGED_TARGET_RE = /^extensions\/memory-core\//u; const BOUNDARY_NODE_TEST_CONFIG = "test/vitest/vitest.boundary.config.ts"; +const DOCKER_SEED_LANE_ORDER = [ + "mcp-channels", + "cron-mcp-cleanup", + "mcp-code-mode-gateway", +] as const; +type DockerSeedLane = (typeof DOCKER_SEED_LANE_ORDER)[number]; +const DOCKER_SEED_LANES_BY_PATH: Readonly> = { + ".github/workflows/ci.yml": DOCKER_SEED_LANE_ORDER, + "scripts/e2e/cron-mcp-cleanup-seed.ts": ["cron-mcp-cleanup"], + "scripts/e2e/docker-openai-seed.ts": DOCKER_SEED_LANE_ORDER, + "scripts/e2e/lib/mcp-code-mode-probe-server.ts": ["mcp-code-mode-gateway"], + "scripts/e2e/mcp-channels-seed.ts": ["mcp-channels"], + "scripts/e2e/mcp-code-mode-gateway-seed.ts": ["mcp-code-mode-gateway"], + "scripts/lib/ci-changed-node-test-plan.mts": DOCKER_SEED_LANE_ORDER, +}; const publicPluginSdkEntrySources = Object.values( buildPluginSdkEntrySources(publicPluginSdkEntrypoints), ); @@ -61,6 +76,17 @@ const splitNodeTestConfigs = new Set( fullNodeTestShards.filter((shard) => shard.includePatterns).flatMap((shard) => shard.configs), ); +export function resolveChangedDockerSeedLanes(changedPaths: string[]) { + const selected = new Set(); + for (const changedPath of changedPaths) { + const normalizedPath = changedPath.replaceAll("\\", "/"); + for (const lane of DOCKER_SEED_LANES_BY_PATH[normalizedPath] ?? []) { + selected.add(lane); + } + } + return DOCKER_SEED_LANE_ORDER.filter((lane) => selected.has(lane)); +} + function isTestOnlyPath(changedPath: string) { return ( isTestFileTarget(changedPath) || diff --git a/scripts/test-projects.test-support.mts b/scripts/test-projects.test-support.mts index 8c56d7b4e3d0..0e119b4d5d80 100644 --- a/scripts/test-projects.test-support.mts +++ b/scripts/test-projects.test-support.mts @@ -2113,10 +2113,7 @@ const EXACT_TOOLING_TARGETS = new Map([ [".github/actions/setup-pnpm-store-cache/action.yml", [packageAcceptance, workflowGuards]], [".github/actions/setup-pnpm-store-cache/ensure-node.sh", ["setup-pnpm-store-cache-ensure-node"]], ["test/e2e/qa-lab/runtime/mcp-channels-docker-client.ts", [dockerE2e, pluginPrerelease]], - [ - "scripts/e2e/lib/mcp-code-mode-probe-server.ts", - ["docker-e2e-seeds", "mcp-code-mode-gateway-client"], - ], + ["scripts/e2e/lib/mcp-code-mode-probe-server.ts", ["mcp-code-mode-gateway-client"]], ["scripts/e2e/cron-cli-docker.sh", [dockerBuild, "docker-e2e-observability"]], ["scripts/ios-release-upload.sh", ["ios-release-wrapper-args", "ios-release-fastlane-gates"]], ["scripts/release-verify-beta.ts", ["release-wrapper-scripts"]], @@ -2782,10 +2779,6 @@ const SEMANTIC_TOOLING_TARGET_PATTERNS: Array<[RegExp, string[]]> = [ ), ["mcp-code-mode-gateway-client", "session-log-mentions"], ], - [ - /^(?:scripts\/e2e\/(?:mcp-channels|mcp-code-mode-gateway|cron-mcp-cleanup)-seed\.ts)$/u, - ["docker-e2e-seeds"], - ], [ /^scripts\/e2e\/(?:mcp-channels|cron-cli|cron-mcp-cleanup)-docker\.sh$/u, ["docker-e2e-observability"], @@ -2800,10 +2793,7 @@ const SEMANTIC_TOOLING_TARGET_PATTERNS: Array<[RegExp, string[]]> = [ "src/cron/active-jobs-manual-run.test.ts", ], ], - [ - /^scripts\/e2e\/cron-mcp-cleanup-docker\.sh$/u, - ["cron-mcp-cleanup-docker-client", "docker-e2e-seeds"], - ], + [/^scripts\/e2e\/cron-mcp-cleanup-docker\.sh$/u, ["cron-mcp-cleanup-docker-client"]], [ /^test\/e2e\/qa-lab\/runtime\/mcp-channels\.fixture\.ts$/u, ["test/e2e/qa-lab/runtime/mcp-gateway-transport.e2e.test.ts", "cron-mcp-cleanup-docker-client"], diff --git a/test/scripts/ci-changed-node-test-plan.test.ts b/test/scripts/ci-changed-node-test-plan.test.ts index 3b661e59f3e0..17a6e7ed9c63 100644 --- a/test/scripts/ci-changed-node-test-plan.test.ts +++ b/test/scripts/ci-changed-node-test-plan.test.ts @@ -11,6 +11,7 @@ import { hasPromptSnapshotAffectingChange, hasQaSmokeAffectingChange, hasSqliteSessionLifecycleAffectingChange, + resolveChangedDockerSeedLanes, } from "../../scripts/lib/ci-changed-node-test-plan.mts"; import { listExtensionTestFilesForRoots, @@ -54,6 +55,30 @@ function expectAllExtensionConfigs( expect(configs).toContain("test/vitest/vitest.extension-codex.config.ts"); } +const allDockerSeedLanes = ["mcp-channels", "cron-mcp-cleanup", "mcp-code-mode-gateway"]; +it.each([ + [["scripts/e2e/mcp-channels-seed.ts"], ["mcp-channels"]], + [["scripts/e2e/cron-mcp-cleanup-seed.ts"], ["cron-mcp-cleanup"]], + [["scripts/e2e/mcp-code-mode-gateway-seed.ts"], ["mcp-code-mode-gateway"]], + [["scripts/e2e/lib/mcp-code-mode-probe-server.ts"], ["mcp-code-mode-gateway"]], + [["scripts/e2e/docker-openai-seed.ts"], allDockerSeedLanes], + [ + [ + "scripts/e2e/mcp-code-mode-gateway-seed.ts", + "scripts/e2e/mcp-channels-seed.ts", + "scripts/e2e/lib/mcp-code-mode-probe-server.ts", + "scripts/e2e/cron-mcp-cleanup-seed.ts", + ], + allDockerSeedLanes, + ], + [[".github/workflows/ci.yml"], allDockerSeedLanes], + [["scripts/lib/ci-changed-node-test-plan.mts"], allDockerSeedLanes], + [["scripts\\e2e\\lib\\mcp-code-mode-probe-server.ts"], ["mcp-code-mode-gateway"]], + [["scripts/e2e/install-e2e.ts", "docs/ci.md"], []], +])("resolves Docker seed lanes for %j", (changedPaths, expected) => { + expect(resolveChangedDockerSeedLanes(changedPaths)).toEqual(expected); +}); + describe("CI changed Node test plan", () => { it("routes Control UI style changes through source-scanning policy tests", () => { const shards = createChangedNodeTestShards(["ui/src/styles/chat/layout.css"]); diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index 13279177a59a..a264b7ffe8d9 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -322,6 +322,7 @@ function runCiManifestFixture(options: { export const hasSqliteSessionLifecycleAffectingChange = (changedPaths) => changedPaths.includes("src/sqlite-session-owner.ts") || changedPaths.includes("test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts"); + export const resolveChangedDockerSeedLanes = (changedPaths) => changedPaths.includes("scripts/e2e/docker-openai-seed.ts") ? ["mcp-channels", "cron-mcp-cleanup"] : []; `, "utf8", ); @@ -372,6 +373,7 @@ function runCiManifestFixture(options: { ...((options.openClawKitTests ?? options.bundledPlanner) ? ["openclawkit-tests-contract-v1"] : []), + ...(options.bundledPlanner ? ["docker-seed-e2e-contract-v1"] : []), ].join("\n"), ); const outputPath = path.join(root, "manifest.out"); @@ -2740,6 +2742,39 @@ NODE expect(workflow.jobs.android.strategy["max-parallel"]).toBe(2); }); + it("runs changed Docker seed owners in one gated scheduler job", () => { + const source = readFileSync(".github/workflows/ci.yml", "utf8"); + const jobs = readCiWorkflow().jobs; + const job = jobs["docker-seed-e2e"]; + expect(source).toContain("docker-seed-e2e-contract-v1"); + expect(source).toContain( + 'typeof changedNodeTestPlan.resolveChangedDockerSeedLanes === "function"', + ); + expect(jobs.preflight.outputs).toMatchObject({ + docker_seed_lanes: "${{ steps.manifest.outputs.docker_seed_lanes }}", + run_docker_seed_e2e: "${{ steps.manifest.outputs.run_docker_seed_e2e }}", + }); + expect(job.if).toBe("needs.preflight.outputs.run_docker_seed_e2e == 'true'"); + expect(job.needs).toEqual(["preflight"]); + expect(job["timeout-minutes"]).toBe(60); + expect(job.permissions).toEqual({ contents: "read" }); + expect(job.strategy).toBeUndefined(); + expect(job.steps[0]).toEqual(jobs["pnpm-store-warmup"].steps[0]); + expect(job.steps[1].uses).toBe("./.github/actions/setup-node-env"); + const run = job.steps[2] as WorkflowStep; + const parallelism = run.env?.OPENCLAW_DOCKER_ALL_PARALLELISM; + expect(run).toMatchObject({ + run: "pnpm test:docker:all", + env: { + OPENCLAW_DOCKER_ALL_LANES: "${{ needs.preflight.outputs.docker_seed_lanes }}", + OPENCLAW_DOCKER_ALL_LIVE_MODE: "skip", + OPENCLAW_DOCKER_E2E_ALLOW_UNRELEASED_CHANGELOG: "1", + OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM: parallelism, + }, + }); + expect(parallelism).toContain("&& 3 || 1"); + }); + it("splits Windows tests two ways on every runner backend", () => { const workflow = readCiWorkflow(); const runStep = workflow.jobs["checks-windows"].steps.find( @@ -3063,6 +3098,7 @@ NODE "checks-ui-e2e": "ubuntu-24.04", "checks-ui-e2e-real-gateway": "ubuntu-24.04", "control-ui-i18n": "ubuntu-24.04", + "docker-seed-e2e": "ubuntu-24.04", "ios-build": "macos-26", "macos-node": "macos-15", "macos-swift": "macos-26", @@ -3084,6 +3120,7 @@ NODE // Same serial Chromium workload as checks-ui-e2e: hosted attempt 1 made it // the run's slowest job (205s mean vs a 150-190s plateau). "checks-ui-e2e-real-gateway": "blacksmith-16vcpu-ubuntu-2404", + "docker-seed-e2e": "blacksmith-16vcpu-ubuntu-2404", "qa-smoke-ci-profile": "blacksmith-16vcpu-ubuntu-2404", "sqlite-session-lifecycle": "blacksmith-8vcpu-ubuntu-2404", "macos-node": "blacksmith-6vcpu-macos-15", @@ -3093,6 +3130,10 @@ NODE "checks-ui": "blacksmith-8vcpu-ubuntu-2404", "checks-windows": "blacksmith-8vcpu-windows-2025", } as const; + const expectedHybridForkRunners = { + ...expectedHybridFirstAttemptRunners, + "docker-seed-e2e": "ubuntu-24.04", + } as const; const configurableJobs = Object.entries(jobs) .filter(([, job]) => String(job["runs-on"]).startsWith("${{")) .map(([jobName]) => jobName) @@ -3160,7 +3201,7 @@ NODE runnerBackend: "hybrid", }), `${jobName}: returning-contributor fork`, - ).toBe(expectedHybridFirstAttemptRunners[jobName as keyof typeof expectedHostedRunners]); + ).toBe(expectedHybridForkRunners[jobName as keyof typeof expectedHostedRunners]); } const widenedHybridMatrixRows = [ @@ -3524,6 +3565,7 @@ NODE "checks-ui-e2e", "checks-ui-e2e-real-gateway", "control-ui-i18n", + "docker-seed-e2e", "native-i18n", "qa-smoke-ci-profile", "sqlite-session-lifecycle", @@ -5904,6 +5946,8 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" expect(legacy.outputs.run_native_i18n).toBe("false"); expect(legacy.outputs.run_openclawkit_tests).toBe("false"); expect(legacy.outputs.run_qa_smoke_ci).toBe("false"); + expect(legacy.outputs.run_docker_seed_e2e).toBe("false"); + expect(legacy.outputs.docker_seed_lanes).toBe(""); expect(legacy.outputs.run_channel_contracts_shards).toBe("false"); expect(legacy.outputs.run_protocol_event_coverage).toBe("false"); expect( @@ -5935,6 +5979,8 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" expect(current.outputs.run_native_i18n).toBe("true"); expect(current.outputs.run_openclawkit_tests).toBe("true"); expect(current.outputs.run_qa_smoke_ci).toBe("true"); + expect(current.outputs.run_docker_seed_e2e).toBe("false"); + expect(current.outputs.docker_seed_lanes).toBe(""); expect(current.outputs.run_sqlite_session_lifecycle).toBe("true"); expect(current.outputs.run_channel_contracts_shards).toBe("true"); expect(current.outputs.run_protocol_event_coverage).toBe("true"); @@ -6018,9 +6064,10 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" ); } + const dockerSeedPath = "scripts/e2e/docker-openai-seed.ts"; const changedPullRequest = runCiManifestFixture({ bundledPlanner: true, - changedPaths: ["src/focused.ts", "extensions/codex/src/focused.ts"], + changedPaths: ["src/focused.ts", "extensions/codex/src/focused.ts", dockerSeedPath], eventName: "pull_request", }); expect(changedPullRequest.status, changedPullRequest.output).toBe(0); @@ -6050,6 +6097,8 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" ); expect(changedPullRequest.outputs.run_checks_node_core_dist).toBe("true"); expect(changedPullRequest.outputs.run_sqlite_session_lifecycle).toBe("false"); + expect(changedPullRequest.outputs.run_docker_seed_e2e).toBe("true"); + expect(changedPullRequest.outputs.docker_seed_lanes).toBe("mcp-channels cron-mcp-cleanup"); const mixedFallbackPullRequest = runCiManifestFixture({ bundledPlanner: true, @@ -7119,6 +7168,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" "macos-swift", "ios-build", "android", + "docker-seed-e2e", ]; expect(workflow.on.pull_request).not.toHaveProperty("paths-ignore"); diff --git a/test/scripts/docker-e2e-seeds.test.ts b/test/scripts/docker-e2e-seeds.test.ts deleted file mode 100644 index 2638f91af87e..000000000000 --- a/test/scripts/docker-e2e-seeds.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Docker E2E seed tests cover generated config and fixture-server contracts. -import { readFileSync } from "node:fs"; -import { describe, expect, it } from "vitest"; - -function readScript(pathname: string): string { - return readFileSync(pathname, "utf8"); -} - -describe("Docker E2E seed scripts", () => { - it("keeps the shared OpenAI seed helper aligned with packaged provider onboarding", () => { - const source = readScript("scripts/e2e/docker-openai-seed.ts"); - - expect(source).toContain("../../dist/plugin-sdk/provider-onboard.js"); - expect(source).toContain('const DOCKER_OPENAI_MODEL_REF = "openai/gpt-5.6-luna"'); - expect(source).toContain('api: "openai-responses"'); - expect(source).toContain('aliases: [{ modelRef: DOCKER_OPENAI_MODEL_REF, alias: "GPT" }]'); - expect(source).toContain("primaryModelRef: DOCKER_OPENAI_MODEL_REF"); - expect(source).toContain("openAiProvider.apiKey = apiKey"); - }); - - it("keeps MCP channels config wired to seeded transcript artifacts", () => { - const source = readScript("scripts/e2e/mcp-channels-seed.ts"); - - expect(source).toContain( - 'const sessionsDir = path.join(stateDir, "agents", "main", "sessions")', - ); - expect(source).toContain('const sessionFile = path.join(sessionsDir, "sess-main.jsonl")'); - expect(source).toContain('const storePath = path.join(sessionsDir, "sessions.json")'); - expect(source).toContain('channel: "imessage"'); - expect(source).toContain('accountId: "imessage-default"'); - expect(source).toContain('"hello from seeded transcript"'); - expect(source).toContain('content: "seeded image attachment"'); - expect(source).toContain("__openclaw: {"); - expect(source).toContain("media: ["); - expect(source).toContain('url: "media://inbound/seeded-image.png"'); - expect(source).toContain('contentType: "image/png"'); - }); - - it("keeps cron MCP cleanup config wired to its probe server artifacts", () => { - const source = readScript("scripts/e2e/cron-mcp-cleanup-seed.ts"); - - expect(source).toContain('process.title = "openclaw-cron-mcp-cleanup-probe"'); - expect(source).toContain('const probeDir = path.join(stateDir, "cron-mcp-cleanup")'); - expect(source).toContain('const serverPath = path.join(probeDir, "probe-server.mjs")'); - expect(source).toContain("await fs.rm(pidsPath, { force: true })"); - expect(source).toContain("cronCleanupProbe: {"); - expect(source).toContain('command: "node"'); - expect(source).toContain("args: [serverPath]"); - expect(source).toContain("cwd: probeDir"); - expect(source).toContain("subagents: {\n runTimeoutSeconds: 8,"); - }); - - it("keeps MCP code-mode gateway config wired to its fixture server artifacts", () => { - const seed = readScript("scripts/e2e/mcp-code-mode-gateway-seed.ts"); - const source = seed + readScript("scripts/e2e/lib/mcp-code-mode-probe-server.ts"); - - expect(source).toContain('const serverPath = path.join(stateDir, "mcp-code-mode-fixture"'); - expect(source).toContain('["alpha", "fixture-note-alpha"]'); - expect(source).toContain("responses: {\n enabled: true,"); - expect(source).toContain("codeMode: {\n enabled: true,"); - expect(source).toContain("fixture: {"); - expect(source).toContain('command: "node"'); - expect(source).toContain("args: [serverPath]"); - expect(source).toContain("cwd: path.dirname(serverPath)"); - expect(source).toContain("connectionTimeoutMs: 30_000"); - expect(seed).not.toContain("sync:"); - }); -}); diff --git a/test/scripts/test-projects.test.ts b/test/scripts/test-projects.test.ts index 66a3f8e9c0a6..6e897201a1ce 100644 --- a/test/scripts/test-projects.test.ts +++ b/test/scripts/test-projects.test.ts @@ -1258,18 +1258,14 @@ describe("scripts/test-projects changed-target routing", () => { "test/e2e/qa-lab/runtime/mcp-channels-docker-client.ts", "test/e2e/qa-lab/runtime/mcp-channels.fixture.ts", "test/e2e/qa-lab/runtime/mcp-client-temp-state.fixture.ts", - "scripts/e2e/mcp-channels-seed.ts", - "scripts/e2e/docker-openai-seed.ts", "scripts/e2e/mcp-code-mode-gateway-docker.sh", "scripts/e2e/mcp-code-mode-gateway-live-docker.sh", - "scripts/e2e/mcp-code-mode-gateway-seed.ts", "scripts/e2e/agent-bundle-mcp-tools-docker.sh", "test/e2e/qa-lab/runtime/agent-bundle-mcp-tools-docker-client.ts", "scripts/mcp-code-mode-gateway-e2e.ts", "scripts/e2e/cron-cli-docker.sh", "scripts/e2e/cron-mcp-cleanup-docker.sh", "scripts/e2e/cron-mcp-cleanup-docker-client.ts", - "scripts/e2e/cron-mcp-cleanup-seed.ts", ]; expect(findUnmatchedExplicitTestTargets(targets)).toEqual([]); @@ -1280,7 +1276,6 @@ describe("scripts/test-projects changed-target routing", () => { "test/scripts/plugin-prerelease-test-plan.test.ts", "test/e2e/qa-lab/runtime/mcp-gateway-transport.e2e.test.ts", "test/scripts/cron-mcp-cleanup-docker-client.test.ts", - "test/scripts/docker-e2e-seeds.test.ts", "test/scripts/mcp-code-mode-gateway-client.test.ts", "test/scripts/session-log-mentions.test.ts", "src/agents/agent-bundle-mcp-runtime.test.ts",