From 0ff0c7ce576427d25483d8e3cdc52d3c4849219f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 27 Apr 2026 05:07:11 +0100 Subject: [PATCH] ci: tolerate legacy qa inventory entries --- scripts/check-openclaw-package-tarball.mjs | 39 +++++++++++ .../check-openclaw-package-tarball.test.ts | 70 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 test/scripts/check-openclaw-package-tarball.test.ts diff --git a/scripts/check-openclaw-package-tarball.mjs b/scripts/check-openclaw-package-tarball.mjs index bdf62b00ded..7a54fd9dff6 100644 --- a/scripts/check-openclaw-package-tarball.mjs +++ b/scripts/check-openclaw-package-tarball.mjs @@ -37,6 +37,36 @@ const entries = list.stdout const normalized = entries.map((entry) => entry.replace(/^package\//u, "")); const entrySet = new Set(normalized); const errors = []; +const warnings = []; + +const LEGACY_OMITTED_PRIVATE_QA_INVENTORY_PREFIXES = [ + "dist/extensions/qa-channel/", + "dist/extensions/qa-lab/", + "dist/extensions/qa-matrix/", + "dist/plugin-sdk/extensions/qa-channel/", + "dist/plugin-sdk/extensions/qa-lab/", +]; +const LEGACY_OMITTED_PRIVATE_QA_INVENTORY_FILES = new Set([ + "dist/plugin-sdk/qa-channel.d.ts", + "dist/plugin-sdk/qa-channel.js", + "dist/plugin-sdk/qa-channel-protocol.d.ts", + "dist/plugin-sdk/qa-channel-protocol.js", + "dist/plugin-sdk/qa-lab.d.ts", + "dist/plugin-sdk/qa-lab.js", + "dist/plugin-sdk/qa-runtime.d.ts", + "dist/plugin-sdk/qa-runtime.js", + "dist/plugin-sdk/src/plugin-sdk/qa-channel.d.ts", + "dist/plugin-sdk/src/plugin-sdk/qa-channel-protocol.d.ts", + "dist/plugin-sdk/src/plugin-sdk/qa-lab.d.ts", + "dist/plugin-sdk/src/plugin-sdk/qa-runtime.d.ts", +]); + +function isLegacyOmittedPrivateQaInventoryEntry(relativePath) { + return ( + LEGACY_OMITTED_PRIVATE_QA_INVENTORY_FILES.has(relativePath) || + LEGACY_OMITTED_PRIVATE_QA_INVENTORY_PREFIXES.some((prefix) => relativePath.startsWith(prefix)) + ); +} function readTarEntry(entryPath) { const candidates = [entryPath, `package/${entryPath}`]; @@ -76,6 +106,12 @@ if (entrySet.has("dist/postinstall-inventory.json")) { for (const inventoryEntry of inventory) { const normalizedEntry = inventoryEntry.replace(/\\/gu, "/"); if (!entrySet.has(normalizedEntry)) { + if (isLegacyOmittedPrivateQaInventoryEntry(normalizedEntry)) { + warnings.push( + `legacy inventory references omitted private QA tar entry ${normalizedEntry}`, + ); + continue; + } errors.push(`inventory references missing tar entry ${normalizedEntry}`); } } @@ -93,4 +129,7 @@ if (errors.length > 0) { fail(`OpenClaw package tarball integrity failed:\n${errors.join("\n")}`); } +for (const warning of warnings) { + console.warn(`OpenClaw package tarball integrity warning: ${warning}`); +} console.log("OpenClaw package tarball integrity passed."); diff --git a/test/scripts/check-openclaw-package-tarball.test.ts b/test/scripts/check-openclaw-package-tarball.test.ts new file mode 100644 index 00000000000..5d1e987d010 --- /dev/null +++ b/test/scripts/check-openclaw-package-tarball.test.ts @@ -0,0 +1,70 @@ +import { spawnSync } from "node:child_process"; +import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { describe, expect, it } from "vitest"; + +const CHECK_SCRIPT = "scripts/check-openclaw-package-tarball.mjs"; + +function withTarball( + inventory: string[], + files: Record, + testBody: (tarball: string) => void, +) { + const root = mkdtempSync(join(tmpdir(), "openclaw-package-tarball-test-")); + try { + const packageRoot = join(root, "package"); + mkdirSync(join(packageRoot, "dist"), { recursive: true }); + writeFileSync( + join(packageRoot, "package.json"), + JSON.stringify({ name: "openclaw", version: "0.0.0" }), + ); + writeFileSync( + join(packageRoot, "dist", "postinstall-inventory.json"), + JSON.stringify(inventory), + ); + for (const [relativePath, body] of Object.entries(files)) { + const filePath = join(packageRoot, relativePath); + mkdirSync(dirname(filePath), { recursive: true }); + writeFileSync(filePath, body); + } + + const tarball = join(root, "openclaw.tgz"); + const pack = spawnSync("tar", ["-czf", tarball, "-C", root, "package"], { + encoding: "utf8", + }); + expect(pack.status, pack.stderr).toBe(0); + testBody(tarball); + } finally { + rmSync(root, { recursive: true, force: true }); + } +} + +describe("check-openclaw-package-tarball", () => { + it("allows legacy private QA inventory entries omitted from shipped tarballs", () => { + withTarball( + ["dist/index.js", "dist/extensions/qa-channel/runtime-api.js"], + { "dist/index.js": "export {};\n" }, + (tarball) => { + const result = spawnSync("node", [CHECK_SCRIPT, tarball], { encoding: "utf8" }); + + expect(result.status, result.stderr).toBe(0); + expect(result.stderr).toContain("legacy inventory references omitted private QA"); + expect(result.stdout).toContain("OpenClaw package tarball integrity passed."); + }, + ); + }); + + it("still rejects non-legacy missing inventory entries", () => { + withTarball( + ["dist/index.js", "dist/cli.js"], + { "dist/index.js": "export {};\n" }, + (tarball) => { + const result = spawnSync("node", [CHECK_SCRIPT, tarball], { encoding: "utf8" }); + + expect(result.status).not.toBe(0); + expect(result.stderr).toContain("inventory references missing tar entry dist/cli.js"); + }, + ); + }); +});