test: reuse Docker test-state in core E2E lanes

This commit is contained in:
Peter Steinberger
2026-04-28 19:47:03 +01:00
parent 4b69dc6228
commit d22a851253
12 changed files with 136 additions and 29 deletions

View File

@@ -41,6 +41,7 @@ EOF
echo "Building Docker image: $IMAGE_NAME"
docker_build_run browser-cdp-snapshot-build -t "$IMAGE_NAME" -f "$build_dir/Dockerfile" "$build_dir"
fi
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 browser-cdp-snapshot empty)"
echo "Starting browser CDP snapshot container..."
docker_cmd docker run -d \
@@ -53,13 +54,23 @@ docker_cmd docker run -d \
-e OPENCLAW_SKIP_GMAIL_WATCHER=1 \
-e OPENCLAW_SKIP_CRON=1 \
-e OPENCLAW_SKIP_CANVAS_HOST=1 \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
{
printf 'export HOME=%q\n' \"\$HOME\"
printf 'export OPENCLAW_HOME=%q\n' \"\$OPENCLAW_HOME\"
printf 'export OPENCLAW_STATE_DIR=%q\n' \"\$OPENCLAW_STATE_DIR\"
printf 'export OPENCLAW_CONFIG_PATH=%q\n' \"\$OPENCLAW_CONFIG_PATH\"
printf 'export OPENCLAW_AGENT_DIR=%q\n' \"\${OPENCLAW_AGENT_DIR-}\"
printf 'export PI_CODING_AGENT_DIR=%q\n' \"\${PI_CODING_AGENT_DIR-}\"
} >/tmp/openclaw-test-state-env
entry=dist/index.mjs
[ -f \"\$entry\" ] || entry=dist/index.js
mkdir -p \"\$HOME/.openclaw\" /tmp/openclaw-browser-cdp/chrome
mkdir -p /tmp/openclaw-browser-cdp/chrome
find dist -maxdepth 1 -type f -name 'pw-ai-*.js' ! -name 'pw-ai-state-*' -exec mv {} /tmp/openclaw-browser-cdp/ \;
cat > \"\$HOME/.openclaw/openclaw.json\" <<'JSON'
cat > \"\$OPENCLAW_CONFIG_PATH\" <<'JSON'
{
\"gateway\": {
\"port\": $PORT,
@@ -143,6 +154,7 @@ fi
echo "Running browser CDP snapshot smoke..."
docker_cmd docker exec "$CONTAINER_NAME" bash -lc "
set -euo pipefail
source /tmp/openclaw-test-state-env
entry=dist/index.mjs
[ -f \"\$entry\" ] || entry=dist/index.js
base_args=(--url ws://127.0.0.1:$PORT --token '$TOKEN')

View File

@@ -16,6 +16,7 @@ cleanup() {
trap cleanup EXIT
docker_e2e_build_or_reuse "$IMAGE_NAME" config-reload "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "" "$SKIP_BUILD"
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 config-reload empty)"
echo "Starting gateway container..."
docker run -d \
@@ -27,12 +28,21 @@ docker run -d \
-e OPENCLAW_SKIP_GMAIL_WATCHER=1 \
-e OPENCLAW_SKIP_CRON=1 \
-e OPENCLAW_SKIP_CANVAS_HOST=1 \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
{
printf 'export HOME=%q\n' \"\$HOME\"
printf 'export OPENCLAW_HOME=%q\n' \"\$OPENCLAW_HOME\"
printf 'export OPENCLAW_STATE_DIR=%q\n' \"\$OPENCLAW_STATE_DIR\"
printf 'export OPENCLAW_CONFIG_PATH=%q\n' \"\$OPENCLAW_CONFIG_PATH\"
printf 'export OPENCLAW_AGENT_DIR=%q\n' \"\${OPENCLAW_AGENT_DIR-}\"
printf 'export PI_CODING_AGENT_DIR=%q\n' \"\${PI_CODING_AGENT_DIR-}\"
} >/tmp/openclaw-test-state-env
entry=dist/index.mjs
[ -f \"\$entry\" ] || entry=dist/index.js
mkdir -p \"\$HOME/.openclaw\"
cat > \"\$HOME/.openclaw/openclaw.json\" <<'JSON'
cat > \"\$OPENCLAW_CONFIG_PATH\" <<'JSON'
{
\"gateway\": {
\"port\": $PORT,
@@ -95,18 +105,18 @@ fi
echo "Checking initial RPC status..."
docker exec "$CONTAINER_NAME" bash -lc "
source /tmp/openclaw-test-state-env
entry=dist/index.mjs
[ -f \"\$entry\" ] || entry=dist/index.js
node \"\$entry\" gateway status --url ws://127.0.0.1:$PORT --token '$TOKEN' --require-rpc --timeout 30000 >/tmp/config-reload-status-before.log
"
echo "Mutating hot-reload gateway metadata..."
docker exec "$CONTAINER_NAME" bash -lc "node --input-type=module - <<'NODE'
docker exec "$CONTAINER_NAME" bash -lc "source /tmp/openclaw-test-state-env
node --input-type=module - <<'NODE'
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
const configPath = process.env.OPENCLAW_CONFIG_PATH;
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
config.gateway.channelHealthCheckMinutes = 2;
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
@@ -122,6 +132,7 @@ fi
echo "Checking post-write RPC status..."
docker exec "$CONTAINER_NAME" bash -lc "
source /tmp/openclaw-test-state-env
entry=dist/index.mjs
[ -f \"\$entry\" ] || entry=dist/index.js
node \"\$entry\" gateway status --url ws://127.0.0.1:$PORT --token '$TOKEN' --require-rpc --timeout 30000 >/tmp/config-reload-status-after.log

View File

@@ -17,17 +17,18 @@ trap cleanup EXIT
docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-first-run
docker_e2e_harness_mount_args
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 crestodian-first-run empty)"
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" \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
"${DOCKER_E2E_HARNESS_ARGS[@]}" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
tsx scripts/e2e/crestodian-first-run-docker-client.ts
" >"$RUN_LOG" 2>&1
status=${PIPESTATUS[0]}

View File

@@ -17,17 +17,18 @@ trap cleanup EXIT
docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-planner
docker_e2e_harness_mount_args
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 crestodian-planner empty)"
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" \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
"${DOCKER_E2E_HARNESS_ARGS[@]}" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
tsx scripts/e2e/crestodian-planner-docker-client.ts
" >"$RUN_LOG" 2>&1
status=${PIPESTATUS[0]}

View File

@@ -17,17 +17,18 @@ trap cleanup EXIT
docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-rescue
docker_e2e_harness_mount_args
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 crestodian-rescue empty)"
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" \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
"${DOCKER_E2E_HARNESS_ARGS[@]}" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
tsx scripts/e2e/crestodian-rescue-docker-client.ts
" >"$RUN_LOG" 2>&1
status=${PIPESTATUS[0]}

View File

@@ -19,6 +19,7 @@ trap cleanup EXIT
docker_e2e_build_or_reuse "$IMAGE_NAME" cron-mcp-cleanup
docker_e2e_harness_mount_args
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 cron-mcp-cleanup empty)"
echo "Running in-container cron/subagent MCP cleanup smoke..."
# Harness files are mounted read-only; the app under test comes from /app/dist.
@@ -32,14 +33,14 @@ docker run --rm \
-e "OPENCLAW_SKIP_CANVAS_HOST=1" \
-e "OPENCLAW_SKIP_ACPX_RUNTIME=1" \
-e "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE=1" \
-e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \
-e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
-e "GW_URL=ws://127.0.0.1:$PORT" \
-e "GW_TOKEN=$TOKEN" \
-e "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1" \
"${DOCKER_E2E_HARNESS_ARGS[@]}" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
entry=dist/index.mjs
[ -f \"\$entry\" ] || entry=dist/index.js
export MOCK_PORT=44081

View File

@@ -19,6 +19,7 @@ trap cleanup EXIT
docker_e2e_build_or_reuse "$IMAGE_NAME" mcp-channels
docker_e2e_harness_mount_args
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 mcp-channels empty)"
echo "Running in-container gateway + MCP smoke..."
# Harness files are mounted read-only; the app under test comes from /app/dist.
@@ -32,14 +33,14 @@ docker run --rm \
-e "OPENCLAW_SKIP_CANVAS_HOST=1" \
-e "OPENCLAW_SKIP_ACPX_RUNTIME=1" \
-e "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE=1" \
-e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \
-e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
-e "GW_URL=ws://127.0.0.1:$PORT" \
-e "GW_TOKEN=$TOKEN" \
-e "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1" \
"${DOCKER_E2E_HARNESS_ARGS[@]}" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
entry=dist/index.mjs
[ -f \"\$entry\" ] || entry=dist/index.js
mock_port=44081

View File

@@ -17,16 +17,18 @@ trap cleanup EXIT
docker_e2e_build_or_reuse "$IMAGE_NAME" pi-bundle-mcp-tools
docker_e2e_harness_mount_args
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 pi-bundle-mcp-tools empty)"
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" \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
"${DOCKER_E2E_HARNESS_ARGS[@]}" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
tsx scripts/e2e/pi-bundle-mcp-tools-docker-client.ts
" >"$RUN_LOG" 2>&1
status=${PIPESTATUS[0]}

View File

@@ -14,15 +14,18 @@ PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz plugin-update "${OPENCLAW_CURRENT_
docker_e2e_package_mount_args "$PACKAGE_TGZ"
docker_e2e_build_or_reuse "$IMAGE_NAME" plugin-update "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "bare" "$SKIP_BUILD"
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 plugin-update empty)"
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 \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
"${DOCKER_E2E_PACKAGE_ARGS[@]}" \
"$IMAGE_NAME" \
bash -lc "set -euo pipefail
eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\"
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\"

View File

@@ -248,15 +248,22 @@ export const mainLanes = [
),
serviceLane("mcp-channels", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels", {
resources: ["npm"],
stateScenario: "empty",
weight: 3,
}),
lane("pi-bundle-mcp-tools", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:pi-bundle-mcp-tools"),
lane("crestodian-rescue", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-rescue"),
lane("crestodian-planner", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-planner"),
lane("pi-bundle-mcp-tools", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:pi-bundle-mcp-tools", {
stateScenario: "empty",
}),
lane("crestodian-rescue", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-rescue", {
stateScenario: "empty",
}),
lane("crestodian-planner", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-planner", {
stateScenario: "empty",
}),
serviceLane(
"cron-mcp-cleanup",
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup",
{ resources: ["npm"], weight: 3 },
{ resources: ["npm"], stateScenario: "empty", weight: 3 },
),
npmLane("doctor-switch", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:doctor-switch", {
stateScenario: "empty",
@@ -291,8 +298,12 @@ export const mainLanes = [
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:bundled-channel-deps:fast",
{ resources: ["service"], stateScenario: "empty", weight: 3 },
),
npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update"),
serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload"),
npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update", {
stateScenario: "empty",
}),
serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload", {
stateScenario: "empty",
}),
...bundledScenarioLanes,
lane("openai-image-auth", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-image-auth", {
stateScenario: "empty",
@@ -300,6 +311,7 @@ export const mainLanes = [
lane(
"crestodian-first-run",
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-first-run",
{ stateScenario: "empty" },
),
lane(
"session-runtime-context",
@@ -424,6 +436,7 @@ const releasePathPluginRuntimeLanes = [
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup",
{
resources: ["npm"],
stateScenario: "empty",
weight: 3,
},
),
@@ -448,6 +461,7 @@ const releasePathPluginRuntimeServiceLanes = [
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup",
{
resources: ["npm"],
stateScenario: "empty",
weight: 3,
},
),
@@ -464,7 +478,9 @@ const releasePathPluginRuntimeCoreLanes = [
];
const releasePathBundledChannelLanes = [
npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update"),
npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update", {
stateScenario: "empty",
}),
...bundledScenarioLanes,
];
@@ -519,7 +535,9 @@ const primaryReleasePathChunks = {
weight: 2,
}),
serviceLane("gateway-network", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:gateway-network"),
serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload"),
serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload", {
stateScenario: "empty",
}),
lane(
"session-runtime-context",
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:session-runtime-context",
@@ -527,9 +545,11 @@ const primaryReleasePathChunks = {
lane(
"pi-bundle-mcp-tools",
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:pi-bundle-mcp-tools",
{ stateScenario: "empty" },
),
serviceLane("mcp-channels", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels", {
resources: ["npm"],
stateScenario: "empty",
weight: 3,
}),
],