From 4b9c85776d29b1cc81ecbdacd7feaca6a46c0941 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 27 Apr 2026 06:42:11 +0100 Subject: [PATCH] ci: allow package plugin metadata migrations --- scripts/e2e/plugin-update-unchanged-docker.sh | 73 +++++++++++++++---- test/scripts/docker-build-helper.test.ts | 10 +++ 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/scripts/e2e/plugin-update-unchanged-docker.sh b/scripts/e2e/plugin-update-unchanged-docker.sh index 430c2f32cea..e1e1b512559 100755 --- a/scripts/e2e/plugin-update-unchanged-docker.sh +++ b/scripts/e2e/plugin-update-unchanged-docker.sh @@ -141,31 +141,74 @@ if [ \"\$registry_ready\" -ne 1 ]; then exit 1 fi -before_hash=\$(node --input-type=module -e ' - import crypto from \"node:crypto\"; +node --input-type=module > /tmp/plugin-update-before.json <<'NODE' import fs from \"node:fs\"; import os from \"node:os\"; import path from \"node:path\"; - const file = path.join(os.homedir(), \".openclaw\", \"openclaw.json\"); - process.stdout.write(crypto.createHash(\"sha256\").update(fs.readFileSync(file)).digest(\"hex\")); -') + + const readJson = (file) => { + try { + return JSON.parse(fs.readFileSync(file, \"utf8\")); + } catch { + return {}; + } + }; + const home = os.homedir(); + const config = readJson(path.join(home, \".openclaw\", \"openclaw.json\")); + const index = readJson(path.join(home, \".openclaw\", \"plugins\", \"installs.json\")); + const records = index.installRecords ?? index.records ?? config.plugins?.installs ?? {}; + const record = records[\"lossless-claw\"] ?? records[\"@example/lossless-claw\"]; + if (!record) { + throw new Error(\"missing seeded plugin install record\"); + } + const snapshot = { + source: record.source, + spec: record.spec, + resolvedName: record.resolvedName, + resolvedVersion: record.resolvedVersion, + resolvedSpec: record.resolvedSpec, + integrity: record.integrity, + shasum: record.shasum + }; + process.stdout.write(JSON.stringify(snapshot, null, 2)); +NODE node \"\$entry\" plugins update @example/lossless-claw > /tmp/plugin-update-output.log 2>&1 -after_hash=\$(node --input-type=module -e ' - import crypto from \"node:crypto\"; +node --input-type=module <<'NODE' import fs from \"node:fs\"; import os from \"node:os\"; import path from \"node:path\"; - const file = path.join(os.homedir(), \".openclaw\", \"openclaw.json\"); - process.stdout.write(crypto.createHash(\"sha256\").update(fs.readFileSync(file)).digest(\"hex\")); -') -if [ \"\$before_hash\" != \"\$after_hash\" ]; then - echo \"Config changed unexpectedly\" - cat /tmp/plugin-update-output.log - exit 1 -fi + const readJson = (file) => { + try { + return JSON.parse(fs.readFileSync(file, \"utf8\")); + } catch { + return {}; + } + }; + const home = os.homedir(); + const before = readJson(\"/tmp/plugin-update-before.json\"); + const config = readJson(path.join(home, \".openclaw\", \"openclaw.json\")); + const index = readJson(path.join(home, \".openclaw\", \"plugins\", \"installs.json\")); + const records = index.installRecords ?? index.records ?? config.plugins?.installs ?? {}; + const record = records[\"lossless-claw\"] ?? records[\"@example/lossless-claw\"]; + if (!record) { + throw new Error(\"missing plugin install record after update\"); + } + const after = { + source: record.source, + spec: record.spec, + resolvedName: record.resolvedName, + resolvedVersion: record.resolvedVersion, + resolvedSpec: record.resolvedSpec, + integrity: record.integrity, + shasum: record.shasum + }; + if (JSON.stringify(before) !== JSON.stringify(after)) { + throw new Error("plugin install record changed unexpectedly: " + JSON.stringify({ before, after })); + } +NODE if grep -q 'Downloading @example/lossless-claw' /tmp/plugin-update-output.log; then echo \"Unexpected npm download/reinstall path\" cat /tmp/plugin-update-output.log diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index df34ddf23b1..f6a2d033801 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -7,6 +7,7 @@ const DOCKER_E2E_SCENARIOS_PATH = "scripts/lib/docker-e2e-scenarios.mjs"; const INSTALL_E2E_RUNNER_PATH = "scripts/docker/install-sh-e2e/run.sh"; const OPENAI_WEB_SEARCH_MINIMAL_E2E_PATH = "scripts/e2e/openai-web-search-minimal-docker.sh"; const PLUGINS_DOCKER_E2E_PATH = "scripts/e2e/plugins-docker.sh"; +const PLUGIN_UPDATE_DOCKER_E2E_PATH = "scripts/e2e/plugin-update-unchanged-docker.sh"; const CENTRALIZED_BUILD_SCRIPTS = [ "scripts/docker/setup.sh", "scripts/e2e/browser-cdp-snapshot-docker.sh", @@ -69,6 +70,15 @@ describe("docker build helper", () => { expect(scenarios).toContain("test:docker:bundled-channel-deps:fast"); }); + it("allows plugin update smoke to tolerate config metadata migrations", () => { + const runner = readFileSync(PLUGIN_UPDATE_DOCKER_E2E_PATH, "utf8"); + + expect(runner).toContain("plugin install record changed unexpectedly"); + expect(runner).toContain("index.installRecords ?? index.records ?? config.plugins?.installs"); + expect(runner).not.toContain("Config changed unexpectedly"); + expect(runner).not.toContain("before_hash"); + }); + it("passes installer tag env to bash, not curl", () => { const runner = readFileSync(INSTALL_E2E_RUNNER_PATH, "utf8");