test(docker): speed bundled dependency e2e

This commit is contained in:
Peter Steinberger
2026-04-23 00:35:10 +01:00
parent ff02563c7c
commit d50181e209
3 changed files with 65 additions and 53 deletions

View File

@@ -896,6 +896,9 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or
- Gateway networking (two containers, WS auth + health): `pnpm test:docker:gateway-network` (script: `scripts/e2e/gateway-network-docker.sh`)
- MCP channel bridge (seeded Gateway + stdio bridge + raw Claude notification-frame smoke): `pnpm test:docker:mcp-channels` (script: `scripts/e2e/mcp-channels-docker.sh`)
- Plugins (install smoke + `/plugin` alias + Claude-bundle restart semantics): `pnpm test:docker:plugins` (script: `scripts/e2e/plugins-docker.sh`)
- Bundled plugin runtime deps: `pnpm test:docker:bundled-channel-deps` builds a small Docker runner image by default, builds and packs OpenClaw once on the host, then mounts that tarball into each Linux install scenario. Reuse the image with `OPENCLAW_SKIP_DOCKER_BUILD=1`, skip the host rebuild after a fresh local build with `OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0`, or point at an existing tarball with `OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ=/path/to/openclaw-*.tgz`.
- 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`.
The live-model Docker runners also bind-mount the current checkout read-only and
stage it into a temporary workdir inside the container. This keeps the runtime

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.7
FROM node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b
FROM node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b AS e2e-runner
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates git \
@@ -18,6 +18,8 @@ ENV NODE_OPTIONS="--disable-warning=ExperimentalWarning"
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 extensions ./extensions
@@ -27,6 +29,8 @@ COPY --chown=appuser:appuser scripts/postinstall-bundled-plugins.mjs scripts/pre
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 tsconfig.json tsconfig.plugin-sdk.dts.json tsdown.config.ts vitest.config.ts openclaw.mjs ./
COPY --chown=appuser:appuser src ./src
COPY --chown=appuser:appuser test ./test

View File

@@ -6,14 +6,55 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-logs.sh"
IMAGE_NAME="${OPENCLAW_BUNDLED_CHANNEL_DEPS_E2E_IMAGE:-openclaw-bundled-channel-deps-e2e}"
UPDATE_BASELINE_VERSION="${OPENCLAW_BUNDLED_CHANNEL_UPDATE_BASELINE_VERSION:-2026.4.20}"
DOCKER_TARGET="${OPENCLAW_BUNDLED_CHANNEL_DOCKER_TARGET:-e2e-runner}"
HOST_BUILD="${OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD:-1}"
PACKAGE_TGZ="${OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ:-}"
RUN_CHANNEL_SCENARIOS="${OPENCLAW_BUNDLED_CHANNEL_SCENARIOS:-1}"
RUN_UPDATE_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO:-1}"
RUN_ROOT_OWNED_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO:-1}"
RUN_SETUP_ENTRY_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO:-1}"
RUN_LOAD_FAILURE_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO:-1}"
echo "Building Docker image..."
run_logged bundled-channel-deps-build docker build -t "$IMAGE_NAME" -f "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR"
if [ "${OPENCLAW_SKIP_DOCKER_BUILD:-0}" = "1" ]; then
echo "Reusing Docker image: $IMAGE_NAME (OPENCLAW_SKIP_DOCKER_BUILD=1)"
else
echo "Building Docker image target $DOCKER_TARGET..."
run_logged bundled-channel-deps-build docker build --target "$DOCKER_TARGET" -t "$IMAGE_NAME" -f "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR"
fi
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")"
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
exit 1
fi
PACKAGE_TGZ="$(cd "$(dirname "$PACKAGE_TGZ")" && pwd)/$(basename "$PACKAGE_TGZ")"
}
prepare_package_tgz
DOCKER_PACKAGE_TGZ="/tmp/openclaw-current.tgz"
PACKAGE_DOCKER_ARGS=(-v "$PACKAGE_TGZ:$DOCKER_PACKAGE_TGZ:ro" -e "OPENCLAW_CURRENT_PACKAGE_TGZ=$DOCKER_PACKAGE_TGZ")
run_channel_scenario() {
local channel="$1"
@@ -26,6 +67,7 @@ run_channel_scenario() {
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
-e OPENCLAW_CHANNEL_UNDER_TEST="$channel" \
-e OPENCLAW_DEP_SENTINEL="$dep_sentinel" \
"${PACKAGE_DOCKER_ARGS[@]}" \
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'
set -euo pipefail
@@ -49,15 +91,8 @@ cleanup() {
}
trap cleanup EXIT
echo "Packing and installing current OpenClaw build..."
pack_dir="$(mktemp -d "/tmp/openclaw-pack.XXXXXX")"
npm pack --ignore-scripts --pack-destination "$pack_dir" >/tmp/openclaw-pack.log 2>&1
package_tgz="$(find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)"
if [ -z "$package_tgz" ]; then
cat /tmp/openclaw-pack.log
echo "missing packed OpenClaw tarball" >&2
exit 1
fi
echo "Installing mounted OpenClaw package..."
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-install.log 2>&1
command -v openclaw >/dev/null
@@ -361,6 +396,7 @@ run_root_owned_global_scenario() {
echo "Running bundled channel root-owned global install Docker E2E..."
if ! docker run --rm --user root \
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
"${PACKAGE_DOCKER_ARGS[@]}" \
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'
set -euo pipefail
@@ -387,15 +423,8 @@ cleanup() {
}
trap cleanup EXIT
echo "Packing and installing current OpenClaw build into root-owned global npm..."
pack_dir="$(mktemp -d "/tmp/openclaw-root-owned-pack.XXXXXX")"
npm pack --ignore-scripts --pack-destination "$pack_dir" >/tmp/openclaw-root-owned-pack.log 2>&1
package_tgz="$(find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)"
if [ -z "$package_tgz" ]; then
cat /tmp/openclaw-root-owned-pack.log
echo "missing packed OpenClaw tarball" >&2
exit 1
fi
echo "Installing mounted OpenClaw package into root-owned global npm..."
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-root-owned-install.log 2>&1
root="$(package_root)"
@@ -550,6 +579,7 @@ run_setup_entry_scenario() {
echo "Running bundled channel setup-entry runtime deps Docker E2E..."
if ! docker run --rm \
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
"${PACKAGE_DOCKER_ARGS[@]}" \
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'
set -euo pipefail
@@ -566,15 +596,8 @@ package_root() {
printf "%s/openclaw" "$(npm root -g)"
}
echo "Packing and installing current OpenClaw build..."
pack_dir="$(mktemp -d "/tmp/openclaw-setup-entry-pack.XXXXXX")"
npm pack --ignore-scripts --pack-destination "$pack_dir" >/tmp/openclaw-setup-entry-pack.log 2>&1
package_tgz="$(find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)"
if [ -z "$package_tgz" ]; then
cat /tmp/openclaw-setup-entry-pack.log
echo "missing packed OpenClaw tarball" >&2
exit 1
fi
echo "Installing mounted OpenClaw package..."
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-setup-entry-install.log 2>&1
root="$(package_root)"
@@ -660,6 +683,7 @@ run_update_scenario() {
if ! docker run --rm \
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
-e OPENCLAW_BUNDLED_CHANNEL_UPDATE_BASELINE_VERSION="$UPDATE_BASELINE_VERSION" \
"${PACKAGE_DOCKER_ARGS[@]}" \
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'
set -euo pipefail
@@ -677,20 +701,7 @@ package_root() {
printf "%s/openclaw" "$(npm root -g)"
}
pack_current_candidate() {
local pack_dir
pack_dir="$(mktemp -d "/tmp/openclaw-update-pack.XXXXXX")"
node --import tsx --input-type=module -e 'const { writePackageDistInventory } = await import("./src/infra/package-dist-inventory.ts"); await writePackageDistInventory(process.cwd());' >/tmp/openclaw-update-inventory.log 2>&1
npm pack --ignore-scripts --pack-destination "$pack_dir" >/tmp/openclaw-update-pack.log 2>&1
find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit
}
package_tgz="$(pack_current_candidate)"
if [ -z "$package_tgz" ]; then
cat /tmp/openclaw-update-pack.log
echo "missing packed OpenClaw candidate tarball" >&2
exit 1
fi
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
update_target="file:$package_tgz"
candidate_version="$(node - <<'NODE' "$package_tgz"
const { execFileSync } = require("node:child_process");
@@ -1009,6 +1020,7 @@ run_load_failure_scenario() {
echo "Running bundled channel load-failure isolation Docker E2E..."
if ! docker run --rm \
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
"${PACKAGE_DOCKER_ARGS[@]}" \
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'
set -euo pipefail
@@ -1021,15 +1033,8 @@ package_root() {
printf "%s/openclaw" "$(npm root -g)"
}
echo "Packing and installing current OpenClaw build..."
pack_dir="$(mktemp -d "/tmp/openclaw-load-failure-pack.XXXXXX")"
npm pack --ignore-scripts --pack-destination "$pack_dir" >/tmp/openclaw-load-failure-pack.log 2>&1
package_tgz="$(find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)"
if [ -z "$package_tgz" ]; then
cat /tmp/openclaw-load-failure-pack.log
echo "missing packed OpenClaw tarball" >&2
exit 1
fi
echo "Installing mounted OpenClaw package..."
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-load-failure-install.log 2>&1
root="$(package_root)"