diff --git a/scripts/e2e/gateway-network-docker.sh b/scripts/e2e/gateway-network-docker.sh index d3c303abdea1..0b51bbfd0479 100644 --- a/scripts/e2e/gateway-network-docker.sh +++ b/scripts/e2e/gateway-network-docker.sh @@ -45,7 +45,7 @@ run_suspension_phase() { -e "GW_MODE=suspension-$stage-restart" \ -e "GW_STATE_PATH=$SUSPENSION_STATE_PATH" \ "$GW_NAME" \ - node --import tsx scripts/e2e/lib/gateway-network/client.mts + node scripts/e2e/lib/gateway-network/client.mts } trap cleanup EXIT @@ -84,7 +84,7 @@ DOCKER_COMMAND_TIMEOUT="$CLIENT_TIMEOUT" run_logged gateway-network-client docke -e "GW_URL=ws://$GW_NAME:$PORT" \ -e "GW_TOKEN=$TOKEN" \ "$IMAGE_NAME" \ - node --import tsx scripts/e2e/lib/gateway-network/client.mts + node scripts/e2e/lib/gateway-network/client.mts phase_started="$SECONDS" echo "Running cooperative suspension lifecycle before container stop..." diff --git a/scripts/e2e/kitchen-sink-rpc-docker.sh b/scripts/e2e/kitchen-sink-rpc-docker.sh index fdb3308849f7..41ce7ecc20a6 100755 --- a/scripts/e2e/kitchen-sink-rpc-docker.sh +++ b/scripts/e2e/kitchen-sink-rpc-docker.sh @@ -52,7 +52,7 @@ echo "Running kitchen-sink RPC Docker E2E..." docker_e2e_docker_cmd rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true docker_e2e_harness_mount_args DOCKER_COMMAND_TIMEOUT="$DOCKER_RUN_TIMEOUT" docker_e2e_docker_run_cmd run --name "$CONTAINER_NAME" "${DOCKER_E2E_HARNESS_ARGS[@]}" "${DOCKER_ENV_ARGS[@]}" -i "$IMAGE_NAME" \ - node --import tsx scripts/e2e/kitchen-sink-rpc-walk.mts >"$RUN_LOG" 2>&1 & + bash -lc "source scripts/lib/openclaw-e2e-instance.sh; openclaw_e2e_run_script_entrypoint scripts/e2e/kitchen-sink-rpc-walk" >"$RUN_LOG" 2>&1 & docker_pid="$!" docker_e2e_sample_stats_until_exit \ diff --git a/scripts/e2e/lib/release-upgrade-user-journey/scenario.sh b/scripts/e2e/lib/release-upgrade-user-journey/scenario.sh index 078313af2b60..0929bfea4b40 100755 --- a/scripts/e2e/lib/release-upgrade-user-journey/scenario.sh +++ b/scripts/e2e/lib/release-upgrade-user-journey/scenario.sh @@ -50,7 +50,11 @@ candidate_version="$( if [ -n "${OPENCLAW_RELEASE_UPGRADE_BASELINE_SPEC:-}" ]; then BASELINE_SPEC="$OPENCLAW_RELEASE_UPGRADE_BASELINE_SPEC" else - BASELINE_SPEC="$(node --import tsx scripts/lib/release-upgrade-baseline.mts --candidate-version "$candidate_version")" + BASELINE_SPEC="$( + openclaw_e2e_run_script_entrypoint \ + scripts/lib/release-upgrade-baseline \ + --candidate-version "$candidate_version" + )" fi mock_pid="" diff --git a/scripts/lib/docker-e2e-image.sh b/scripts/lib/docker-e2e-image.sh index e424054c617b..59beeb1f862d 100644 --- a/scripts/lib/docker-e2e-image.sh +++ b/scripts/lib/docker-e2e-image.sh @@ -167,11 +167,7 @@ docker_e2e_test_state_entrypoint() { docker_e2e_run_test_state() { local entrypoint entrypoint="$(docker_e2e_test_state_entrypoint)" || return - if [[ "$entrypoint" == *.mts ]]; then - node --import tsx "$entrypoint" "$@" - else - node "$entrypoint" "$@" - fi + node "$entrypoint" "$@" } docker_e2e_test_state_shell_b64() { diff --git a/scripts/lib/docker-e2e-package.sh b/scripts/lib/docker-e2e-package.sh index 205c1fbe1bdf..7ef4808232d8 100644 --- a/scripts/lib/docker-e2e-package.sh +++ b/scripts/lib/docker-e2e-package.sh @@ -268,7 +268,10 @@ docker_e2e_harness_mount_args() { DOCKER_E2E_HARNESS_ARGS=( -v "$harness_root/scripts/e2e:/app/scripts/e2e:ro" -v "$harness_root/scripts/lib:/app/scripts/lib:ro" + -v "$harness_root/packages/gateway-client/src:/app/packages/gateway-client/src:ro" + -v "$harness_root/packages/normalization-core/package.json:/app/packages/normalization-core/package.json:ro" -v "$harness_root/packages/normalization-core/src:/app/packages/normalization-core/src:ro" + -v "$harness_root/tsconfig.json:/app/tsconfig.json:ro" -v "$harness_root/test/e2e/qa-lab:/app/test/e2e/qa-lab:ro" -v "$harness_root/test/helpers:/app/test/helpers:ro" -v "$harness_root/scripts/windows-cmd-helpers.mjs:/app/scripts/windows-cmd-helpers.mjs:ro" diff --git a/scripts/lib/openclaw-e2e-instance.sh b/scripts/lib/openclaw-e2e-instance.sh index 1584e482a1bf..e6bde807e7be 100644 --- a/scripts/lib/openclaw-e2e-instance.sh +++ b/scripts/lib/openclaw-e2e-instance.sh @@ -47,6 +47,20 @@ openclaw_e2e_resolve_entrypoint() { echo "OpenClaw entrypoint not found under dist/" >&2 return 1 } +openclaw_e2e_run_script_entrypoint() { + local stem="${1:?missing OpenClaw E2E script stem}" + shift + if [ -f "$stem.mts" ]; then + tsx "$stem.mts" "$@" + return + fi + if [ -f "$stem.mjs" ]; then + node "$stem.mjs" "$@" + return + fi + echo "OpenClaw E2E script entrypoint not found: $stem.{mts,mjs}" >&2 + return 1 +} openclaw_e2e_package_root() { local prefix="${1:-}" if [ -n "$prefix" ]; then diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index 6e14d02fda28..bc19403a361f 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -14,6 +14,7 @@ const DOCKER_ALL_SCHEDULER_PATH = "scripts/test-docker-all.mts"; const DOCKER_E2E_PACKAGE_HELPER_PATH = "scripts/lib/docker-e2e-package.sh"; const DOCKER_E2E_IMAGE_HELPER_PATH = "scripts/lib/docker-e2e-image.sh"; const DOCKER_E2E_SCENARIOS_PATH = "scripts/lib/docker-e2e-scenarios.mts"; +const OPENCLAW_E2E_INSTANCE_HELPER_PATH = "scripts/lib/openclaw-e2e-instance.sh"; const COMPOSE_SETUP_E2E_PATH = "scripts/e2e/compose-setup.sh"; const CLI_INSTALLER_DISTRIBUTION_E2E_PATH = "scripts/e2e/cli-installer-distribution-docker.sh"; const DOCKER_PACKAGE_INSTALL_E2E_PATH = "scripts/e2e/docker-package-install.sh"; @@ -567,6 +568,72 @@ print_log_tail "$LOG_PATH" expect(compiledResult.stdout.trim()).toBe(compiledEntrypoint); }); + it("runs current TypeScript and frozen JavaScript Docker harness entrypoints", () => { + const fixtureRoot = tempDirs.make("openclaw-docker-script-entrypoint-"); + const scriptStem = join(fixtureRoot, "fixture"); + const runFixture = (value: string) => + spawnSync( + "bash", + [ + "-c", + `source "${OPENCLAW_E2E_INSTANCE_HELPER_PATH}"; openclaw_e2e_run_script_entrypoint "$1" "$2"`, + "openclaw-docker-script-entrypoint", + scriptStem, + value, + ], + { + cwd: process.cwd(), + encoding: "utf8", + env: { + ...process.env, + PATH: `${join(process.cwd(), "node_modules/.bin")}:${process.env.PATH ?? ""}`, + }, + }, + ); + + writeFileSync( + `${scriptStem}.mts`, + 'enum Choice { Current = "current" }\nconst value: Choice = process.argv[2] as Choice; process.stdout.write(`mts:${value}`);\n', + "utf8", + ); + const sourceResult = runFixture("current"); + expect(sourceResult.status, sourceResult.stderr).toBe(0); + expect(sourceResult.stdout).toBe("mts:current"); + + rmSync(`${scriptStem}.mts`); + writeFileSync( + `${scriptStem}.mjs`, + 'process.stdout.write(`mjs:${process.argv[2] ?? ""}`);\n', + "utf8", + ); + const compiledResult = runFixture("frozen"); + expect(compiledResult.status, compiledResult.stderr).toBe(0); + expect(compiledResult.stdout).toBe("mjs:frozen"); + + rmSync(`${scriptStem}.mjs`); + const missingResult = runFixture("missing"); + expect(missingResult.status).toBe(1); + expect(missingResult.stderr).toContain("script entrypoint not found"); + }); + + it("routes package-only Docker TypeScript entrypoints through their required runtime", () => { + const gatewayRunner = readFileSync(GATEWAY_NETWORK_DOCKER_E2E_PATH, "utf8"); + const imageHelper = readFileSync(DOCKER_E2E_IMAGE_HELPER_PATH, "utf8"); + const kitchenSinkRunner = readFileSync(KITCHEN_SINK_RPC_DOCKER_E2E_PATH, "utf8"); + const releaseUpgradeScenario = readFileSync(RELEASE_UPGRADE_USER_JOURNEY_SCENARIO_PATH, "utf8"); + expect(gatewayRunner).toContain("node scripts/e2e/lib/gateway-network/client.mts"); + expect(kitchenSinkRunner).toContain( + "openclaw_e2e_run_script_entrypoint scripts/e2e/kitchen-sink-rpc-walk", + ); + expect(releaseUpgradeScenario).toContain( + "openclaw_e2e_run_script_entrypoint \\\n scripts/lib/release-upgrade-baseline", + ); + expect(gatewayRunner).not.toContain("node --import tsx"); + expect(imageHelper).not.toContain('node --import tsx "$entrypoint"'); + expect(kitchenSinkRunner).not.toContain("node --import tsx"); + expect(releaseUpgradeScenario).not.toContain("node --import tsx"); + }); + it("rejects malformed Docker E2E resource limits before a suite starts", () => { const helper = readFileSync(DOCKER_E2E_IMAGE_HELPER_PATH, "utf8"); const scripts = [ @@ -4315,7 +4382,10 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-logs.sh" "--allow-unreleased-changelog", 'local harness_root="${DOCKER_E2E_HARNESS_ROOT_DIR:-$ROOT_DIR}"', '-v "$harness_root/scripts/windows-cmd-helpers.mjs:/app/scripts/windows-cmd-helpers.mjs:ro"', + '-v "$harness_root/packages/gateway-client/src:/app/packages/gateway-client/src:ro"', + '-v "$harness_root/packages/normalization-core/package.json:/app/packages/normalization-core/package.json:ro"', '-v "$harness_root/packages/normalization-core/src:/app/packages/normalization-core/src:ro"', + '-v "$harness_root/tsconfig.json:/app/tsconfig.json:ro"', '-v "$harness_root/test/e2e/qa-lab:/app/test/e2e/qa-lab:ro"', '-v "$harness_root/test/helpers:/app/test/helpers:ro"', ]); diff --git a/test/scripts/plugin-prerelease-test-plan.test.ts b/test/scripts/plugin-prerelease-test-plan.test.ts index 26422659baf5..b41584d22a7e 100644 --- a/test/scripts/plugin-prerelease-test-plan.test.ts +++ b/test/scripts/plugin-prerelease-test-plan.test.ts @@ -271,7 +271,9 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => { expect(script).toContain("OPENCLAW_KITCHEN_SINK_COMMAND_MAX_RSS_MIB"); expect(script).toContain("docker_e2e_sample_stats_until_exit"); expect(script).toContain("scripts/e2e/lib/docker-stats/assert-resource-ceiling.mjs"); - expect(script).toContain("node --import tsx scripts/e2e/kitchen-sink-rpc-walk.mts"); + expect(script).toContain( + "openclaw_e2e_run_script_entrypoint scripts/e2e/kitchen-sink-rpc-walk", + ); expect(walkScript).toContain("commands.list"); expect(walkScript).toContain("tools.invoke"); expect(walkScript).toContain("tts.providers");