From c110f8c028389c4fe3d75f9ac300e99434251ea5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 27 Apr 2026 07:37:26 +0100 Subject: [PATCH] fix(docker): stabilize bundled channel release lanes --- scripts/e2e/lib/bundled-channel/channel.sh | 68 +++++++++++++--------- src/plugins/bundled-runtime-deps.test.ts | 21 +++++++ src/plugins/bundled-runtime-deps.ts | 10 ++++ 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/scripts/e2e/lib/bundled-channel/channel.sh b/scripts/e2e/lib/bundled-channel/channel.sh index a9c1e7a3a53..bea81fe4061 100644 --- a/scripts/e2e/lib/bundled-channel/channel.sh +++ b/scripts/e2e/lib/bundled-channel/channel.sh @@ -271,33 +271,9 @@ wait_for_gateway_health() { return 1 } -assert_channel_status() { - local channel="$1" - if [ "$channel" = "memory-lancedb" ]; then - echo "memory-lancedb plugin activation verified by dependency sentinel" - return 0 - fi - local out="/tmp/openclaw-channel-status-$channel.json" - local err="/tmp/openclaw-channel-status-$channel.err" - for _ in $(seq 1 12); do - if openclaw gateway call channels.status \ - --url "ws://127.0.0.1:$PORT" \ - --token "$TOKEN" \ - --timeout 10000 \ - --json \ - --params '{"probe":false}' >"$out" 2>"$err"; then - break - fi - sleep 2 - done - if [ ! -s "$out" ]; then - if grep -Eq "\\[gateway\\] ready \\(.*\\b$channel\\b" /tmp/openclaw-"$channel"-*.log 2>/dev/null; then - echo "$channel channel plugin visible in gateway ready log" - return 0 - fi - cat "$err" >&2 || true - return 1 - fi +parse_channel_status_json() { + local out="$1" + local channel="$2" node - <<'NODE' "$out" "$channel" const fs = require("node:fs"); const raw = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); @@ -321,6 +297,44 @@ console.log(`${channel} channel plugin visible`); NODE } +assert_channel_status() { + local channel="$1" + if [ "$channel" = "memory-lancedb" ]; then + echo "memory-lancedb plugin activation verified by dependency sentinel" + return 0 + fi + local out="/tmp/openclaw-channel-status-$channel.json" + local err="/tmp/openclaw-channel-status-$channel.err" + local parse_err="/tmp/openclaw-channel-status-$channel.parse.err" + local parse_out="/tmp/openclaw-channel-status-$channel.parse.out" + for _ in $(seq 1 30); do + if openclaw gateway call channels.status \ + --url "ws://127.0.0.1:$PORT" \ + --token "$TOKEN" \ + --timeout 10000 \ + --json \ + --params '{"probe":false}' >"$out" 2>"$err"; then + if parse_channel_status_json "$out" "$channel" >"$parse_out" 2>"$parse_err"; then + cat "$parse_out" + return 0 + fi + fi + if grep -Eq "\\[gateway\\] ready \\(.*\\b$channel\\b" /tmp/openclaw-"$channel"-*.log 2>/dev/null; then + echo "$channel channel plugin visible in gateway ready log" + return 0 + fi + sleep 2 + done + if [ ! -s "$out" ]; then + cat "$err" >&2 || true + else + cat "$parse_err" >&2 || true + cat "$out" >&2 || true + fi + cat /tmp/openclaw-"$channel"-*.log >&2 2>/dev/null || true + return 1 +} + assert_installed_once() { local log_file="$1" local channel="$2" diff --git a/src/plugins/bundled-runtime-deps.test.ts b/src/plugins/bundled-runtime-deps.test.ts index e4797a6f76a..360a2978786 100644 --- a/src/plugins/bundled-runtime-deps.test.ts +++ b/src/plugins/bundled-runtime-deps.test.ts @@ -17,6 +17,7 @@ import { ensureBundledPluginRuntimeDeps, installBundledRuntimeDeps, isWritableDirectory, + materializeBundledRuntimeMirrorDistFile, repairBundledRuntimeDepsInstallRootAsync, resolveBundledRuntimeDependencyInstallRoot, resolveBundledRuntimeDepsNpmRunner, @@ -219,6 +220,26 @@ describe("resolveBundledRuntimeDepsNpmRunner", () => { }); describe("installBundledRuntimeDeps", () => { + it("keeps already-materialized mirror chunks when source and target match", () => { + const tempDir = makeTempDir(); + const chunkPath = path.join(tempDir, "dist", "accounts.js"); + fs.mkdirSync(path.dirname(chunkPath), { recursive: true }); + fs.writeFileSync( + chunkPath, + [ + `//#region extensions/slack/src/accounts.ts`, + `export const marker = "same-file";`, + `//#endregion`, + "", + ].join("\n"), + "utf8", + ); + + materializeBundledRuntimeMirrorDistFile(chunkPath, chunkPath); + + expect(fs.readFileSync(chunkPath, "utf8")).toContain("same-file"); + }); + it("uses a real write probe for runtime dependency roots", () => { const accessSpy = vi.spyOn(fs, "accessSync").mockImplementation(() => undefined); const mkdirSpy = vi.spyOn(fs, "mkdtempSync").mockImplementation(() => { diff --git a/src/plugins/bundled-runtime-deps.ts b/src/plugins/bundled-runtime-deps.ts index 101bf068e82..d3ba071d0f6 100644 --- a/src/plugins/bundled-runtime-deps.ts +++ b/src/plugins/bundled-runtime-deps.ts @@ -87,6 +87,16 @@ export function materializeBundledRuntimeMirrorDistFile( sourcePath: string, targetPath: string, ): void { + if (path.resolve(sourcePath) === path.resolve(targetPath)) { + return; + } + try { + if (fs.realpathSync(sourcePath) === fs.realpathSync(targetPath)) { + return; + } + } catch { + // Missing targets are expected before the mirror file is materialized. + } fs.mkdirSync(path.dirname(targetPath), { recursive: true, mode: 0o755 }); fs.rmSync(targetPath, { recursive: true, force: true }); try {