From 890e299e30ed947f3f0cba7e0011b990aa0489d1 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 15 Apr 2026 12:12:51 +0100 Subject: [PATCH] fix(ci): align docker smoke cache tests and reuse built dist --- .github/workflows/install-smoke.yml | 2 ++ scripts/test-install-sh-docker.sh | 19 ++++++++++- src/docker-build-cache.test.ts | 2 +- src/infra/tsdown-config.test.ts | 37 +++++++++++---------- test/scripts/test-install-sh-docker.test.ts | 9 +++++ 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/.github/workflows/install-smoke.yml b/.github/workflows/install-smoke.yml index 3dcfeb7c2d9..bb4b9c3f423 100644 --- a/.github/workflows/install-smoke.yml +++ b/.github/workflows/install-smoke.yml @@ -211,4 +211,6 @@ jobs: OPENCLAW_INSTALL_NONROOT_SKIP_IMAGE_BUILD: ${{ github.event_name == 'pull_request' && '0' || '1' }} OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }} OPENCLAW_INSTALL_SMOKE_SKIP_PREVIOUS: "1" + OPENCLAW_INSTALL_SMOKE_UPDATE_DIST_IMAGE: openclaw-dockerfile-smoke:local + OPENCLAW_INSTALL_SMOKE_UPDATE_SKIP_LOCAL_BUILD: "1" run: bash scripts/test-install-sh-docker.sh diff --git a/scripts/test-install-sh-docker.sh b/scripts/test-install-sh-docker.sh index c4df79937d7..5afc52c934d 100755 --- a/scripts/test-install-sh-docker.sh +++ b/scripts/test-install-sh-docker.sh @@ -150,6 +150,7 @@ SKIP_UPDATE="${OPENCLAW_INSTALL_SMOKE_SKIP_UPDATE:-0}" SKIP_NPM_GLOBAL="${OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL:-0}" UPDATE_BASELINE_VERSION="${OPENCLAW_INSTALL_SMOKE_UPDATE_BASELINE:-2026.4.10}" UPDATE_PACKAGE_SPEC="${OPENCLAW_INSTALL_SMOKE_UPDATE_PACKAGE_SPEC:-}" +UPDATE_DIST_IMAGE="${OPENCLAW_INSTALL_SMOKE_UPDATE_DIST_IMAGE:-}" UPDATE_SKIP_LOCAL_BUILD="${OPENCLAW_INSTALL_SMOKE_UPDATE_SKIP_LOCAL_BUILD:-0}" UPDATE_HOST_ALIAS="${OPENCLAW_INSTALL_SMOKE_UPDATE_HOST:-host.docker.internal}" UPDATE_PORT="${OPENCLAW_INSTALL_SMOKE_UPDATE_PORT:-}" @@ -191,6 +192,20 @@ allocate_host_port() { ' } +restore_local_dist_from_image() { + local image="$1" + local container_id="" + + echo "==> Reuse local dist/ from Docker image: $image" + container_id="$(docker create "$image")" + rm -rf "$ROOT_DIR/dist" + if ! docker cp "${container_id}:/app/dist" "$ROOT_DIR/dist"; then + docker rm -f "$container_id" >/dev/null 2>&1 || true + return 1 + fi + docker rm -f "$container_id" >/dev/null +} + prepare_update_tarball() { local pack_json local baseline_pack_json @@ -204,7 +219,9 @@ prepare_update_tarball() { quiet_npm pack "$UPDATE_PACKAGE_SPEC" --json --pack-destination "$UPDATE_DIR" >"$pack_json_file" else echo "==> Build local release artifacts for update smoke" - if [[ "$UPDATE_SKIP_LOCAL_BUILD" != "1" ]]; then + if [[ -n "$UPDATE_DIST_IMAGE" ]]; then + restore_local_dist_from_image "$UPDATE_DIST_IMAGE" + elif [[ "$UPDATE_SKIP_LOCAL_BUILD" != "1" ]]; then pnpm build pnpm ui:build fi diff --git a/src/docker-build-cache.test.ts b/src/docker-build-cache.test.ts index 1fede0ae237..19e6c83d02a 100644 --- a/src/docker-build-cache.test.ts +++ b/src/docker-build-cache.test.ts @@ -111,7 +111,7 @@ describe("docker build cache layout", () => { expectPatternBeforeInstall(/^COPY(?:\s+--chown=\S+)?\s+extensions \.\/extensions$/m); expectPatternBeforeInstall(/^COPY(?:\s+--chown=\S+)?\s+patches \.\/patches$/m); expectPatternBeforeInstall( - /^COPY(?:\s+--chown=\S+)?\s+scripts\/postinstall-bundled-plugins\.mjs scripts\/npm-runner\.mjs scripts\/windows-cmd-helpers\.mjs \.\/scripts\/$/m, + /^COPY(?:\s+--chown=\S+)?\s+scripts\/postinstall-bundled-plugins\.mjs scripts\/preinstall-package-manager-warning\.mjs scripts\/npm-runner\.mjs scripts\/windows-cmd-helpers\.mjs \.\/scripts\/$/m, ); expectPatternAfterInstall( /^COPY(?:\s+--chown=\S+)?\s+tsconfig\.json tsconfig\.plugin-sdk\.dts\.json tsdown\.config\.ts vitest\.config\.ts openclaw\.mjs \.\/$/m, diff --git a/src/infra/tsdown-config.test.ts b/src/infra/tsdown-config.test.ts index 6f3e88fe477..c955400bbf8 100644 --- a/src/infra/tsdown-config.test.ts +++ b/src/infra/tsdown-config.test.ts @@ -46,25 +46,17 @@ function bundledEntry(pluginId: string): string { } function unifiedDistGraph(): TsdownConfigEntry | undefined { - return asConfigArray(tsdownConfig).find((config) => entryKeys(config).includes("index")); + return asConfigArray(tsdownConfig).find((config) => + entryKeys(config).includes("plugins/runtime/index"), + ); } describe("tsdown config", () => { - it("keeps core, plugin runtime, plugin-sdk, bundled plugins, and bundled hooks in one dist graph", () => { - const configs = asConfigArray(tsdownConfig); - const distGraphs = configs.filter((config) => { - const keys = entryKeys(config); - return ( - keys.includes("index") || - keys.includes("plugins/runtime/index") || - keys.includes("plugin-sdk/index") || - keys.includes(bundledEntry("openai")) || - keys.includes("bundled/boot-md/handler") - ); - }); + it("keeps core, plugin runtime, plugin-sdk, bundled root plugins, and bundled hooks in one dist graph", () => { + const distGraph = unifiedDistGraph(); - expect(distGraphs).toHaveLength(1); - expect(entryKeys(distGraphs[0])).toEqual( + expect(distGraph).toBeDefined(); + expect(entryKeys(distGraph as TsdownConfigEntry)).toEqual( expect.arrayContaining([ "agents/auth-profiles.runtime", "agents/model-catalog.runtime", @@ -79,14 +71,25 @@ describe("tsdown config", () => { "plugin-sdk/compat", "plugin-sdk/index", bundledEntry("openai"), - bundledEntry("matrix"), bundledEntry("msteams"), - bundledEntry("whatsapp"), "bundled/boot-md/handler", ]), ); }); + it("emits staged bundled plugins as separate extension graphs", () => { + const stagedGraphs = asConfigArray(tsdownConfig).filter( + (config) => typeof config.outDir === "string" && config.outDir.startsWith("dist/extensions/"), + ); + + expect(stagedGraphs.length).toBeGreaterThan(0); + expect(stagedGraphs.every((config) => entryKeys(config).includes("index"))).toBe(true); + expect(stagedGraphs.every((config) => !entryKeys(config).includes("plugin-sdk/index"))).toBe( + true, + ); + expect(stagedGraphs.some((config) => config.outDir === "dist/extensions/discord")).toBe(true); + }); + it("does not emit plugin-sdk or hooks from a separate dist graph", () => { const configs = asConfigArray(tsdownConfig); diff --git a/test/scripts/test-install-sh-docker.test.ts b/test/scripts/test-install-sh-docker.test.ts index 50a8f8ba2ea..0c04ca6b790 100644 --- a/test/scripts/test-install-sh-docker.test.ts +++ b/test/scripts/test-install-sh-docker.test.ts @@ -27,6 +27,15 @@ describe("test-install-sh-docker", () => { ); }); + it("can reuse dist from the already-built root Docker smoke image", () => { + const script = readFileSync(SCRIPT_PATH, "utf8"); + + expect(script).toContain('UPDATE_DIST_IMAGE="${OPENCLAW_INSTALL_SMOKE_UPDATE_DIST_IMAGE:-}"'); + expect(script).toContain("restore_local_dist_from_image"); + expect(script).toContain('docker cp "${container_id}:/app/dist" "$ROOT_DIR/dist"'); + expect(script).toContain('echo "==> Reuse local dist/ from Docker image: $image"'); + }); + it("prints package size audits for release smoke tarballs", () => { const script = readFileSync(SCRIPT_PATH, "utf8");