diff --git a/scripts/e2e/doctor-install-switch-docker.sh b/scripts/e2e/doctor-install-switch-docker.sh index f2cebdbe0f6f..50915187a278 100755 --- a/scripts/e2e/doctor-install-switch-docker.sh +++ b/scripts/e2e/doctor-install-switch-docker.sh @@ -7,6 +7,11 @@ 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)" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" +} +trap cleanup EXIT + 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" diff --git a/scripts/e2e/multi-node-update-docker.sh b/scripts/e2e/multi-node-update-docker.sh index 70bc89296383..5ba15dab29fc 100755 --- a/scripts/e2e/multi-node-update-docker.sh +++ b/scripts/e2e/multi-node-update-docker.sh @@ -26,6 +26,10 @@ ARTIFACT_DIR="${OPENCLAW_MULTI_NODE_ARTIFACT_DIR:-$ROOT_DIR/.artifacts/multi-nod mkdir -p "$ARTIFACT_DIR" chmod -R a+rwX "$ARTIFACT_DIR" || true +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" +} +trap cleanup EXIT # Build the bare e2e image and prepare the package tarball. docker_e2e_build_or_reuse "$IMAGE_NAME" multi-node-update "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "bare" "${OPENCLAW_SKIP_DOCKER_BUILD:-0}" diff --git a/scripts/e2e/plugin-lifecycle-matrix-docker.sh b/scripts/e2e/plugin-lifecycle-matrix-docker.sh index 536d2e97ad67..1d15c0dd033b 100644 --- a/scripts/e2e/plugin-lifecycle-matrix-docker.sh +++ b/scripts/e2e/plugin-lifecycle-matrix-docker.sh @@ -8,6 +8,11 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh" IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-plugin-lifecycle-matrix-e2e" OPENCLAW_PLUGIN_LIFECYCLE_MATRIX_E2E_IMAGE)" SKIP_BUILD="${OPENCLAW_PLUGIN_LIFECYCLE_MATRIX_E2E_SKIP_BUILD:-0}" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" +} +trap cleanup EXIT + PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz plugin-lifecycle-matrix "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")" docker_e2e_package_mount_args "$PACKAGE_TGZ" diff --git a/scripts/e2e/plugin-update-unchanged-docker.sh b/scripts/e2e/plugin-update-unchanged-docker.sh index 9dec2ed44755..b88b5a239b70 100755 --- a/scripts/e2e/plugin-update-unchanged-docker.sh +++ b/scripts/e2e/plugin-update-unchanged-docker.sh @@ -9,6 +9,11 @@ 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}" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" +} +trap cleanup EXIT + 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" diff --git a/scripts/e2e/release-media-memory-docker.sh b/scripts/e2e/release-media-memory-docker.sh index 23aec14fc897..1cf471c2ae22 100755 --- a/scripts/e2e/release-media-memory-docker.sh +++ b/scripts/e2e/release-media-memory-docker.sh @@ -8,6 +8,15 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh" IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-release-media-memory-e2e" OPENCLAW_RELEASE_MEDIA_MEMORY_E2E_IMAGE)" SKIP_BUILD="${OPENCLAW_RELEASE_MEDIA_MEMORY_E2E_SKIP_BUILD:-0}" +run_log="" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" + if [ -n "${run_log:-}" ]; then + rm -f "$run_log" + fi +} +trap cleanup EXIT + PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz release-media-memory "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")" docker_e2e_package_mount_args "$PACKAGE_TGZ" @@ -22,9 +31,7 @@ if ! docker_e2e_run_with_harness \ "${DOCKER_E2E_PACKAGE_ARGS[@]}" \ -i "$IMAGE_NAME" bash scripts/e2e/lib/release-media-memory/scenario.sh >"$run_log" 2>&1; then docker_e2e_print_log "$run_log" - rm -f "$run_log" exit 1 fi -rm -f "$run_log" echo "Release media memory Docker E2E passed." diff --git a/scripts/e2e/release-plugin-marketplace-docker.sh b/scripts/e2e/release-plugin-marketplace-docker.sh index da6de364e769..ec4a7f0a3f57 100755 --- a/scripts/e2e/release-plugin-marketplace-docker.sh +++ b/scripts/e2e/release-plugin-marketplace-docker.sh @@ -8,6 +8,15 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh" IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-release-plugin-marketplace-e2e" OPENCLAW_RELEASE_PLUGIN_MARKETPLACE_E2E_IMAGE)" SKIP_BUILD="${OPENCLAW_RELEASE_PLUGIN_MARKETPLACE_E2E_SKIP_BUILD:-0}" +run_log="" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" + if [ -n "${run_log:-}" ]; then + rm -f "$run_log" + fi +} +trap cleanup EXIT + PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz release-plugin-marketplace "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")" docker_e2e_package_mount_args "$PACKAGE_TGZ" @@ -22,9 +31,7 @@ if ! docker_e2e_run_with_harness \ "${DOCKER_E2E_PACKAGE_ARGS[@]}" \ -i "$IMAGE_NAME" bash scripts/e2e/lib/release-plugin-marketplace/scenario.sh >"$run_log" 2>&1; then docker_e2e_print_log "$run_log" - rm -f "$run_log" exit 1 fi -rm -f "$run_log" echo "Release plugin marketplace Docker E2E passed." diff --git a/scripts/e2e/release-typed-onboarding-docker.sh b/scripts/e2e/release-typed-onboarding-docker.sh index 7085238a9f4e..f856517480f3 100755 --- a/scripts/e2e/release-typed-onboarding-docker.sh +++ b/scripts/e2e/release-typed-onboarding-docker.sh @@ -8,6 +8,15 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh" IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-release-typed-onboarding-e2e" OPENCLAW_RELEASE_TYPED_ONBOARDING_E2E_IMAGE)" SKIP_BUILD="${OPENCLAW_RELEASE_TYPED_ONBOARDING_E2E_SKIP_BUILD:-0}" +run_log="" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" + if [ -n "${run_log:-}" ]; then + rm -f "$run_log" + fi +} +trap cleanup EXIT + PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz release-typed-onboarding "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")" docker_e2e_package_mount_args "$PACKAGE_TGZ" @@ -22,9 +31,7 @@ if ! docker_e2e_run_with_harness \ "${DOCKER_E2E_PACKAGE_ARGS[@]}" \ -i "$IMAGE_NAME" bash scripts/e2e/lib/release-typed-onboarding/scenario.sh >"$run_log" 2>&1; then docker_e2e_print_log "$run_log" - rm -f "$run_log" exit 1 fi -rm -f "$run_log" echo "Release typed onboarding Docker E2E passed." diff --git a/scripts/e2e/release-upgrade-user-journey-docker.sh b/scripts/e2e/release-upgrade-user-journey-docker.sh index 525709606d6a..0254ec4a9cd3 100755 --- a/scripts/e2e/release-upgrade-user-journey-docker.sh +++ b/scripts/e2e/release-upgrade-user-journey-docker.sh @@ -8,6 +8,15 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh" IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-release-upgrade-user-journey-e2e" OPENCLAW_RELEASE_UPGRADE_USER_JOURNEY_E2E_IMAGE)" SKIP_BUILD="${OPENCLAW_RELEASE_UPGRADE_USER_JOURNEY_E2E_SKIP_BUILD:-0}" +run_log="" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" + if [ -n "${run_log:-}" ]; then + rm -f "$run_log" + fi +} +trap cleanup EXIT + PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz release-upgrade-user-journey "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")" docker_e2e_package_mount_args "$PACKAGE_TGZ" @@ -23,9 +32,7 @@ if ! docker_e2e_run_with_harness \ "${DOCKER_E2E_PACKAGE_ARGS[@]}" \ -i "$IMAGE_NAME" bash scripts/e2e/lib/release-upgrade-user-journey/scenario.sh >"$run_log" 2>&1; then docker_e2e_print_log "$run_log" - rm -f "$run_log" exit 1 fi -rm -f "$run_log" echo "Release upgrade user journey Docker E2E passed." diff --git a/scripts/e2e/release-user-journey-docker.sh b/scripts/e2e/release-user-journey-docker.sh index c43d15ce7855..cc11b4502d74 100755 --- a/scripts/e2e/release-user-journey-docker.sh +++ b/scripts/e2e/release-user-journey-docker.sh @@ -9,6 +9,15 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh" IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-release-user-journey-e2e" OPENCLAW_RELEASE_USER_JOURNEY_E2E_IMAGE)" SKIP_BUILD="${OPENCLAW_RELEASE_USER_JOURNEY_E2E_SKIP_BUILD:-0}" +run_log="" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" + if [ -n "${run_log:-}" ]; then + rm -f "$run_log" + fi +} +trap cleanup EXIT + PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz release-user-journey "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")" docker_e2e_package_mount_args "$PACKAGE_TGZ" @@ -23,9 +32,7 @@ if ! docker_e2e_run_with_harness \ "${DOCKER_E2E_PACKAGE_ARGS[@]}" \ -i "$IMAGE_NAME" bash scripts/e2e/lib/release-user-journey/scenario.sh >"$run_log" 2>&1; then docker_e2e_print_log "$run_log" - rm -f "$run_log" exit 1 fi -rm -f "$run_log" echo "Release user journey Docker E2E passed." diff --git a/scripts/e2e/skill-install-docker.sh b/scripts/e2e/skill-install-docker.sh index ce639b5c24bf..02dc6dcf4218 100644 --- a/scripts/e2e/skill-install-docker.sh +++ b/scripts/e2e/skill-install-docker.sh @@ -8,6 +8,11 @@ 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-skill-install-e2e" OPENCLAW_SKILL_INSTALL_E2E_IMAGE)" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" +} +trap cleanup EXIT + PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz skill-install "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")" OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 skill-install empty)" diff --git a/scripts/e2e/update-channel-switch-docker.sh b/scripts/e2e/update-channel-switch-docker.sh index 2d66596d8d90..95711b89496a 100755 --- a/scripts/e2e/update-channel-switch-docker.sh +++ b/scripts/e2e/update-channel-switch-docker.sh @@ -9,6 +9,11 @@ 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}" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" +} +trap cleanup EXIT + 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" diff --git a/scripts/e2e/update-corrupt-plugin-docker.sh b/scripts/e2e/update-corrupt-plugin-docker.sh index 56ba9f965a05..6fbf604b4006 100644 --- a/scripts/e2e/update-corrupt-plugin-docker.sh +++ b/scripts/e2e/update-corrupt-plugin-docker.sh @@ -10,6 +10,11 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-package.sh" IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-update-corrupt-plugin-e2e" OPENCLAW_UPDATE_CORRUPT_PLUGIN_E2E_IMAGE)" SKIP_BUILD="${OPENCLAW_UPDATE_CORRUPT_PLUGIN_E2E_SKIP_BUILD:-0}" +cleanup() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" +} +trap cleanup EXIT + PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz update-corrupt-plugin "${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" diff --git a/scripts/e2e/upgrade-survivor-docker.sh b/scripts/e2e/upgrade-survivor-docker.sh index 19a5ac9c7325..955d36c6f68c 100755 --- a/scripts/e2e/upgrade-survivor-docker.sh +++ b/scripts/e2e/upgrade-survivor-docker.sh @@ -19,6 +19,10 @@ LANE_ARTIFACT_SUFFIX="${LANE_ARTIFACT_SUFFIX//[^A-Za-z0-9_.-]/_}" ARTIFACT_DIR="${OPENCLAW_UPGRADE_SURVIVOR_ARTIFACT_DIR:-$ROOT_DIR/.artifacts/upgrade-survivor/$LANE_ARTIFACT_SUFFIX}" ROOT_MANAGED_VPS="${OPENCLAW_UPGRADE_SURVIVOR_ROOT_MANAGED_VPS:-0}" DOCKER_RUN_USER_ARGS=() +cleanup_outer() { + docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" +} +trap cleanup_outer EXIT if [ "$ROOT_MANAGED_VPS" = "1" ]; then if [ "${OPENCLAW_UPGRADE_SURVIVOR_PUBLISHED_BASELINE:-0}" != "1" ]; then diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index 8c3796b1b30f..a5b01c52e29e 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -1,5 +1,5 @@ import { execFileSync } from "node:child_process"; -import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { mkdtempSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; @@ -71,6 +71,14 @@ const CENTRALIZED_BUILD_SCRIPTS = [ "scripts/test-live-build-docker.sh", ] as const; +function packageBackedDockerRunnerPaths(): string[] { + return readdirSync("scripts/e2e") + .filter((entry) => entry.endsWith("-docker.sh")) + .map((entry) => join("scripts/e2e", entry)) + .filter((path) => readFileSync(path, "utf8").includes("docker_e2e_prepare_package_tgz")) + .sort(); +} + function shellQuote(value: string): string { return `'${value.replace(/'/gu, `'\\''`)}'`; } @@ -432,6 +440,21 @@ test -f "$TMPDIR/docker-cmd-seen" } }); + it("cleans every prepared Docker package tarball on every runner exit path", () => { + const paths = packageBackedDockerRunnerPaths(); + + expect(paths.length).toBeGreaterThan(0); + for (const path of paths) { + const runner = readFileSync(path, "utf8"); + + expect(runner, path).toMatch( + /docker_e2e_cleanup_package_tgz "\$\{PACKAGE_TGZ:-\}"|docker_e2e_cleanup_package_tgz "\$PACKAGE_TGZ"/u, + ); + expect(runner, path).toMatch(/trap cleanup(?:_outer)? EXIT/u); + expect(runner, path).not.toContain('rm -f "$run_log"\n exit 1'); + } + }); + it("runs skill install through the package-cleaning Docker harness", () => { const runner = readFileSync(SKILL_INSTALL_DOCKER_E2E_PATH, "utf8");