mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
ci: use packaged tarball for docker e2e
This commit is contained in:
@@ -100,14 +100,18 @@ docker_lanes: install-e2e bundled-channel-update-acpx
|
||||
```
|
||||
|
||||
That skips the three chunk matrix and runs one targeted Docker job against the
|
||||
prepared GHCR images. Release-path normal mode remains max three Docker chunk
|
||||
jobs:
|
||||
prepared GHCR images and the prepared OpenClaw npm tarball. Live-only targeted
|
||||
reruns skip the E2E images and build only the live-test image. Release-path
|
||||
normal mode remains max three Docker chunk jobs:
|
||||
|
||||
- `core`
|
||||
- `package-update`
|
||||
- `plugins-integrations`
|
||||
|
||||
Every scheduler run writes `.artifacts/docker-tests/**/summary.json`. Read it
|
||||
Docker E2E images never copy repo sources as the app under test: the bare image
|
||||
is a Node/Git runner, and the functional image installs the same prebuilt npm
|
||||
tarball that bare lanes mount. Every scheduler run writes
|
||||
`.artifacts/docker-tests/**/summary.json`. Read it
|
||||
before rerunning. Lane entries include `command`, `rerunCommand`, status,
|
||||
timing, timeout state, image kind, and log file path. The summary also includes
|
||||
top-level phase timings for preflight, image build, package prep, lane pools,
|
||||
|
||||
@@ -438,6 +438,7 @@ jobs:
|
||||
OPENCLAW_DOCKER_E2E_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.image }}
|
||||
OPENCLAW_DOCKER_E2E_BARE_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.bare_image }}
|
||||
OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.functional_image }}
|
||||
OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz
|
||||
OPENCLAW_SKIP_DOCKER_BUILD: "1"
|
||||
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
|
||||
DOCKER_E2E_CHUNK: ${{ matrix.chunk_id }}
|
||||
@@ -465,6 +466,12 @@ jobs:
|
||||
- name: Hydrate live auth/profile inputs
|
||||
run: bash scripts/ci-hydrate-live-auth.sh
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: docker-e2e-package
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
- name: Pull shared Docker E2E image
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -623,6 +630,7 @@ jobs:
|
||||
OPENCLAW_DOCKER_E2E_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.image }}
|
||||
OPENCLAW_DOCKER_E2E_BARE_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.bare_image }}
|
||||
OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.functional_image }}
|
||||
OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz
|
||||
OPENCLAW_SKIP_DOCKER_BUILD: "1"
|
||||
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
|
||||
DOCKER_E2E_LANES: ${{ inputs.docker_lanes }}
|
||||
@@ -650,7 +658,31 @@ jobs:
|
||||
- name: Hydrate live auth/profile inputs
|
||||
run: bash scripts/ci-hydrate-live-auth.sh
|
||||
|
||||
- name: Detect targeted Docker lane image needs
|
||||
id: lane_class
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
needs_e2e=0
|
||||
IFS=', ' read -r -a lanes <<< "${DOCKER_E2E_LANES}"
|
||||
for lane in "${lanes[@]}"; do
|
||||
[[ -z "$lane" ]] && continue
|
||||
if [[ "$lane" != live-* ]]; then
|
||||
needs_e2e=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "needs_e2e=${needs_e2e}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: docker-e2e-package
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
- name: Pull shared Docker E2E images
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -691,10 +723,9 @@ jobs:
|
||||
export OPENCLAW_DOCKER_ALL_LOG_DIR=".artifacts/docker-tests/targeted"
|
||||
export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/targeted-timings.json"
|
||||
if [[ "$lanes" == *" live-"* ]]; then
|
||||
export OPENCLAW_DOCKER_ALL_BUILD=1
|
||||
else
|
||||
export OPENCLAW_DOCKER_ALL_BUILD=0
|
||||
pnpm test:docker:live-build
|
||||
fi
|
||||
export OPENCLAW_DOCKER_ALL_BUILD=0
|
||||
|
||||
pnpm test:docker:all
|
||||
|
||||
@@ -825,7 +856,60 @@ jobs:
|
||||
echo "Shared Docker E2E bare image: \`$bare_image\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Shared Docker E2E functional image: \`$functional_image\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Classify selected Docker lanes
|
||||
id: lane_class
|
||||
shell: bash
|
||||
env:
|
||||
DOCKER_E2E_LANES: ${{ inputs.docker_lanes }}
|
||||
INCLUDE_RELEASE_PATH_SUITES: ${{ inputs.include_release_path_suites }}
|
||||
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
needs_e2e=0
|
||||
if [[ "${INCLUDE_RELEASE_PATH_SUITES}" == "true" || "${INCLUDE_OPENWEBUI}" == "true" ]]; then
|
||||
needs_e2e=1
|
||||
elif [[ -n "${DOCKER_E2E_LANES}" ]]; then
|
||||
IFS=', ' read -r -a lanes <<< "${DOCKER_E2E_LANES}"
|
||||
for lane in "${lanes[@]}"; do
|
||||
[[ -z "$lane" ]] && continue
|
||||
if [[ "$lane" != live-* ]]; then
|
||||
needs_e2e=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo "needs_e2e=${needs_e2e}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup Node environment
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
install-bun: "true"
|
||||
|
||||
- name: Pack OpenClaw package for Docker E2E
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p .artifacts/docker-e2e-package
|
||||
pnpm build
|
||||
node --import tsx --input-type=module -e 'const { writePackageDistInventory } = await import("./src/infra/package-dist-inventory.ts"); await writePackageDistInventory(process.cwd());'
|
||||
npm pack --silent --ignore-scripts --pack-destination .artifacts/docker-e2e-package >/tmp/openclaw-docker-e2e-pack.out
|
||||
packed="$(tail -n 1 /tmp/openclaw-docker-e2e-pack.out | tr -d '\r')"
|
||||
mv ".artifacts/docker-e2e-package/$packed" .artifacts/docker-e2e-package/openclaw-current.tgz
|
||||
|
||||
- name: Upload OpenClaw Docker E2E package
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: docker-e2e-package
|
||||
path: .artifacts/docker-e2e-package/openclaw-current.tgz
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Log in to GHCR
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -833,15 +917,16 @@ jobs:
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Setup Docker builder
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
|
||||
|
||||
- name: Build and push bare Docker E2E image
|
||||
if: inputs.include_release_path_suites || inputs.docker_lanes != ''
|
||||
if: steps.lane_class.outputs.needs_e2e == '1' && (inputs.include_release_path_suites || inputs.docker_lanes != '')
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
context: .
|
||||
file: ./scripts/e2e/Dockerfile
|
||||
target: build
|
||||
target: bare
|
||||
platforms: linux/amd64
|
||||
cache-from: type=gha,scope=docker-e2e-bare
|
||||
cache-to: type=gha,mode=max,scope=docker-e2e-bare
|
||||
@@ -851,11 +936,14 @@ jobs:
|
||||
push: true
|
||||
|
||||
- name: Build and push functional Docker E2E image
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
context: .
|
||||
file: ./scripts/e2e/Dockerfile
|
||||
target: functional
|
||||
build-contexts: |
|
||||
openclaw_package=.artifacts/docker-e2e-package
|
||||
platforms: linux/amd64
|
||||
cache-from: |
|
||||
type=gha,scope=docker-e2e-bare
|
||||
|
||||
@@ -92,7 +92,7 @@ Scope logic lives in `scripts/ci-changed-scope.mjs` and is covered by unit tests
|
||||
CI workflow edits validate the Node CI graph plus workflow linting, but do not force Windows, Android, or macOS native builds by themselves; those platform lanes stay scoped to platform source changes.
|
||||
CI routing-only edits, selected cheap core-test fixture edits, and narrow plugin contract helper/test-routing edits use a fast Node-only manifest path: preflight, security, and a single `checks-fast-core` task. That path avoids build artifacts, Node 22 compatibility, channel contracts, full core shards, bundled-plugin shards, and additional guard matrices when the changed files are limited to the routing or helper surfaces that the fast task exercises directly.
|
||||
Windows Node checks are scoped to Windows-specific process/path wrappers, npm/pnpm/UI runner helpers, package manager config, and the CI workflow surfaces that execute that lane; unrelated source, plugin, install-smoke, and test-only changes stay on the Linux Node lanes so they do not reserve a 16-vCPU Windows worker for coverage that is already exercised by the normal test shards.
|
||||
The separate `install-smoke` workflow reuses the same scope script through its own `preflight` job. It splits smoke coverage into `run_fast_install_smoke` and `run_full_install_smoke`. Pull requests run the fast path for Docker/package surfaces, bundled plugin package/manifest changes, and core plugin/channel/gateway/Plugin SDK surfaces that the Docker smoke jobs exercise. Source-only bundled plugin changes, test-only edits, and docs-only edits do not reserve Docker workers. The fast path builds the root Dockerfile image once, checks the CLI, runs the agents delete shared-workspace CLI smoke, runs the container gateway-network e2e, verifies a bundled extension build arg, and runs the bounded bundled-plugin Docker profile under a 240-second aggregate command timeout with each scenario's Docker run capped separately. The full path keeps QR package install and installer Docker/update coverage for nightly scheduled runs, manual dispatches, workflow-call release checks, and pull requests that truly touch installer/package/Docker surfaces. `main` pushes, including merge commits, do not force the full path; when changed-scope logic would request full coverage on a push, the workflow keeps the fast Docker smoke and leaves the full install smoke to nightly or release validation. The slow Bun global install image-provider smoke is separately gated by `run_bun_global_install_smoke`; it runs on the nightly schedule and from the release checks workflow, and manual `install-smoke` dispatches can opt into it, but pull requests and `main` pushes do not run it. QR and installer Docker tests keep their own install-focused Dockerfiles. Local `test:docker:all` prebuilds one shared live-test image plus two shared `scripts/e2e/Dockerfile` built-app images: a bare image for installer/update/plugin-dependency lanes and a functional image that pre-stages bundled plugin runtime dependencies for normal functionality lanes. The scheduler selects the image per lane with `OPENCLAW_DOCKER_E2E_BARE_IMAGE` and `OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE`, then runs lanes with `OPENCLAW_SKIP_DOCKER_BUILD=1`; tune the default main-pool slot count of 10 with `OPENCLAW_DOCKER_ALL_PARALLELISM` and the provider-sensitive tail-pool slot count of 10 with `OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM`. Heavy lane caps default to `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=6`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=8`, and `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7` so npm install and multi-service lanes do not overcommit Docker while lighter lanes still fill available slots. Lane starts are staggered by 2 seconds by default to avoid local Docker daemon create storms; override with `OPENCLAW_DOCKER_ALL_START_STAGGER_MS=0` or another millisecond value. The local aggregate preflights Docker, removes stale OpenClaw E2E containers, emits active-lane status, persists lane timings for longest-first ordering, and supports `OPENCLAW_DOCKER_ALL_DRY_RUN=1` for scheduler inspection. It stops scheduling new pooled lanes after the first failure by default, and each lane has a 120-minute fallback timeout overrideable with `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS`; selected live/tail lanes use tighter per-lane caps. `OPENCLAW_DOCKER_ALL_LANES=<lane[,lane]>` runs exact scheduler lanes, including release-only lanes such as `install-e2e` and split bundled update lanes such as `bundled-channel-update-acpx`, while skipping the cleanup smoke so agents can reproduce one failed lane. The reusable live/E2E workflow builds and pushes one SHA-tagged bare GHCR Docker E2E image and one SHA-tagged functional GHCR Docker E2E image, then runs the release-path Docker suite as at most three chunked jobs with `OPENCLAW_SKIP_DOCKER_BUILD=1` so each chunk pulls 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|plugins-integrations`). Each chunk uploads `.artifacts/docker-tests/` with lane logs, timings, `summary.json`, phase timings, and per-lane rerun commands. The workflow `docker_lanes` input runs selected lanes against the prepared images instead of the three chunk jobs, which keeps failed-lane debugging bounded to one targeted Docker job; if a selected lane is a live Docker lane, the targeted job builds the live-test image locally for that rerun. When Open WebUI is requested with the release-path suite, it runs inside the plugins/integrations chunk instead of reserving a fourth Docker worker; Open WebUI keeps a standalone job only for openwebui-only dispatches. The scheduled live/E2E workflow runs the full release-path Docker suite daily. The bundled update matrix is split by update target so repeated npm update and doctor repair passes can shard with other bundled checks.
|
||||
The separate `install-smoke` workflow reuses the same scope script through its own `preflight` job. It splits smoke coverage into `run_fast_install_smoke` and `run_full_install_smoke`. Pull requests run the fast path for Docker/package surfaces, bundled plugin package/manifest changes, and core plugin/channel/gateway/Plugin SDK surfaces that the Docker smoke jobs exercise. Source-only bundled plugin changes, test-only edits, and docs-only edits do not reserve Docker workers. The fast path builds the root Dockerfile image once, checks the CLI, runs the agents delete shared-workspace CLI smoke, runs the container gateway-network e2e, verifies a bundled extension build arg, and runs the bounded bundled-plugin Docker profile under a 240-second aggregate command timeout with each scenario's Docker run capped separately. The full path keeps QR package install and installer Docker/update coverage for nightly scheduled runs, manual dispatches, workflow-call release checks, and pull requests that truly touch installer/package/Docker surfaces. `main` pushes, including merge commits, do not force the full path; when changed-scope logic would request full coverage on a push, the workflow keeps the fast Docker smoke and leaves the full install smoke to nightly or release validation. The slow Bun global install image-provider smoke is separately gated by `run_bun_global_install_smoke`; it runs on the nightly schedule and from the release checks workflow, and manual `install-smoke` dispatches can opt into it, but pull requests and `main` pushes do not run it. QR and installer Docker tests keep their own install-focused Dockerfiles. Local `test:docker:all` prebuilds one shared live-test image, packs OpenClaw once as an npm tarball, and builds two shared `scripts/e2e/Dockerfile` images: a bare Node/Git runner for installer/update/plugin-dependency lanes and a functional image that installs the same tarball into `/app` for normal functionality lanes. The scheduler selects the image per lane with `OPENCLAW_DOCKER_E2E_BARE_IMAGE` and `OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE`, then runs lanes with `OPENCLAW_SKIP_DOCKER_BUILD=1`; tune the default main-pool slot count of 10 with `OPENCLAW_DOCKER_ALL_PARALLELISM` and the provider-sensitive tail-pool slot count of 10 with `OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM`. Heavy lane caps default to `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=6`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=8`, and `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7` so npm install and multi-service lanes do not overcommit Docker while lighter lanes still fill available slots. Lane starts are staggered by 2 seconds by default to avoid local Docker daemon create storms; override with `OPENCLAW_DOCKER_ALL_START_STAGGER_MS=0` or another millisecond value. The local aggregate preflights Docker, removes stale OpenClaw E2E containers, emits active-lane status, persists lane timings for longest-first ordering, and supports `OPENCLAW_DOCKER_ALL_DRY_RUN=1` for scheduler inspection. It stops scheduling new pooled lanes after the first failure by default, and each lane has a 120-minute fallback timeout overrideable with `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS`; selected live/tail lanes use tighter per-lane caps. `OPENCLAW_DOCKER_ALL_LANES=<lane[,lane]>` runs exact scheduler lanes, including release-only lanes such as `install-e2e` and split bundled update lanes such as `bundled-channel-update-acpx`, while skipping the cleanup smoke so agents can reproduce one failed lane. The reusable live/E2E workflow builds and pushes one SHA-tagged bare GHCR Docker E2E image and one SHA-tagged functional GHCR Docker E2E image, then runs the release-path Docker suite as at most three chunked jobs with `OPENCLAW_SKIP_DOCKER_BUILD=1` so each chunk pulls 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|plugins-integrations`). Each chunk uploads `.artifacts/docker-tests/` with lane logs, timings, `summary.json`, phase timings, and per-lane rerun commands. The workflow `docker_lanes` input runs selected lanes against the prepared images instead of the three chunk jobs, which keeps failed-lane debugging bounded to one targeted Docker job; if a selected lane is a live Docker lane, the targeted job builds the live-test image locally for that rerun. When Open WebUI is requested with the release-path suite, it runs inside the plugins/integrations chunk instead of reserving a fourth Docker worker; Open WebUI keeps a standalone job only for openwebui-only dispatches. The scheduled live/E2E workflow runs the full release-path Docker suite daily. The bundled update matrix is split by update target so repeated npm update and doctor repair passes can shard with other bundled checks.
|
||||
|
||||
Local changed-lane logic lives in `scripts/changed-lanes.mjs` and is executed by `scripts/check-changed.mjs`. That local gate is stricter about architecture boundaries than the broad CI platform scope: core production changes run core prod typecheck plus core tests, core test-only changes run only core test typecheck/tests, extension production changes run extension prod typecheck plus extension tests, and extension test-only changes run only extension test typecheck/tests. Public Plugin SDK or plugin-contract changes expand to extension validation because extensions depend on those core contracts. Release metadata-only version bumps run targeted version/config/root-dependency checks. Unknown root/config changes fail safe to all lanes.
|
||||
|
||||
|
||||
@@ -606,7 +606,7 @@ These Docker runners split into two buckets:
|
||||
`OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=45000`, and
|
||||
`OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`. Override those env vars when you
|
||||
explicitly want the larger exhaustive scan.
|
||||
- `test:docker:all` builds the live Docker image once via `test:docker:live-build`, then reuses it for the live Docker lanes. It also builds one shared `scripts/e2e/Dockerfile` image via `test:docker:e2e-build` and reuses it for the E2E container smoke runners that exercise the built app. The aggregate uses a weighted local scheduler: `OPENCLAW_DOCKER_ALL_PARALLELISM` controls process slots, while resource caps keep heavy live, npm-install, and multi-service lanes from all starting at once. Defaults are 10 slots, `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=6`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=8`, and `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`; tune `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` or `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT` only when the Docker host has more headroom. The runner performs a Docker preflight by default, removes stale OpenClaw E2E containers, prints status every 30 seconds, stores successful lane timings in `.artifacts/docker-tests/lane-timings.json`, and uses those timings to start longer lanes first on later runs. Use `OPENCLAW_DOCKER_ALL_DRY_RUN=1` to print the weighted lane manifest without building or running Docker.
|
||||
- `test:docker:all` builds the live Docker image once via `test:docker:live-build`, packs OpenClaw once as an npm tarball, then builds/reuses two `scripts/e2e/Dockerfile` images. The bare image is only the Node/Git runner for install/update/plugin-dependency lanes; those lanes mount the prebuilt tarball. The functional image installs the same tarball into `/app` for built-app functionality lanes. The aggregate uses a weighted local scheduler: `OPENCLAW_DOCKER_ALL_PARALLELISM` controls process slots, while resource caps keep heavy live, npm-install, and multi-service lanes from all starting at once. Defaults are 10 slots, `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=6`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=8`, and `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`; tune `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` or `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT` only when the Docker host has more headroom. The runner performs a Docker preflight by default, removes stale OpenClaw E2E containers, prints status every 30 seconds, stores successful lane timings in `.artifacts/docker-tests/lane-timings.json`, and uses those timings to start longer lanes first on later runs. Use `OPENCLAW_DOCKER_ALL_DRY_RUN=1` to print the weighted lane manifest without building or running Docker.
|
||||
- Container smoke runners: `test:docker:openwebui`, `test:docker:onboard`, `test:docker:npm-onboard-channel-agent`, `test:docker:update-channel-switch`, `test:docker:session-runtime-context`, `test:docker:agents-delete-shared-workspace`, `test:docker:gateway-network`, `test:docker:browser-cdp-snapshot`, `test:docker:mcp-channels`, `test:docker:pi-bundle-mcp-tools`, `test:docker:cron-mcp-cleanup`, `test:docker:plugins`, `test:docker:plugin-update`, and `test:docker:config-reload` boot one or more real containers and verify higher-level integration paths.
|
||||
|
||||
The live-model Docker runners also bind-mount only the needed CLI auth homes (or all supported ones when the run is not narrowed), then copy them into the container home before the run so external-CLI OAuth can refresh tokens without mutating the host auth store:
|
||||
@@ -639,11 +639,11 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or
|
||||
- Narrow bundled plugin runtime deps while iterating by disabling unrelated scenarios, for example:
|
||||
`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 pnpm test:docker:bundled-channel-deps`.
|
||||
|
||||
To prebuild and reuse the shared built-app image manually:
|
||||
To prebuild and reuse the shared functional image manually:
|
||||
|
||||
```bash
|
||||
OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e:local pnpm test:docker:e2e-build
|
||||
OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e:local OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels
|
||||
OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e-functional:local pnpm test:docker:e2e-build
|
||||
OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e-functional:local OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels
|
||||
```
|
||||
|
||||
Suite-specific image overrides such as `OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE` still win when set. When `OPENCLAW_SKIP_DOCKER_BUILD=1` points at a remote shared image, the scripts pull it if it is not already local. The QR and installer Docker tests keep their own Dockerfiles because they validate package/install behavior rather than the shared built-app runtime.
|
||||
|
||||
@@ -33,7 +33,7 @@ title: "Tests"
|
||||
- Gateway integration: opt-in via `OPENCLAW_TEST_INCLUDE_GATEWAY=1 pnpm test` or `pnpm test:gateway`.
|
||||
- `pnpm test:e2e`: Runs gateway end-to-end smoke tests (multi-instance WS/HTTP/node pairing). Defaults to `threads` + `isolate: false` with adaptive workers in `vitest.e2e.config.ts`; tune with `OPENCLAW_E2E_WORKERS=<n>` and set `OPENCLAW_E2E_VERBOSE=1` for verbose logs.
|
||||
- `pnpm test:live`: Runs provider live tests (minimax/zai). Requires API keys and `LIVE=1` (or provider-specific `*_LIVE_TEST=1`) to unskip.
|
||||
- `pnpm test:docker:all`: Builds the shared live-test image plus two Docker E2E images once, then runs the Docker smoke lanes with `OPENCLAW_SKIP_DOCKER_BUILD=1` through a weighted scheduler. The bare image (`OPENCLAW_DOCKER_E2E_BARE_IMAGE`) is used for installer/update/plugin-dependency lanes; the functional image (`OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE`) pre-stages bundled plugin runtime dependencies for normal functionality lanes. `OPENCLAW_DOCKER_ALL_PARALLELISM=<n>` controls process slots and defaults to 10; `OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM=<n>` controls the provider-sensitive tail pool and defaults to 10. Heavy lane caps default to `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=10`, and `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`; provider caps default to one heavy lane per provider via `OPENCLAW_DOCKER_ALL_LIVE_CLAUDE_LIMIT=4`, `OPENCLAW_DOCKER_ALL_LIVE_CODEX_LIMIT=4`, and `OPENCLAW_DOCKER_ALL_LIVE_GEMINI_LIMIT=4`. Use `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` or `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT` for larger hosts. Lane starts are staggered by 2 seconds by default to avoid local Docker daemon create storms; override with `OPENCLAW_DOCKER_ALL_START_STAGGER_MS=<ms>`. The runner preflights Docker by default, cleans stale OpenClaw E2E containers, emits active-lane status every 30 seconds, shares provider CLI tool caches between compatible lanes, retries transient live-provider failures once by default (`OPENCLAW_DOCKER_ALL_LIVE_RETRIES=<n>`), and stores lane timings in `.artifacts/docker-tests/lane-timings.json` for longest-first ordering on later runs. Use `OPENCLAW_DOCKER_ALL_DRY_RUN=1` to print the lane manifest without running Docker, `OPENCLAW_DOCKER_ALL_STATUS_INTERVAL_MS=<ms>` to tune status output, or `OPENCLAW_DOCKER_ALL_TIMINGS=0` to disable timing reuse. Use `OPENCLAW_DOCKER_ALL_LIVE_MODE=skip` for deterministic/local lanes only or `OPENCLAW_DOCKER_ALL_LIVE_MODE=only` for live-provider lanes only; package aliases are `pnpm test:docker:local:all` and `pnpm test:docker:live:all`. Live-only mode merges main and tail live lanes into one longest-first pool so provider buckets can pack Claude, Codex, and Gemini work together. The runner stops scheduling new pooled lanes after the first failure unless `OPENCLAW_DOCKER_ALL_FAIL_FAST=0` is set, and each lane has a 120-minute fallback timeout overrideable with `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS`; selected live/tail lanes use tighter per-lane caps. CLI backend Docker setup commands have their own timeout via `OPENCLAW_LIVE_CLI_BACKEND_SETUP_TIMEOUT_SECONDS` (default 180). Per-lane logs and `summary.json` phase timings are written under `.artifacts/docker-tests/<run-id>/`.
|
||||
- `pnpm test:docker:all`: Builds the shared live-test image, packs OpenClaw once as an npm tarball, builds/reuses a bare Node/Git runner image plus a functional image that installs that tarball into `/app`, then runs Docker smoke lanes with `OPENCLAW_SKIP_DOCKER_BUILD=1` through a weighted scheduler. The bare image (`OPENCLAW_DOCKER_E2E_BARE_IMAGE`) is used for installer/update/plugin-dependency lanes; those lanes mount the prebuilt tarball instead of using copied repo sources. The functional image (`OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE`) is used for normal built-app functionality lanes. `OPENCLAW_DOCKER_ALL_PARALLELISM=<n>` controls process slots and defaults to 10; `OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM=<n>` controls the provider-sensitive tail pool and defaults to 10. Heavy lane caps default to `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=10`, and `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`; provider caps default to one heavy lane per provider via `OPENCLAW_DOCKER_ALL_LIVE_CLAUDE_LIMIT=4`, `OPENCLAW_DOCKER_ALL_LIVE_CODEX_LIMIT=4`, and `OPENCLAW_DOCKER_ALL_LIVE_GEMINI_LIMIT=4`. Use `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` or `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT` for larger hosts. Lane starts are staggered by 2 seconds by default to avoid local Docker daemon create storms; override with `OPENCLAW_DOCKER_ALL_START_STAGGER_MS=<ms>`. The runner preflights Docker by default, cleans stale OpenClaw E2E containers, emits active-lane status every 30 seconds, shares provider CLI tool caches between compatible lanes, retries transient live-provider failures once by default (`OPENCLAW_DOCKER_ALL_LIVE_RETRIES=<n>`), and stores lane timings in `.artifacts/docker-tests/lane-timings.json` for longest-first ordering on later runs. Use `OPENCLAW_DOCKER_ALL_DRY_RUN=1` to print the lane manifest without running Docker, `OPENCLAW_DOCKER_ALL_STATUS_INTERVAL_MS=<ms>` to tune status output, or `OPENCLAW_DOCKER_ALL_TIMINGS=0` to disable timing reuse. Use `OPENCLAW_DOCKER_ALL_LIVE_MODE=skip` for deterministic/local lanes only or `OPENCLAW_DOCKER_ALL_LIVE_MODE=only` for live-provider lanes only; package aliases are `pnpm test:docker:local:all` and `pnpm test:docker:live:all`. Live-only mode merges main and tail live lanes into one longest-first pool so provider buckets can pack Claude, Codex, and Gemini work together. The runner stops scheduling new pooled lanes after the first failure unless `OPENCLAW_DOCKER_ALL_FAIL_FAST=0` is set, and each lane has a 120-minute fallback timeout overrideable with `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS`; selected live/tail lanes use tighter per-lane caps. CLI backend Docker setup commands have their own timeout via `OPENCLAW_LIVE_CLI_BACKEND_SETUP_TIMEOUT_SECONDS` (default 180). Per-lane logs and `summary.json` phase timings are written under `.artifacts/docker-tests/<run-id>/`.
|
||||
- `pnpm test:docker:browser-cdp-snapshot`: Builds a Chromium-backed source E2E container, starts raw CDP plus an isolated Gateway, runs `browser doctor --deep`, and verifies CDP role snapshots include link URLs, cursor-promoted clickables, iframe refs, and frame metadata.
|
||||
- CLI backend live Docker probes can be run as focused lanes, for example `pnpm test:docker:live-cli-backend:codex`, `pnpm test:docker:live-cli-backend:codex:resume`, or `pnpm test:docker:live-cli-backend:codex:mcp`. Claude and Gemini have matching `:resume` and `:mcp` aliases.
|
||||
- `pnpm test:docker:openwebui`: Starts Dockerized OpenClaw + Open WebUI, signs in through Open WebUI, checks `/api/models`, then runs a real proxied chat through `/api/chat/completions`. Requires a usable live model key (for example OpenAI in `~/.profile`), pulls an external Open WebUI image, and is not expected to be CI-stable like the normal unit/e2e suites.
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
#
|
||||
# Shared Docker E2E image.
|
||||
# `bare` is a clean Node/Git runner for install/update lanes. `functional`
|
||||
# installs the prepared OpenClaw npm tarball into /app for built-app lanes.
|
||||
|
||||
FROM node:24-bookworm-slim@sha256:e8e2e91b1378f83c5b2dd15f0247f34110e2fe895f6ca7719dbb780f929368eb AS e2e-runner
|
||||
|
||||
@@ -7,12 +11,14 @@ RUN apt-get update \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN corepack enable
|
||||
RUN npm install -g tsx@4.21.0 --no-fund --no-audit
|
||||
|
||||
RUN useradd --create-home --shell /bin/bash appuser \
|
||||
&& mkdir -p /app \
|
||||
&& chown appuser:appuser /app
|
||||
|
||||
ENV HOME="/home/appuser"
|
||||
ENV PATH="/home/appuser/.local/bin:${PATH}"
|
||||
ENV NODE_OPTIONS="--disable-warning=ExperimentalWarning"
|
||||
# Docker E2E lanes start many loopback gateways concurrently; mDNS advertising
|
||||
# is unrelated to those checks and can flap under container CPU/network load.
|
||||
@@ -21,48 +27,23 @@ ENV OPENCLAW_DISABLE_BONJOUR="1"
|
||||
USER appuser
|
||||
WORKDIR /app
|
||||
|
||||
FROM e2e-runner AS deps
|
||||
|
||||
COPY --chown=appuser:appuser package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
|
||||
COPY --chown=appuser:appuser ui/package.json ./ui/package.json
|
||||
COPY --chown=appuser:appuser patches ./patches
|
||||
COPY --chown=appuser:appuser scripts/postinstall-bundled-plugins.mjs scripts/preinstall-package-manager-warning.mjs scripts/npm-runner.mjs scripts/windows-cmd-helpers.mjs ./scripts/
|
||||
RUN --mount=type=bind,source=extensions,target=/tmp/extensions,readonly \
|
||||
find /tmp/extensions -mindepth 2 -maxdepth 2 -name package.json -print | \
|
||||
while IFS= read -r manifest; do \
|
||||
dest="${manifest#/tmp/}"; \
|
||||
mkdir -p "$(dirname "$dest")"; \
|
||||
cp "$manifest" "$dest"; \
|
||||
done
|
||||
|
||||
RUN --mount=type=cache,id=openclaw-pnpm-store,target=/home/appuser/.local/share/pnpm/store,sharing=locked \
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
FROM deps AS build
|
||||
|
||||
COPY --chown=appuser:appuser .oxlintrc.json tsconfig.json tsconfig.plugin-sdk.dts.json tsconfig.oxlint*.json tsdown.config.ts vitest.config.ts openclaw.mjs ./
|
||||
COPY --chown=appuser:appuser src ./src
|
||||
COPY --chown=appuser:appuser test ./test
|
||||
COPY --chown=appuser:appuser scripts ./scripts
|
||||
COPY --chown=appuser:appuser docs ./docs
|
||||
COPY --chown=appuser:appuser packages ./packages
|
||||
COPY --chown=appuser:appuser skills ./skills
|
||||
COPY --chown=appuser:appuser ui ./ui
|
||||
COPY --chown=appuser:appuser extensions ./extensions
|
||||
COPY --chown=appuser:appuser vendor/a2ui/renderers/lit ./vendor/a2ui/renderers/lit
|
||||
COPY --chown=appuser:appuser apps/shared/OpenClawKit/Sources/OpenClawKit/Resources ./apps/shared/OpenClawKit/Sources/OpenClawKit/Resources
|
||||
COPY --chown=appuser:appuser apps/shared/OpenClawKit/Tools/CanvasA2UI ./apps/shared/OpenClawKit/Tools/CanvasA2UI
|
||||
|
||||
RUN pnpm build
|
||||
# Onboard Docker E2E does not exercise the Control UI itself; it only needs the
|
||||
# asset-existence check to pass so configure/onboard can continue.
|
||||
RUN mkdir -p dist/control-ui \
|
||||
&& printf '%s\n' '<!doctype html><title>OpenClaw Control UI</title>' > dist/control-ui/index.html
|
||||
FROM e2e-runner AS bare
|
||||
|
||||
CMD ["bash"]
|
||||
|
||||
FROM build AS functional
|
||||
|
||||
RUN node scripts/stage-bundled-plugin-runtime-deps.mjs
|
||||
FROM bare AS build
|
||||
|
||||
CMD ["bash"]
|
||||
|
||||
FROM bare AS functional
|
||||
|
||||
# The app under test enters through the named BuildKit context, not by copying
|
||||
# checkout sources into the image.
|
||||
COPY --from=openclaw_package --chown=appuser:appuser openclaw-current.tgz /tmp/openclaw-current.tgz
|
||||
RUN npm install -g --prefix /tmp/openclaw-prefix /tmp/openclaw-current.tgz --no-fund --no-audit \
|
||||
&& cp -a /tmp/openclaw-prefix/lib/node_modules/openclaw/. /app/ \
|
||||
&& mkdir -p "$HOME/.local/bin" \
|
||||
&& ln -sf /app/openclaw.mjs "$HOME/.local/bin/openclaw" \
|
||||
&& rm -rf /tmp/openclaw-prefix /tmp/openclaw-current.tgz
|
||||
|
||||
CMD ["bash"]
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs bundled plugin runtime-dependency Docker scenarios from a mounted OpenClaw
|
||||
# npm tarball. The default image is a clean runner; each scenario installs the
|
||||
# tarball so package install behavior is what gets tested.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh"
|
||||
|
||||
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-bundled-channel-deps-e2e" OPENCLAW_BUNDLED_CHANNEL_DEPS_E2E_IMAGE)"
|
||||
UPDATE_BASELINE_VERSION="${OPENCLAW_BUNDLED_CHANNEL_UPDATE_BASELINE_VERSION:-2026.4.20}"
|
||||
DOCKER_TARGET="${OPENCLAW_BUNDLED_CHANNEL_DOCKER_TARGET:-e2e-runner}"
|
||||
DOCKER_TARGET="${OPENCLAW_BUNDLED_CHANNEL_DOCKER_TARGET:-bare}"
|
||||
HOST_BUILD="${OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD:-1}"
|
||||
PACKAGE_TGZ="${OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ:-}"
|
||||
RUN_CHANNEL_SCENARIOS="${OPENCLAW_BUNDLED_CHANNEL_SCENARIOS:-1}"
|
||||
@@ -22,32 +26,14 @@ docker_e2e_build_or_reuse "$IMAGE_NAME" bundled-channel-deps "$ROOT_DIR/scripts/
|
||||
|
||||
prepare_package_tgz() {
|
||||
if [ -n "$PACKAGE_TGZ" ]; then
|
||||
if [ ! -f "$PACKAGE_TGZ" ]; then
|
||||
echo "OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ does not exist: $PACKAGE_TGZ" >&2
|
||||
exit 1
|
||||
fi
|
||||
PACKAGE_TGZ="$(cd "$(dirname "$PACKAGE_TGZ")" && pwd)/$(basename "$PACKAGE_TGZ")"
|
||||
PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz bundled-channel-deps "$PACKAGE_TGZ")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$HOST_BUILD" != "0" ]; then
|
||||
echo "Building host package artifacts..."
|
||||
run_logged bundled-channel-deps-host-build pnpm build
|
||||
else
|
||||
echo "Skipping host build (OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0)"
|
||||
fi
|
||||
|
||||
echo "Writing package inventory and packing once..."
|
||||
run_logged bundled-channel-deps-inventory node --import tsx --input-type=module -e 'const { writePackageDistInventory } = await import("./src/infra/package-dist-inventory.ts"); await writePackageDistInventory(process.cwd());'
|
||||
local pack_dir
|
||||
pack_dir="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-bundled-channel-pack.XXXXXX")"
|
||||
run_logged bundled-channel-deps-pack npm pack --ignore-scripts --pack-destination "$pack_dir"
|
||||
PACKAGE_TGZ="$(find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)"
|
||||
if [ -z "$PACKAGE_TGZ" ]; then
|
||||
echo "missing packed OpenClaw tarball" >&2
|
||||
if [ "$HOST_BUILD" = "0" ] && [ -z "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}" ]; then
|
||||
echo "OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0 requires OPENCLAW_CURRENT_PACKAGE_TGZ or OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ" >&2
|
||||
exit 1
|
||||
fi
|
||||
PACKAGE_TGZ="$(cd "$(dirname "$PACKAGE_TGZ")" && pwd)/$(basename "$PACKAGE_TGZ")"
|
||||
PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz bundled-channel-deps)"
|
||||
}
|
||||
|
||||
prepare_package_tgz
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
// Crestodian first-run Docker harness.
|
||||
// Imports packaged dist modules so the Docker lane verifies the npm tarball,
|
||||
// while this small test driver stays mounted from the checkout.
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { runCli, shouldStartCrestodianForBareRoot } from "../../src/cli/run-main.js";
|
||||
import { clearConfigCache } from "../../src/config/config.js";
|
||||
import type { OpenClawConfig } from "../../src/config/types.openclaw.js";
|
||||
import { runCrestodian } from "../../src/crestodian/crestodian.js";
|
||||
import type { RuntimeEnv } from "../../src/runtime.js";
|
||||
import { runCli, shouldStartCrestodianForBareRoot } from "../../dist/cli/run-main.js";
|
||||
import { clearConfigCache } from "../../dist/config/config.js";
|
||||
import type { OpenClawConfig } from "../../dist/config/types.openclaw.js";
|
||||
import { runCrestodian } from "../../dist/crestodian/crestodian.js";
|
||||
import type { RuntimeEnv } from "../../dist/runtime.js";
|
||||
|
||||
type CrestodianFirstRunCommand = {
|
||||
id: string;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs the Crestodian first-run Docker smoke against the package-installed
|
||||
# functional E2E image, with only the test harness mounted from the checkout.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -16,11 +18,13 @@ trap cleanup EXIT
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-first-run
|
||||
|
||||
echo "Running in-container Crestodian first-run smoke..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
set +e
|
||||
docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \
|
||||
-e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc "set -euo pipefail
|
||||
node --import tsx scripts/e2e/crestodian-first-run-docker-client.ts
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// Crestodian planner Docker harness.
|
||||
// Imports packaged dist modules so the Docker lane verifies the npm tarball,
|
||||
// while this small test driver stays mounted from the checkout.
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { clearConfigCache } from "../../src/config/config.js";
|
||||
import type { OpenClawConfig } from "../../src/config/types.openclaw.js";
|
||||
import { runCrestodian } from "../../src/crestodian/crestodian.js";
|
||||
import type { RuntimeEnv } from "../../src/runtime.js";
|
||||
import { clearConfigCache } from "../../dist/config/config.js";
|
||||
import type { OpenClawConfig } from "../../dist/config/types.openclaw.js";
|
||||
import { runCrestodian } from "../../dist/crestodian/crestodian.js";
|
||||
import type { RuntimeEnv } from "../../dist/runtime.js";
|
||||
|
||||
function assert(condition: unknown, message: string): asserts condition {
|
||||
if (!condition) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs the Crestodian planner fallback Docker smoke against the package-installed
|
||||
# functional E2E image, with only the test harness mounted from the checkout.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -16,11 +18,13 @@ trap cleanup EXIT
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-planner
|
||||
|
||||
echo "Running in-container Crestodian planner fallback smoke..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
set +e
|
||||
docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \
|
||||
-e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc "set -euo pipefail
|
||||
node --import tsx scripts/e2e/crestodian-planner-docker-client.ts
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// Crestodian rescue-message Docker harness.
|
||||
// Imports packaged dist modules so the Docker lane verifies the npm tarball,
|
||||
// while this small test driver stays mounted from the checkout.
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { handleCrestodianCommand } from "../../src/auto-reply/reply/commands-crestodian.js";
|
||||
import { clearConfigCache } from "../../src/config/config.js";
|
||||
import type { OpenClawConfig } from "../../src/config/types.openclaw.js";
|
||||
import { runCrestodianRescueMessage } from "../../src/crestodian/rescue-message.js";
|
||||
import { handleCrestodianCommand } from "../../dist/auto-reply/reply/commands-crestodian.js";
|
||||
import { clearConfigCache } from "../../dist/config/config.js";
|
||||
import type { OpenClawConfig } from "../../dist/config/types.openclaw.js";
|
||||
import { runCrestodianRescueMessage } from "../../dist/crestodian/rescue-message.js";
|
||||
|
||||
type CommandResult = Awaited<ReturnType<typeof handleCrestodianCommand>>;
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs the Crestodian rescue-message Docker smoke against the package-installed
|
||||
# functional E2E image, with only the test harness mounted from the checkout.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -16,11 +18,13 @@ trap cleanup EXIT
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-rescue
|
||||
|
||||
echo "Running in-container Crestodian rescue smoke..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
set +e
|
||||
docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \
|
||||
-e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc "set -euo pipefail
|
||||
node --import tsx scripts/e2e/crestodian-rescue-docker-client.ts
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Starts Gateway plus seeded cron/subagent MCP work in Docker, then verifies MCP
|
||||
# child-process cleanup through a mounted test harness.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -18,6 +20,7 @@ trap cleanup EXIT
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" cron-mcp-cleanup
|
||||
|
||||
echo "Running in-container cron/subagent MCP cleanup smoke..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
set +e
|
||||
docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
@@ -33,6 +36,7 @@ docker run --rm \
|
||||
-e "GW_URL=ws://127.0.0.1:$PORT" \
|
||||
-e "GW_TOKEN=$TOKEN" \
|
||||
-e "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1" \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc "set -euo pipefail
|
||||
entry=dist/index.mjs
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Shared Docker E2E OpenAI provider config seed helper.
|
||||
// Uses packaged plugin-sdk runtime modules so seeded configs match the npm tarball.
|
||||
import {
|
||||
applyProviderConfigWithDefaultModelPreset,
|
||||
type ModelDefinitionConfig,
|
||||
type OpenClawConfig,
|
||||
} from "../../src/plugin-sdk/provider-onboard.ts";
|
||||
} from "../../dist/plugin-sdk/provider-onboard.js";
|
||||
|
||||
export type { OpenClawConfig };
|
||||
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verifies doctor/daemon repair switches service entrypoints between package and
|
||||
# git installs. Both fixtures come from the same prepared OpenClaw npm tarball.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh"
|
||||
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-doctor-install-switch-e2e" OPENCLAW_DOCTOR_INSTALL_SWITCH_E2E_IMAGE)"
|
||||
PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz doctor-switch "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")"
|
||||
# Bare lanes mount the package artifact instead of baking app sources into the image.
|
||||
docker_e2e_package_mount_args "$PACKAGE_TGZ"
|
||||
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" doctor-switch
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" doctor-switch "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "bare"
|
||||
|
||||
echo "Running doctor install switch E2E..."
|
||||
docker run --rm -e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 "$IMAGE_NAME" bash -lc '
|
||||
docker run --rm \
|
||||
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
"${DOCKER_E2E_PACKAGE_ARGS[@]}" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
|
||||
# Keep logs focused; the npm global install step can emit noisy deprecation warnings.
|
||||
@@ -74,15 +84,23 @@ exit 0
|
||||
LOGINCTL
|
||||
chmod +x /tmp/openclaw-bin/loginctl
|
||||
|
||||
# Install the npm-global variant from the local /app source.
|
||||
# `npm pack` can emit script output; keep only the tarball name.
|
||||
pkg_tgz="$(npm pack --ignore-scripts --silent /app | tail -n 1 | tr -d '\r')"
|
||||
if [ ! -f "/app/$pkg_tgz" ]; then
|
||||
echo "npm pack failed (expected /app/$pkg_tgz)"
|
||||
exit 1
|
||||
fi
|
||||
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
|
||||
git_root="/tmp/openclaw-git"
|
||||
mkdir -p "$git_root"
|
||||
# The git-style install fixture is unpacked from the tarball so this lane does
|
||||
# not depend on checkout source files being present in the Docker image.
|
||||
tar -xzf "$package_tgz" -C "$git_root" --strip-components=1
|
||||
(
|
||||
cd "$git_root"
|
||||
npm install --omit=optional --no-fund --no-audit >/tmp/openclaw-git-install.log 2>&1
|
||||
git init -q
|
||||
git config user.email "docker-e2e@openclaw.local"
|
||||
git config user.name "OpenClaw Docker E2E"
|
||||
git add -A
|
||||
git commit -qm "test fixture"
|
||||
)
|
||||
npm_log="/tmp/openclaw-doctor-switch-npm-install.log"
|
||||
if ! npm install -g --prefix /tmp/npm-prefix "/app/$pkg_tgz" >"$npm_log" 2>&1; then
|
||||
if ! npm install -g --prefix /tmp/npm-prefix "$package_tgz" >"$npm_log" 2>&1; then
|
||||
cat "$npm_log"
|
||||
exit 1
|
||||
fi
|
||||
@@ -95,12 +113,12 @@ LOGINCTL
|
||||
npm_entry="$npm_root/dist/index.js"
|
||||
fi
|
||||
|
||||
if [ -f "/app/dist/index.mjs" ]; then
|
||||
git_entry="/app/dist/index.mjs"
|
||||
if [ -f "$git_root/dist/index.mjs" ]; then
|
||||
git_entry="$git_root/dist/index.mjs"
|
||||
else
|
||||
git_entry="/app/dist/index.js"
|
||||
git_entry="$git_root/dist/index.js"
|
||||
fi
|
||||
git_cli="/app/openclaw.mjs"
|
||||
git_cli="$git_root/openclaw.mjs"
|
||||
|
||||
assert_entrypoint() {
|
||||
local unit_path="$1"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs a Docker Gateway plus MCP stdio bridge smoke with seeded conversations and
|
||||
# raw Claude notification-frame assertions.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -18,6 +20,7 @@ trap cleanup EXIT
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" mcp-channels
|
||||
|
||||
echo "Running in-container gateway + MCP smoke..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
set +e
|
||||
docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
@@ -33,6 +36,7 @@ docker run --rm \
|
||||
-e "GW_URL=ws://127.0.0.1:$PORT" \
|
||||
-e "GW_TOKEN=$TOKEN" \
|
||||
-e "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1" \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc "set -euo pipefail
|
||||
entry=dist/index.mjs
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Shared MCP-channel Docker E2E harness helpers.
|
||||
// The mounted test harness imports packaged dist modules so bridge assertions run
|
||||
// against the OpenClaw npm tarball installed in the functional image.
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import process from "node:process";
|
||||
@@ -6,10 +9,10 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
||||
import { WebSocket } from "ws";
|
||||
import { z } from "zod";
|
||||
import { PROTOCOL_VERSION } from "../../src/gateway/protocol/index.ts";
|
||||
import { formatErrorMessage } from "../../src/infra/errors.ts";
|
||||
import { rawDataToString } from "../../src/infra/ws.ts";
|
||||
import { readStringValue } from "../../src/shared/string-coerce.ts";
|
||||
import { PROTOCOL_VERSION } from "../../dist/gateway/protocol/index.js";
|
||||
import { formatErrorMessage } from "../../dist/infra/errors.js";
|
||||
import { rawDataToString } from "../../dist/infra/ws.js";
|
||||
import { readStringValue } from "../../dist/shared/string-coerce.js";
|
||||
|
||||
export const ClaudeChannelNotificationSchema = z.object({
|
||||
method: z.literal("notifications/claude/channel"),
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Installs a prepared OpenClaw npm tarball in Docker, runs non-interactive
|
||||
# onboarding for a channel, and verifies one mocked model turn through Gateway.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh"
|
||||
|
||||
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-npm-onboard-channel-agent-e2e" OPENCLAW_NPM_ONBOARD_E2E_IMAGE)"
|
||||
DOCKER_TARGET="${OPENCLAW_NPM_ONBOARD_DOCKER_TARGET:-e2e-runner}"
|
||||
DOCKER_TARGET="${OPENCLAW_NPM_ONBOARD_DOCKER_TARGET:-bare}"
|
||||
HOST_BUILD="${OPENCLAW_NPM_ONBOARD_HOST_BUILD:-1}"
|
||||
PACKAGE_TGZ="${OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ:-}"
|
||||
CHANNEL="${OPENCLAW_NPM_ONBOARD_CHANNEL:-telegram}"
|
||||
@@ -22,32 +25,14 @@ docker_e2e_build_or_reuse "$IMAGE_NAME" npm-onboard-channel-agent "$ROOT_DIR/scr
|
||||
|
||||
prepare_package_tgz() {
|
||||
if [ -n "$PACKAGE_TGZ" ]; then
|
||||
if [ ! -f "$PACKAGE_TGZ" ]; then
|
||||
echo "OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ does not exist: $PACKAGE_TGZ" >&2
|
||||
exit 1
|
||||
fi
|
||||
PACKAGE_TGZ="$(cd "$(dirname "$PACKAGE_TGZ")" && pwd)/$(basename "$PACKAGE_TGZ")"
|
||||
PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz npm-onboard-channel-agent "$PACKAGE_TGZ")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$HOST_BUILD" != "0" ]; then
|
||||
echo "Building host package artifacts..."
|
||||
run_logged npm-onboard-channel-agent-host-build pnpm build
|
||||
else
|
||||
echo "Skipping host build (OPENCLAW_NPM_ONBOARD_HOST_BUILD=0)"
|
||||
fi
|
||||
|
||||
echo "Writing package inventory and packing once..."
|
||||
run_logged npm-onboard-channel-agent-inventory node --import tsx --input-type=module -e 'const { writePackageDistInventory } = await import("./src/infra/package-dist-inventory.ts"); await writePackageDistInventory(process.cwd());'
|
||||
local pack_dir
|
||||
pack_dir="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-npm-onboard-pack.XXXXXX")"
|
||||
run_logged npm-onboard-channel-agent-pack npm pack --ignore-scripts --pack-destination "$pack_dir"
|
||||
PACKAGE_TGZ="$(find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)"
|
||||
if [ -z "$PACKAGE_TGZ" ]; then
|
||||
echo "missing packed OpenClaw tarball" >&2
|
||||
if [ "$HOST_BUILD" = "0" ] && [ -z "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}" ]; then
|
||||
echo "OPENCLAW_NPM_ONBOARD_HOST_BUILD=0 requires OPENCLAW_CURRENT_PACKAGE_TGZ or OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ" >&2
|
||||
exit 1
|
||||
fi
|
||||
PACKAGE_TGZ="$(cd "$(dirname "$PACKAGE_TGZ")" && pwd)/$(basename "$PACKAGE_TGZ")"
|
||||
PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz npm-onboard-channel-agent)"
|
||||
}
|
||||
|
||||
prepare_package_tgz
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Installs a published OpenClaw npm package in Docker, performs Telegram
|
||||
# onboarding/doctor recovery, then runs the Telegram QA live harness.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -141,9 +143,12 @@ command -v openclaw
|
||||
openclaw --version
|
||||
EOF
|
||||
|
||||
# Mount only test harness/plugin QA sources; the SUT itself is the npm install.
|
||||
run_logged docker run --rm \
|
||||
"${docker_env[@]}" \
|
||||
-v "$ROOT_DIR/.artifacts:/app/.artifacts" \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
-v "$ROOT_DIR/extensions:/app/extensions:ro" \
|
||||
-v "$npm_prefix_host:/npm-global" \
|
||||
-i "$IMAGE_NAME" bash -s <<'EOF'
|
||||
set -euo pipefail
|
||||
@@ -171,6 +176,10 @@ trap 'status=$?; dump_hotpath_logs "$status"; exit "$status"' ERR
|
||||
|
||||
command -v openclaw
|
||||
openclaw --version
|
||||
# The mounted QA harness imports openclaw/plugin-sdk; point that package import
|
||||
# at the installed npm package without copying source into the test image.
|
||||
mkdir -p /app/node_modules
|
||||
ln -sfn /npm-global/lib/node_modules/openclaw /app/node_modules/openclaw
|
||||
|
||||
echo "Running installed npm onboarding recovery hot path..."
|
||||
OPENAI_API_KEY="${OPENAI_API_KEY:-sk-openclaw-npm-telegram-hotpath}" openclaw onboard --non-interactive --accept-risk \
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env -S node --import tsx
|
||||
// Telegram npm-live Docker harness.
|
||||
// Runs QA live transport code against the published package installed in Docker.
|
||||
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { formatErrorMessage } from "../../dist/infra/errors.js";
|
||||
import { runTelegramQaLive } from "../../extensions/qa-lab/src/live-transports/telegram/telegram-live.runtime.ts";
|
||||
import { formatErrorMessage } from "../../src/infra/errors.ts";
|
||||
|
||||
function parseBoolean(value: string | undefined) {
|
||||
const normalized = value?.trim().toLowerCase();
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs a mocked OpenAI image-generation auth smoke inside Docker against the
|
||||
# package-installed functional E2E image.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -10,9 +12,11 @@ SKIP_BUILD="${OPENCLAW_OPENAI_IMAGE_AUTH_E2E_SKIP_BUILD:-0}"
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" openai-image-auth "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "" "$SKIP_BUILD"
|
||||
|
||||
echo "Running OpenAI image auth Docker E2E..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
run_logged openai-image-auth docker run --rm \
|
||||
-e "OPENAI_API_KEY=sk-openclaw-image-auth-e2e" \
|
||||
-e "OPENCLAW_QA_ALLOW_LOCAL_IMAGE_PROVIDER=1" \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
-i "$IMAGE_NAME" bash -lc '
|
||||
set -euo pipefail
|
||||
export HOME="$(mktemp -d "/tmp/openclaw-openai-image-auth.XXXXXX")"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs Open WebUI against a Dockerized OpenClaw Gateway and verifies the proxied
|
||||
# chat path with a real OpenAI-compatible request.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -55,6 +57,7 @@ echo "Creating Docker network..."
|
||||
docker_cmd docker network create "$NET_NAME" >/dev/null
|
||||
|
||||
echo "Starting gateway container..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
docker_cmd docker run -d \
|
||||
--name "$GW_NAME" \
|
||||
--network "$NET_NAME" \
|
||||
@@ -66,6 +69,7 @@ docker_cmd docker run -d \
|
||||
-e "OPENCLAW_SKIP_CANVAS_HOST=1" \
|
||||
-e OPENAI_API_KEY \
|
||||
${OPENAI_BASE_URL_VALUE:+-e OPENAI_BASE_URL} \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
// Pi bundle MCP tools Docker harness.
|
||||
// Imports packaged dist modules so tool materialization is verified against the
|
||||
// npm tarball installed in the functional image.
|
||||
import { randomUUID } from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import { createRequire } from "node:module";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { materializeBundleMcpToolsForRun } from "../../src/agents/pi-bundle-mcp-materialize.ts";
|
||||
import { materializeBundleMcpToolsForRun } from "../../dist/agents/pi-bundle-mcp-materialize.js";
|
||||
import {
|
||||
disposeAllSessionMcpRuntimes,
|
||||
getOrCreateSessionMcpRuntime,
|
||||
} from "../../src/agents/pi-bundle-mcp-runtime.ts";
|
||||
import { applyFinalEffectiveToolPolicy } from "../../src/agents/pi-embedded-runner/effective-tool-policy.ts";
|
||||
import type { OpenClawConfig } from "../../src/config/types.openclaw.ts";
|
||||
import { getPluginToolMeta } from "../../src/plugins/tools.ts";
|
||||
} from "../../dist/agents/pi-bundle-mcp-runtime.js";
|
||||
import { applyFinalEffectiveToolPolicy } from "../../dist/agents/pi-embedded-runner/effective-tool-policy.js";
|
||||
import type { OpenClawConfig } from "../../dist/config/types.openclaw.js";
|
||||
import { getPluginToolMeta } from "../../dist/plugins/tools.js";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verifies embedded Pi bundle MCP tool materialization and tool-policy behavior
|
||||
# inside the package-installed functional E2E image.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -16,10 +18,12 @@ trap cleanup EXIT
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" pi-bundle-mcp-tools
|
||||
|
||||
echo "Running in-container Pi bundle MCP tool availability smoke..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
set +e
|
||||
docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc "set -euo pipefail
|
||||
node --import tsx scripts/e2e/pi-bundle-mcp-tools-docker-client.ts
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verifies `openclaw plugins update` is a no-op for an already-current plugin.
|
||||
# The CLI under test is installed from the prepared npm tarball in a bare runner.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh"
|
||||
|
||||
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-plugin-update-e2e" OPENCLAW_PLUGIN_UPDATE_E2E_IMAGE)"
|
||||
SKIP_BUILD="${OPENCLAW_PLUGIN_UPDATE_E2E_SKIP_BUILD:-0}"
|
||||
PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz plugin-update "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")"
|
||||
# Bare lanes mount the package artifact instead of baking app sources into the image.
|
||||
docker_e2e_package_mount_args "$PACKAGE_TGZ"
|
||||
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" plugin-update "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "" "$SKIP_BUILD"
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" plugin-update "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "bare" "$SKIP_BUILD"
|
||||
|
||||
echo "Running unchanged plugin update smoke..."
|
||||
docker run --rm \
|
||||
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
-e OPENCLAW_SKIP_CHANNELS=1 \
|
||||
-e OPENCLAW_SKIP_PROVIDERS=1 \
|
||||
"${DOCKER_E2E_PACKAGE_ARGS[@]}" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc "set -euo pipefail
|
||||
entry=dist/index.mjs
|
||||
[ -f \"\$entry\" ] || entry=dist/index.js
|
||||
package_tgz=\"\${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}\"
|
||||
npm install -g --prefix /tmp/npm-prefix \"\$package_tgz\" --no-fund --no-audit >/tmp/openclaw-install.log 2>&1
|
||||
entry=\"/tmp/npm-prefix/lib/node_modules/openclaw/dist/index.mjs\"
|
||||
[ -f \"\$entry\" ] || entry=/tmp/npm-prefix/lib/node_modules/openclaw/dist/index.js
|
||||
export NPM_CONFIG_REGISTRY=http://127.0.0.1:4873
|
||||
export PATH=\"/tmp/npm-prefix/bin:\$PATH\"
|
||||
|
||||
mkdir -p \"\$HOME/.openclaw/extensions/lossless-claw\"
|
||||
cat > \"\$HOME/.openclaw/extensions/lossless-claw/package.json\" <<'JSON'
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Session runtime-context Docker harness.
|
||||
// Imports packaged dist modules so transcript behavior is verified against the
|
||||
// npm tarball installed in the functional image.
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
@@ -6,7 +9,7 @@ import { SessionManager } from "@mariozechner/pi-coding-agent";
|
||||
import {
|
||||
queueRuntimeContextForNextTurn,
|
||||
resolveRuntimeContextPromptParts,
|
||||
} from "../../src/agents/pi-embedded-runner/run/runtime-context-prompt.js";
|
||||
} from "../../dist/agents/pi-embedded-runner/run/runtime-context-prompt.js";
|
||||
|
||||
type TranscriptEntry = {
|
||||
type?: string;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verifies hidden runtime context transcript persistence in Docker using the
|
||||
# package-installed functional E2E image.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
@@ -17,10 +19,12 @@ trap cleanup EXIT
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" session-runtime-context
|
||||
|
||||
echo "Running session runtime context Docker E2E..."
|
||||
# Harness files are mounted read-only; the app under test comes from /app/dist.
|
||||
set +e
|
||||
docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
-v "$ROOT_DIR/scripts/e2e:/app/scripts/e2e:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc 'set -euo pipefail; node --import tsx scripts/e2e/session-runtime-context-docker-client.ts' \
|
||||
>"$RUN_LOG" 2>&1
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# Exercises package-to-git and git-to-package update channel switching in Docker.
|
||||
# Both package and git fixtures are derived from the same prepared npm tarball.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh"
|
||||
|
||||
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-update-channel-switch-e2e" OPENCLAW_UPDATE_CHANNEL_SWITCH_E2E_IMAGE)"
|
||||
SKIP_BUILD="${OPENCLAW_UPDATE_CHANNEL_SWITCH_E2E_SKIP_BUILD:-0}"
|
||||
PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz update-channel-switch "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")"
|
||||
# Bare lanes mount the package artifact instead of baking app sources into the image.
|
||||
docker_e2e_package_mount_args "$PACKAGE_TGZ"
|
||||
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" update-channel-switch "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "" "$SKIP_BUILD"
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" update-channel-switch "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "bare" "$SKIP_BUILD"
|
||||
|
||||
echo "Running update channel switch E2E..."
|
||||
docker run --rm \
|
||||
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
-e OPENCLAW_SKIP_CHANNELS=1 \
|
||||
-e OPENCLAW_SKIP_PROVIDERS=1 \
|
||||
"${DOCKER_E2E_PACKAGE_ARGS[@]}" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc 'set -euo pipefail
|
||||
|
||||
@@ -29,32 +36,26 @@ export OPENCLAW_DISABLE_BUNDLED_PLUGINS=1
|
||||
export OPENCLAW_NO_ONBOARD=1
|
||||
export OPENCLAW_NO_PROMPT=1
|
||||
|
||||
cat > /app/.gitignore <<'"'"'GITIGNORE'"'"'
|
||||
node_modules
|
||||
**/node_modules/
|
||||
dist
|
||||
dist-runtime
|
||||
.turbo
|
||||
coverage
|
||||
GITIGNORE
|
||||
|
||||
node --import tsx scripts/write-package-dist-inventory.ts
|
||||
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
|
||||
git_root="/tmp/openclaw-git"
|
||||
mkdir -p "$git_root"
|
||||
# Build the fake git install from the packed package contents, not the checkout.
|
||||
tar -xzf "$package_tgz" -C "$git_root" --strip-components=1
|
||||
(
|
||||
cd "$git_root"
|
||||
npm install --omit=optional --no-fund --no-audit >/tmp/openclaw-git-install.log 2>&1
|
||||
)
|
||||
|
||||
git config --global user.email "docker-e2e@openclaw.local"
|
||||
git config --global user.name "OpenClaw Docker E2E"
|
||||
git config --global gc.auto 0
|
||||
git -C /app init -q
|
||||
git -C /app config gc.auto 0
|
||||
git -C /app add -A
|
||||
git -C /app commit -qm "test fixture"
|
||||
fixture_sha="$(git -C /app rev-parse HEAD)"
|
||||
git -C "$git_root" init -q
|
||||
git -C "$git_root" config gc.auto 0
|
||||
git -C "$git_root" add -A
|
||||
git -C "$git_root" commit -qm "test fixture"
|
||||
fixture_sha="$(git -C "$git_root" rev-parse HEAD)"
|
||||
|
||||
pkg_tgz="$(npm pack --ignore-scripts --silent --pack-destination /tmp /app | tail -n 1 | tr -d "\r")"
|
||||
pkg_tgz_path="/tmp/$pkg_tgz"
|
||||
if [ ! -f "$pkg_tgz_path" ]; then
|
||||
echo "npm pack failed (expected $pkg_tgz_path)"
|
||||
exit 1
|
||||
fi
|
||||
pkg_tgz_path="$package_tgz"
|
||||
|
||||
npm install -g --prefix /tmp/npm-prefix --omit=optional "$pkg_tgz_path"
|
||||
|
||||
@@ -70,7 +71,7 @@ cat > "$HOME/.openclaw/openclaw.json" <<'"'"'JSON'"'"'
|
||||
}
|
||||
JSON
|
||||
|
||||
export OPENCLAW_GIT_DIR=/app
|
||||
export OPENCLAW_GIT_DIR="$git_root"
|
||||
export OPENCLAW_UPDATE_DEV_TARGET_REF="$fixture_sha"
|
||||
|
||||
echo "==> package -> git dev channel"
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Shared Docker E2E image resolver/builder.
|
||||
# Suite-specific scripts call this to resolve overrides, reuse pulled images, or
|
||||
# build the runner/functional images with the prepared OpenClaw package tarball.
|
||||
|
||||
DOCKER_E2E_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="${ROOT_DIR:-$(cd "$DOCKER_E2E_LIB_DIR/../.." && pwd)}"
|
||||
|
||||
source "$DOCKER_E2E_LIB_DIR/docker-e2e-logs.sh"
|
||||
source "$DOCKER_E2E_LIB_DIR/docker-build.sh"
|
||||
source "$DOCKER_E2E_LIB_DIR/docker-e2e-package.sh"
|
||||
|
||||
docker_e2e_resolve_image() {
|
||||
local default_image="$1"
|
||||
@@ -34,6 +39,11 @@ docker_e2e_build_or_reuse() {
|
||||
local context="${4:-$ROOT_DIR}"
|
||||
local target="${5:-}"
|
||||
local skip_build="${6:-0}"
|
||||
if [ -z "$target" ] && [ "$dockerfile" = "$ROOT_DIR/scripts/e2e/Dockerfile" ]; then
|
||||
# The generic E2E image defaults to the package-installed app image; tests
|
||||
# that need a clean install runner pass target=bare explicitly.
|
||||
target="functional"
|
||||
fi
|
||||
|
||||
if [ "${OPENCLAW_SKIP_DOCKER_BUILD:-0}" = "1" ] || [ "$skip_build" = "1" ]; then
|
||||
echo "Reusing Docker image: $image_name"
|
||||
@@ -53,6 +63,15 @@ docker_e2e_build_or_reuse() {
|
||||
if [ -n "$target" ]; then
|
||||
build_args+=(--target "$target")
|
||||
fi
|
||||
if [ "$target" = "functional" ]; then
|
||||
local package_tgz
|
||||
local package_context
|
||||
package_tgz="$(docker_e2e_prepare_package_tgz "$label")"
|
||||
package_context="$(docker_e2e_prepare_package_context "$package_tgz")"
|
||||
# The Dockerfile never sees repo sources as app input; functional installs
|
||||
# exactly this tarball through a named BuildKit context.
|
||||
build_args+=(--build-context "openclaw_package=$package_context")
|
||||
fi
|
||||
build_args+=(-t "$image_name" -f "$dockerfile" "$context")
|
||||
docker_build_run "$label-build" "${build_args[@]}"
|
||||
}
|
||||
|
||||
63
scripts/lib/docker-e2e-package.sh
Normal file
63
scripts/lib/docker-e2e-package.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Shared package helpers for Docker E2E scripts.
|
||||
# Builds or resolves one OpenClaw npm tarball and exposes mount/build-context
|
||||
# helpers so Docker lanes test the package artifact instead of repo sources.
|
||||
|
||||
DOCKER_E2E_PACKAGE_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="${ROOT_DIR:-$(cd "$DOCKER_E2E_PACKAGE_LIB_DIR/../.." && pwd)}"
|
||||
|
||||
if ! declare -F run_logged >/dev/null 2>&1; then
|
||||
source "$DOCKER_E2E_PACKAGE_LIB_DIR/docker-e2e-logs.sh"
|
||||
fi
|
||||
|
||||
docker_e2e_abs_path() {
|
||||
local file="$1"
|
||||
(cd "$(dirname "$file")" && printf '%s/%s\n' "$(pwd)" "$(basename "$file")")
|
||||
}
|
||||
|
||||
docker_e2e_prepare_package_tgz() {
|
||||
local label="$1"
|
||||
local package_tgz="${2:-${OPENCLAW_CURRENT_PACKAGE_TGZ:-}}"
|
||||
|
||||
if [ -n "$package_tgz" ]; then
|
||||
if [ ! -f "$package_tgz" ]; then
|
||||
echo "OpenClaw package tarball does not exist: $package_tgz" >&2
|
||||
return 1
|
||||
fi
|
||||
docker_e2e_abs_path "$package_tgz"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Building OpenClaw package artifacts..."
|
||||
run_logged "$label-host-build" pnpm build
|
||||
echo "Writing package inventory and packing OpenClaw once..."
|
||||
run_logged "$label-inventory" node --import tsx --input-type=module -e 'const { writePackageDistInventory } = await import("./src/infra/package-dist-inventory.ts"); await writePackageDistInventory(process.cwd());'
|
||||
|
||||
local pack_dir
|
||||
pack_dir="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-docker-e2e-pack.XXXXXX")"
|
||||
run_logged "$label-pack" npm pack --ignore-scripts --pack-destination "$pack_dir"
|
||||
|
||||
package_tgz="$(find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)"
|
||||
if [ -z "$package_tgz" ]; then
|
||||
echo "missing packed OpenClaw tarball" >&2
|
||||
return 1
|
||||
fi
|
||||
docker_e2e_abs_path "$package_tgz"
|
||||
}
|
||||
|
||||
docker_e2e_prepare_package_context() {
|
||||
local package_tgz="$1"
|
||||
local context_dir
|
||||
context_dir="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-docker-e2e-package-context.XXXXXX")"
|
||||
# BuildKit named contexts must be directories, so expose the tarball as a
|
||||
# stable filename inside a tiny temporary context.
|
||||
cp "$package_tgz" "$context_dir/openclaw-current.tgz"
|
||||
printf '%s\n' "$context_dir"
|
||||
}
|
||||
|
||||
docker_e2e_package_mount_args() {
|
||||
local package_tgz="$1"
|
||||
local target="${2:-/tmp/openclaw-current.tgz}"
|
||||
DOCKER_E2E_PACKAGE_ARGS=(-v "$package_tgz:$target:ro" -e "OPENCLAW_CURRENT_PACKAGE_TGZ=$target")
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// Docker E2E aggregate scheduler.
|
||||
// Builds shared Docker images, prepares one OpenClaw npm tarball, assigns lanes
|
||||
// to bare/functional images, and runs lanes through weighted resource pools.
|
||||
import { spawn } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import { mkdir, readFile } from "node:fs/promises";
|
||||
@@ -661,8 +664,12 @@ function buildLaneRerunCommand(name, baseEnv) {
|
||||
["OPENCLAW_DOCKER_E2E_IMAGE", image || DEFAULT_E2E_IMAGE],
|
||||
["OPENCLAW_DOCKER_E2E_BARE_IMAGE", baseEnv.OPENCLAW_DOCKER_E2E_BARE_IMAGE],
|
||||
["OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE", baseEnv.OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE],
|
||||
["OPENCLAW_CURRENT_PACKAGE_TGZ", baseEnv.OPENCLAW_CURRENT_PACKAGE_TGZ],
|
||||
];
|
||||
return `${env.map(([key, value]) => `${key}=${shellQuote(value)}`).join(" ")} pnpm test:docker:all`;
|
||||
return `${env
|
||||
.filter(([, value]) => value !== undefined && value !== "")
|
||||
.map(([key, value]) => `${key}=${shellQuote(value)}`)
|
||||
.join(" ")} pnpm test:docker:all`;
|
||||
}
|
||||
|
||||
function findLaneByName(name) {
|
||||
@@ -805,11 +812,8 @@ function printLaneManifest(label, poolLanes, timingStore) {
|
||||
}
|
||||
}
|
||||
|
||||
function lanesNeedBundledPackage(poolLanes) {
|
||||
return poolLanes.some(
|
||||
(poolLane) =>
|
||||
poolLane.name === "npm-onboard-channel-agent" || poolLane.name.startsWith("bundled-channel"),
|
||||
);
|
||||
function lanesNeedOpenClawPackage(poolLanes) {
|
||||
return poolLanes.some((poolLane) => poolLane.e2eImageKind);
|
||||
}
|
||||
|
||||
function dockerPreflightContainerNames(raw) {
|
||||
@@ -1011,30 +1015,33 @@ async function runDockerPreflight(baseEnv, options) {
|
||||
console.log(`==> Docker preflight run: ${elapsedSeconds}s`);
|
||||
}
|
||||
|
||||
async function prepareBundledChannelPackage(baseEnv, logDir) {
|
||||
if (baseEnv.OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ) {
|
||||
console.log(`==> Bundled channel package: ${baseEnv.OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ}`);
|
||||
async function prepareOpenClawPackage(baseEnv, logDir) {
|
||||
const existing =
|
||||
baseEnv.OPENCLAW_CURRENT_PACKAGE_TGZ ||
|
||||
baseEnv.OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ ||
|
||||
baseEnv.OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ;
|
||||
if (existing) {
|
||||
const packageTgz = path.resolve(existing);
|
||||
baseEnv.OPENCLAW_CURRENT_PACKAGE_TGZ = packageTgz;
|
||||
baseEnv.OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ ||= packageTgz;
|
||||
baseEnv.OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ ||= packageTgz;
|
||||
baseEnv.OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD = "0";
|
||||
baseEnv.OPENCLAW_NPM_ONBOARD_HOST_BUILD = "0";
|
||||
console.log(`==> OpenClaw package: ${packageTgz}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const packDir = path.join(logDir, "bundled-channel-package");
|
||||
const packDir = path.join(logDir, "openclaw-package");
|
||||
await mkdir(packDir, { recursive: true });
|
||||
const packScript = [
|
||||
"set -euo pipefail",
|
||||
"node --import tsx --input-type=module -e \"const { writePackageDistInventory } = await import('./src/infra/package-dist-inventory.ts'); await writePackageDistInventory(process.cwd());\"",
|
||||
"npm pack --silent --ignore-scripts --pack-destination /tmp/openclaw-pack >/tmp/openclaw-pack.out",
|
||||
"cat /tmp/openclaw-pack.out",
|
||||
].join("\n");
|
||||
await runForeground("Build OpenClaw package artifacts once", "pnpm build", baseEnv);
|
||||
await runForeground(
|
||||
"Pack bundled channel package once from bare Docker E2E image",
|
||||
[
|
||||
"docker run --rm",
|
||||
"-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0",
|
||||
`-v ${shellQuote(packDir)}:/tmp/openclaw-pack`,
|
||||
shellQuote(baseEnv.OPENCLAW_DOCKER_E2E_BARE_IMAGE),
|
||||
"bash -lc",
|
||||
shellQuote(packScript),
|
||||
].join(" "),
|
||||
"Write OpenClaw package inventory",
|
||||
"node --import tsx --input-type=module -e \"const { writePackageDistInventory } = await import('./src/infra/package-dist-inventory.ts'); await writePackageDistInventory(process.cwd());\"",
|
||||
baseEnv,
|
||||
);
|
||||
await runForeground(
|
||||
"Pack OpenClaw package once",
|
||||
`npm pack --silent --ignore-scripts --pack-destination ${shellQuote(packDir)}`,
|
||||
baseEnv,
|
||||
);
|
||||
|
||||
@@ -1045,11 +1052,12 @@ async function prepareBundledChannelPackage(baseEnv, logDir) {
|
||||
if (!packed) {
|
||||
throw new Error(`missing packed OpenClaw tarball in ${packDir}`);
|
||||
}
|
||||
baseEnv.OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ = path.join(packDir, packed);
|
||||
baseEnv.OPENCLAW_CURRENT_PACKAGE_TGZ = path.join(packDir, packed);
|
||||
baseEnv.OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ = baseEnv.OPENCLAW_CURRENT_PACKAGE_TGZ;
|
||||
baseEnv.OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD = "0";
|
||||
baseEnv.OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ = baseEnv.OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ;
|
||||
baseEnv.OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ = baseEnv.OPENCLAW_CURRENT_PACKAGE_TGZ;
|
||||
baseEnv.OPENCLAW_NPM_ONBOARD_HOST_BUILD = "0";
|
||||
console.log(`==> Bundled channel package: ${baseEnv.OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ}`);
|
||||
console.log(`==> OpenClaw package: ${baseEnv.OPENCLAW_CURRENT_PACKAGE_TGZ}`);
|
||||
}
|
||||
|
||||
function laneEnv(poolLane, baseEnv, logDir, cacheKey) {
|
||||
@@ -1530,10 +1538,17 @@ async function main() {
|
||||
});
|
||||
},
|
||||
);
|
||||
const scheduledLanes = [...orderedLanes, ...orderedTailLanes];
|
||||
if (lanesNeedOpenClawPackage(scheduledLanes)) {
|
||||
await runPhase(phases, "prepare-openclaw-package", {}, async () => {
|
||||
await prepareOpenClawPackage(baseEnv, logDir);
|
||||
});
|
||||
} else {
|
||||
console.log("==> OpenClaw package: not needed for selected lanes");
|
||||
}
|
||||
|
||||
if (buildEnabled) {
|
||||
const buildEntries = [];
|
||||
const scheduledLanes = [...orderedLanes, ...orderedTailLanes];
|
||||
if (scheduledLanes.some((poolLane) => poolLane.live)) {
|
||||
buildEntries.push({
|
||||
command: "pnpm test:docker:live-build",
|
||||
@@ -1547,7 +1562,7 @@ async function main() {
|
||||
command: "pnpm test:docker:e2e-build",
|
||||
env: {
|
||||
OPENCLAW_DOCKER_E2E_IMAGE: baseEnv.OPENCLAW_DOCKER_E2E_BARE_IMAGE,
|
||||
OPENCLAW_DOCKER_E2E_TARGET: "build",
|
||||
OPENCLAW_DOCKER_E2E_TARGET: "bare",
|
||||
},
|
||||
label: `shared bare Docker E2E image once: ${baseEnv.OPENCLAW_DOCKER_E2E_BARE_IMAGE}`,
|
||||
phaseDetails: { image: baseEnv.OPENCLAW_DOCKER_E2E_BARE_IMAGE, imageKind: "bare" },
|
||||
@@ -1573,13 +1588,6 @@ async function main() {
|
||||
} else {
|
||||
console.log(`==> Shared Docker image builds: skipped`);
|
||||
}
|
||||
if (lanesNeedBundledPackage([...orderedLanes, ...orderedTailLanes])) {
|
||||
await runPhase(phases, "prepare-bundled-channel-package", { imageKind: "bare" }, async () => {
|
||||
await prepareBundledChannelPackage(baseEnv, logDir);
|
||||
});
|
||||
} else {
|
||||
console.log("==> Bundled channel package: not needed for selected lanes");
|
||||
}
|
||||
|
||||
const options = {
|
||||
...schedulerOptions,
|
||||
|
||||
Reference in New Issue
Block a user