diff --git a/.agents/skills/openclaw-testing/SKILL.md b/.agents/skills/openclaw-testing/SKILL.md index 28f6b76f0bae..ca73227d8ef7 100644 --- a/.agents/skills/openclaw-testing/SKILL.md +++ b/.agents/skills/openclaw-testing/SKILL.md @@ -432,10 +432,10 @@ the `Verify preinstalled live media dependencies` step before assuming the media tests themselves slowed down. The release Docker path intentionally shards the plugin/runtime tail. The -workflow uses `plugins-runtime-plugins`, `plugins-runtime-services`, and -`plugins-runtime-install-a` through `plugins-runtime-install-d`; aggregate -aliases such as `plugins-runtime-core`, `plugins-runtime`, and -`plugins-integrations` remain for manual reruns. +workflow uses `plugins-runtime-plugins`, `plugins-runtime-services`, +`plugins-runtime-install-a` through `plugins-runtime-install-h`, and a +dedicated `openwebui` job; aggregate aliases such as `plugins-runtime-core`, +`plugins-runtime`, and `plugins-integrations` remain for manual reruns. The release QA parity box is internally split into candidate and baseline lane jobs, followed by a report job that downloads both artifacts and runs @@ -499,8 +499,9 @@ Release-path Docker chunks are currently `core`, `package-update-openai`, `plugins-runtime-plugins`, `plugins-runtime-services`, `plugins-runtime-install-a`, `plugins-runtime-install-b`, `plugins-runtime-install-c`, `plugins-runtime-install-d`, -`bundled-channels-core`, `bundled-channels-update-a`, -`bundled-channels-update-b`, and `bundled-channels-contracts`. The aggregate +`plugins-runtime-install-e`, `plugins-runtime-install-f`, +`plugins-runtime-install-g`, `plugins-runtime-install-h`, and the dedicated +`openwebui` job. The aggregate `bundled-channels`, `plugins-runtime-core`, `plugins-runtime`, and `plugins-integrations` chunks remain valid for manual one-shot reruns, but release checks use the split chunks. @@ -592,13 +593,18 @@ image. Release-path normal mode fans out into smaller Docker chunk jobs: - `plugins-runtime-install-b` - `plugins-runtime-install-c` - `plugins-runtime-install-d` -- `bundled-channels` +- `plugins-runtime-install-e` +- `plugins-runtime-install-f` +- `plugins-runtime-install-g` +- `plugins-runtime-install-h` +- `openwebui` -OpenWebUI is folded into `plugins-runtime-services` for full release-path -coverage and keeps a standalone `openwebui` chunk only for OpenWebUI-only -dispatches. The legacy `package-update`, `plugins-runtime-core`, +OpenWebUI runs as a standalone `openwebui` chunk on a dedicated large-disk +runner whenever stable or full release-path coverage requests it. The legacy +`package-update`, `plugins-runtime-core`, `plugins-runtime`, and `plugins-integrations` chunks still work as aggregate -aliases for manual reruns, but the release workflow uses the split chunks so +aliases for manual reruns and may still fold in OpenWebUI, but the release +workflow uses the split chunks so provider installer checks, plugin runtime checks, bundled plugin install/uninstall shards, and bundled-channel checks can run on separate machines. The bundled-channel runtime-dependency coverage @@ -608,8 +614,8 @@ than the serial `bundled-channel-deps` lane, so failures produce cheap targeted 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. +`bundled-plugin-install-uninstall-23`; selecting the legacy +`bundled-plugin-install-uninstall` lane expands to all 24 shards. ## Package Acceptance diff --git a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml index 9b91ba472969..fdb5270982b9 100644 --- a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml +++ b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml @@ -103,7 +103,7 @@ on: - stable - full use_github_hosted_runners: - description: Use GitHub-hosted runners instead of Blacksmith runners + description: Use GitHub-hosted runners where supported; OpenWebUI always uses a large-disk Blacksmith runner required: false default: false type: boolean @@ -214,7 +214,7 @@ on: default: stable type: string use_github_hosted_runners: - description: Use GitHub-hosted runners instead of Blacksmith runners + description: Use GitHub-hosted runners where supported; OpenWebUI always uses a large-disk Blacksmith runner required: false default: true type: boolean @@ -1236,10 +1236,10 @@ jobs: validate_docker_openwebui: needs: [validate_selected_ref, prepare_docker_e2e_image] - if: inputs.include_openwebui && !inputs.include_release_path_suites && inputs.docker_lanes == '' + if: inputs.include_openwebui && inputs.docker_lanes == '' && (inputs.release_test_profile == 'stable' || inputs.release_test_profile == 'full') name: Docker E2E (openwebui) continue-on-error: ${{ inputs.advisory }} - runs-on: ${{ inputs.use_github_hosted_runners && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }} + runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 60 env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} @@ -1250,6 +1250,7 @@ jobs: OPENCLAW_DOCKER_E2E_REPO_ROOT: ${{ github.workspace }} OPENCLAW_DOCKER_E2E_SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }} OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz + OPENCLAW_DOCKER_ALL_RELEASE_PROFILE: ${{ inputs.release_test_profile }} OPENCLAW_SKIP_DOCKER_BUILD: "1" steps: - name: Checkout selected ref @@ -1275,7 +1276,9 @@ jobs: uses: ./.github/actions/setup-node-env with: node-version: ${{ env.NODE_VERSION }} - install-bun: "true" + install-bun: "false" + install-deps: "false" + use-actions-cache: "false" - name: Validate Open WebUI credentials shell: bash diff --git a/docs/ci.md b/docs/ci.md index f20cc1571f32..65a059b3b332 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -483,11 +483,11 @@ The reusable live/E2E workflow asks `scripts/test-docker-all.mjs --plan-json` wh Release Docker coverage runs smaller chunked jobs with `OPENCLAW_SKIP_DOCKER_BUILD=1` so each chunk pulls only the image kind it needs and executes multiple lanes through the same weighted scheduler: - `OPENCLAW_DOCKER_ALL_PROFILE=release-path` -- `OPENCLAW_DOCKER_ALL_CHUNK=core | package-update-openai | package-update-anthropic | package-update-core | plugins-runtime-plugins | plugins-runtime-services | plugins-runtime-install-a..h` +- `OPENCLAW_DOCKER_ALL_CHUNK=core | package-update-openai | package-update-anthropic | package-update-core | plugins-runtime-plugins | plugins-runtime-services | plugins-runtime-install-a..h | openwebui` -Current release Docker chunks are `core`, `package-update-openai`, `package-update-anthropic`, `package-update-core`, `plugins-runtime-plugins`, `plugins-runtime-services`, and `plugins-runtime-install-a` through `plugins-runtime-install-h`. `package-update-openai` includes the live Codex plugin package lane, which installs the candidate OpenClaw package, installs the Codex plugin from `codex_plugin_spec` or a same-ref tarball with explicit Codex CLI install approval, runs Codex CLI preflight, then runs multiple same-session OpenClaw agent turns against OpenAI. `plugins-runtime-core`, `plugins-runtime`, and `plugins-integrations` remain aggregate plugin/runtime aliases. The `install-e2e` lane alias remains the aggregate manual rerun alias for both provider installer lanes. +Current release Docker chunks are `core`, `package-update-openai`, `package-update-anthropic`, `package-update-core`, `plugins-runtime-plugins`, `plugins-runtime-services`, `plugins-runtime-install-a` through `plugins-runtime-install-h`, and `openwebui`. `package-update-openai` includes the live Codex plugin package lane, which installs the candidate OpenClaw package, installs the Codex plugin from `codex_plugin_spec` or a same-ref tarball with explicit Codex CLI install approval, runs Codex CLI preflight, then runs multiple same-session OpenClaw agent turns against OpenAI. `plugins-runtime-core`, `plugins-runtime`, and `plugins-integrations` remain aggregate plugin/runtime aliases. The `install-e2e` lane alias remains the aggregate manual rerun alias for both provider installer lanes. -OpenWebUI is folded into `plugins-runtime-services` when full release-path coverage requests it, and keeps a standalone `openwebui` chunk only for OpenWebUI-only dispatches. Bundled-channel update lanes retry once for transient npm network failures. +OpenWebUI runs as a standalone `openwebui` chunk on a dedicated large-disk Blacksmith runner whenever stable or full release-path coverage requests it, even when the reusable workflow routes supported jobs to GitHub-hosted runners. Keeping the external image pull separate prevents the large image from competing with the shared package and plugin images in `plugins-runtime-services`; legacy aggregate plugin/runtime chunks still include OpenWebUI for compatible manual reruns. Bundled-channel update lanes retry once for transient npm network failures. Each chunk uploads `.artifacts/docker-tests/` with lane logs, timings, `summary.json`, `failures.json`, phase timings, scheduler plan JSON, slow-lane tables, and per-lane rerun commands. The workflow `docker_lanes` input runs selected lanes against the prepared images instead of the chunk jobs, which keeps failed-lane debugging bounded to one targeted Docker job and prepares, downloads, or reuses the package artifact for that run; if a selected lane is a live Docker lane, the targeted job builds the live-test image locally for that rerun. Generated per-lane GitHub rerun commands include `package_artifact_run_id`, `package_artifact_name`, and prepared image inputs when those values exist, so a failed lane can reuse the exact package and images from the failed run. diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index 199ee71f87ee..40982066d053 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -371,8 +371,8 @@ Release Docker coverage includes: - full install smoke with the slow Bun global install smoke enabled - root Dockerfile smoke image preparation/reuse by target SHA, with QR, root/gateway, and installer/Bun smoke jobs running as separate install-smoke shards - repository E2E lanes -- release-path Docker chunks: `core`, `package-update-openai`, `package-update-anthropic`, `package-update-core`, `plugins-runtime-plugins`, `plugins-runtime-services`, `plugins-runtime-install-a` through `plugins-runtime-install-h` -- OpenWebUI coverage inside the `plugins-runtime-services` chunk when requested +- release-path Docker chunks: `core`, `package-update-openai`, `package-update-anthropic`, `package-update-core`, `plugins-runtime-plugins`, `plugins-runtime-services`, `plugins-runtime-install-a` through `plugins-runtime-install-h`, and `openwebui` +- OpenWebUI coverage on a dedicated large-disk runner when requested - split bundled plugin install/uninstall lanes `bundled-plugin-install-uninstall-0` through `bundled-plugin-install-uninstall-23` - live/E2E provider suites and Docker live model coverage when release checks include live suites diff --git a/docs/reference/full-release-validation.md b/docs/reference/full-release-validation.md index 591a0e85a725..71384bb9acde 100644 --- a/docs/reference/full-release-validation.md +++ b/docs/reference/full-release-validation.md @@ -104,8 +104,9 @@ empty: | `package-update-anthropic` | Anthropic package install and update behavior. | | `package-update-core` | Provider-neutral package and update behavior. | | `plugins-runtime-plugins` | Plugin runtime lanes that exercise plugin behavior. | -| `plugins-runtime-services` | Service-backed and live plugin runtime lanes; includes OpenWebUI when requested. | +| `plugins-runtime-services` | Service-backed and live plugin runtime lanes. | | `plugins-runtime-install-a` through `plugins-runtime-install-h` | Plugin install/runtime batches split for parallel release validation. | +| `openwebui` | OpenWebUI compatibility smoke isolated on a dedicated large-disk runner when requested. | Use targeted `docker_lanes=` on the reusable live/E2E workflow when only one Docker lane failed. The release artifacts include per-lane rerun diff --git a/scripts/lib/docker-e2e-scenarios.mjs b/scripts/lib/docker-e2e-scenarios.mjs index 7b5b9bfe4905..c0cac0954e71 100644 --- a/scripts/lib/docker-e2e-scenarios.mjs +++ b/scripts/lib/docker-e2e-scenarios.mjs @@ -921,8 +921,7 @@ export function releasePathChunkLanes(chunk, options = {}) { return options.includeOpenWebUI ? [openWebUILane()] : []; } if ( - (chunk !== "plugins-runtime-services" && - chunk !== "plugins-runtime-core" && + (chunk !== "plugins-runtime-core" && chunk !== "plugins-runtime" && chunk !== "plugins-integrations") || !options.includeOpenWebUI @@ -935,7 +934,6 @@ export function releasePathChunkLanes(chunk, options = {}) { export function allReleasePathLanes(options = {}) { const releaseProfile = normalizeReleaseProfile(options.releaseProfile); return Object.keys(primaryReleasePathChunks) - .filter((chunk) => chunk !== "openwebui") .flatMap((chunk) => releasePathChunkLanes(chunk, { includeOpenWebUI: options.includeOpenWebUI, diff --git a/test/scripts/docker-e2e-plan.test.ts b/test/scripts/docker-e2e-plan.test.ts index 8d725fdead3e..41855a7eb645 100644 --- a/test/scripts/docker-e2e-plan.test.ts +++ b/test/scripts/docker-e2e-plan.test.ts @@ -181,7 +181,7 @@ describe("scripts/lib/docker-e2e-plan", () => { }); expect(withoutOpenWebUI.lanes.map((lane) => lane.name)).not.toContain("openwebui"); - expect(withOpenWebUI.lanes.map((lane) => lane.name)).toContain("openwebui"); + expect(withOpenWebUI.lanes.filter((lane) => lane.name === "openwebui")).toHaveLength(1); }); it("keeps beta release-path coverage to install, provider, and update proof lanes", () => { @@ -264,6 +264,11 @@ describe("scripts/lib/docker-e2e-plan", () => { profile: RELEASE_PATH_PROFILE, releaseChunk: "plugins-runtime-services", }); + const openWebUI = planFor({ + includeOpenWebUI: true, + profile: RELEASE_PATH_PROFILE, + releaseChunk: "openwebui", + }); const pluginsRuntimeInstallA = planFor({ includeOpenWebUI: true, profile: RELEASE_PATH_PROFILE, @@ -457,6 +462,8 @@ describe("scripts/lib/docker-e2e-plan", () => { timeoutMs: 1_200_000, weight: 3, }, + ]); + expect(openWebUI.lanes.map(summarizeLane)).toEqual([ { command: "OPENCLAW_OPENWEBUI_MODEL=openai/gpt-5.4-mini OPENCLAW_OPENWEBUI_PROVIDER_TIMEOUT_SECONDS=300 OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openwebui", @@ -592,6 +599,33 @@ describe("scripts/lib/docker-e2e-plan", () => { ]); }); + it("includes OpenWebUI exactly once in each legacy plugin aggregate", () => { + for (const releaseChunk of [ + "plugins-runtime-core", + "plugins-runtime", + "plugins-integrations", + ]) { + const withOpenWebUI = planFor({ + includeOpenWebUI: true, + profile: RELEASE_PATH_PROFILE, + releaseChunk, + }); + const withoutOpenWebUI = planFor({ + includeOpenWebUI: false, + profile: RELEASE_PATH_PROFILE, + releaseChunk, + }); + + expect( + withOpenWebUI.lanes.filter((lane) => lane.name === "openwebui"), + releaseChunk, + ).toHaveLength(1); + expect(withoutOpenWebUI.lanes.map((lane) => lane.name), releaseChunk).not.toContain( + "openwebui", + ); + } + }); + it("expands the published upgrade survivor lane across deduped baselines", () => { const plan = planFor({ selectedLaneNames: ["published-upgrade-survivor"], diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index ccdec247e825..9c56a5f49750 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -1745,6 +1745,24 @@ describe("package artifact reuse", () => { } }); + it("isolates Open WebUI release coverage on a lean large-disk runner", () => { + const job = workflowJob(LIVE_E2E_WORKFLOW, "validate_docker_openwebui"); + const setupNode = workflowStep(job, "Setup Node environment"); + + expect(job.if).toBe( + "inputs.include_openwebui && inputs.docker_lanes == '' && (inputs.release_test_profile == 'stable' || inputs.release_test_profile == 'full')", + ); + expect(job["runs-on"]).toBe("blacksmith-32vcpu-ubuntu-2404"); + expect(job.env?.OPENCLAW_DOCKER_ALL_RELEASE_PROFILE).toBe( + "${{ inputs.release_test_profile }}", + ); + expect(setupNode.with).toMatchObject({ + "install-bun": "false", + "install-deps": "false", + "use-actions-cache": "false", + }); + }); + it("names package acceptance Telegram as artifact-backed package validation", () => { const workflow = readFileSync(PACKAGE_ACCEPTANCE_WORKFLOW, "utf8");