mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:50:42 +00:00
build: restore qa lab updater sidecar
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
"!dist/extensions/qa-channel/**",
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"!dist/extensions/qa-lab/**",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
"!dist/extensions/qa-matrix/**",
|
||||
"!dist/plugin-sdk/extensions/qa-lab/**",
|
||||
"!dist/plugin-sdk/qa-lab.*",
|
||||
|
||||
@@ -46,6 +46,7 @@ const LEGACY_CONTEXT_ENGINE_UNRESOLVED_RUNTIME_MARKER =
|
||||
"Failed to load legacy context engine runtime.";
|
||||
const LEGACY_UPDATE_COMPAT_RUNTIME_SIDECAR_PATHS = [
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
] as const;
|
||||
const PUBLISHED_BUNDLED_RUNTIME_SIDECAR_PATHS = [
|
||||
...BUNDLED_RUNTIME_SIDECAR_PATHS.filter((relativePath) =>
|
||||
|
||||
@@ -59,7 +59,10 @@ export type NpmDistTagMirrorAuth = {
|
||||
};
|
||||
const EXPECTED_REPOSITORY_URL = "https://github.com/openclaw/openclaw";
|
||||
const MAX_CALVER_DISTANCE_DAYS = 2;
|
||||
const LEGACY_UPDATE_COMPAT_PACKED_PATHS = ["dist/extensions/qa-channel/runtime-api.js"] as const;
|
||||
const LEGACY_UPDATE_COMPAT_PACKED_PATHS = [
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
] as const;
|
||||
const REQUIRED_PACKED_PATHS = [
|
||||
PACKAGE_DIST_INVENTORY_RELATIVE_PATH,
|
||||
"dist/control-ui/index.html",
|
||||
|
||||
@@ -41,6 +41,12 @@ const LEGACY_UPDATE_COMPAT_SIDECARS = [
|
||||
content:
|
||||
"// Compatibility stub for older OpenClaw updaters. The QA channel implementation is not packaged.\nexport {};\n",
|
||||
},
|
||||
{
|
||||
path: "dist/extensions/qa-lab/runtime-api.js",
|
||||
removedPrefix: "dist/extensions/qa-lab/",
|
||||
content:
|
||||
"// Compatibility stub for older OpenClaw updaters. The QA lab implementation is not packaged.\nexport {};\n",
|
||||
},
|
||||
];
|
||||
const BAILEYS_MEDIA_FILE = join(
|
||||
"node_modules",
|
||||
@@ -313,10 +319,9 @@ export function restoreLegacyUpdaterCompatSidecars(params = {}) {
|
||||
const restored = [];
|
||||
|
||||
for (const sidecar of LEGACY_UPDATE_COMPAT_SIDECARS) {
|
||||
// Older npm updater builds verify this exact sidecar after npm has already
|
||||
// replaced the package. npm may remove stale QA Lab files before this
|
||||
// postinstall hook runs, so this must be generated independently of prune
|
||||
// results. The tarball and dist inventory still omit QA Lab.
|
||||
// Older npm updater builds verify these exact sidecars after npm has
|
||||
// already replaced the package, so generate them independently of prune
|
||||
// results.
|
||||
const sidecarPath = join(packageRoot, sidecar.path);
|
||||
makeDirectory(dirname(sidecarPath), { recursive: true });
|
||||
writeFile(sidecarPath, sidecar.content, "utf8");
|
||||
|
||||
@@ -57,7 +57,13 @@ const requiredPathGroups = [
|
||||
"dist/build-info.json",
|
||||
"dist/channel-catalog.json",
|
||||
"dist/control-ui/index.html",
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
];
|
||||
const legacyUpdateCompatPackPaths = new Set([
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
]);
|
||||
const forbiddenPrefixes = [
|
||||
"dist-runtime/",
|
||||
"dist/OpenClaw.app/",
|
||||
@@ -274,7 +280,9 @@ export function collectForbiddenPackPaths(paths: Iterable<string>): string[] {
|
||||
return [...paths]
|
||||
.filter(
|
||||
(path) =>
|
||||
forbiddenPrefixes.some((prefix) => path.startsWith(prefix)) || /node_modules\//.test(path),
|
||||
!legacyUpdateCompatPackPaths.has(path) &&
|
||||
(forbiddenPrefixes.some((prefix) => path.startsWith(prefix)) ||
|
||||
/node_modules\//.test(path)),
|
||||
)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@ export const NPM_UPDATE_COMPAT_SIDECARS = [
|
||||
content:
|
||||
"// Compatibility stub for older OpenClaw updaters. The QA channel implementation is not packaged.\nexport {};\n",
|
||||
},
|
||||
{
|
||||
path: "dist/extensions/qa-lab/runtime-api.js",
|
||||
content:
|
||||
"// Compatibility stub for older OpenClaw updaters. The QA lab implementation is not packaged.\nexport {};\n",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const NPM_UPDATE_COMPAT_SIDECAR_PATHS = new Set<string>(
|
||||
|
||||
@@ -37,14 +37,22 @@ describe("package dist inventory", () => {
|
||||
|
||||
it("keeps npm-omitted dist artifacts out of the inventory", async () => {
|
||||
await withTempDir({ prefix: "openclaw-dist-inventory-pack-" }, async (packageRoot) => {
|
||||
const packagedQaRuntime = path.join(
|
||||
const packagedQaChannelRuntime = path.join(
|
||||
packageRoot,
|
||||
"dist",
|
||||
"extensions",
|
||||
"qa-channel",
|
||||
"runtime-api.js",
|
||||
);
|
||||
const packagedQaLabRuntime = path.join(
|
||||
packageRoot,
|
||||
"dist",
|
||||
"extensions",
|
||||
"qa-lab",
|
||||
"runtime-api.js",
|
||||
);
|
||||
const omittedQaChunk = path.join(packageRoot, "dist", "extensions", "qa-channel", "cli.js");
|
||||
const omittedQaLabChunk = path.join(packageRoot, "dist", "extensions", "qa-lab", "cli.js");
|
||||
const omittedQaMatrixChunk = path.join(
|
||||
packageRoot,
|
||||
"dist",
|
||||
@@ -72,13 +80,16 @@ describe("package dist inventory", () => {
|
||||
"color-support",
|
||||
);
|
||||
const omittedMap = path.join(packageRoot, "dist", "feature.runtime.js.map");
|
||||
await fs.mkdir(path.dirname(packagedQaRuntime), { recursive: true });
|
||||
await fs.mkdir(path.dirname(packagedQaChannelRuntime), { recursive: true });
|
||||
await fs.mkdir(path.dirname(packagedQaLabRuntime), { recursive: true });
|
||||
await fs.mkdir(path.dirname(omittedQaMatrixChunk), { recursive: true });
|
||||
await fs.mkdir(path.dirname(omittedQaLabTypes), { recursive: true });
|
||||
await fs.mkdir(path.dirname(omittedExtensionNodeModuleSymlink), { recursive: true });
|
||||
await fs.writeFile(path.join(packageRoot, "color-support.js"), "export {};\n", "utf8");
|
||||
await fs.writeFile(packagedQaRuntime, "export {};\n", "utf8");
|
||||
await fs.writeFile(packagedQaChannelRuntime, "export {};\n", "utf8");
|
||||
await fs.writeFile(packagedQaLabRuntime, "export {};\n", "utf8");
|
||||
await fs.writeFile(omittedQaChunk, "export {};\n", "utf8");
|
||||
await fs.writeFile(omittedQaLabChunk, "export {};\n", "utf8");
|
||||
await fs.writeFile(omittedQaMatrixChunk, "export {};\n", "utf8");
|
||||
await fs.writeFile(omittedQaLabPluginSdk, "export {};\n", "utf8");
|
||||
await fs.writeFile(omittedQaLabTypes, "export {};\n", "utf8");
|
||||
@@ -91,6 +102,7 @@ describe("package dist inventory", () => {
|
||||
|
||||
await expect(writePackageDistInventory(packageRoot)).resolves.toEqual([
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,10 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
export const PACKAGE_DIST_INVENTORY_RELATIVE_PATH = "dist/postinstall-inventory.json";
|
||||
const PACKAGED_QA_RUNTIME_PATHS = new Set(["dist/extensions/qa-channel/runtime-api.js"]);
|
||||
const PACKAGED_QA_RUNTIME_PATHS = new Set([
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
]);
|
||||
const OMITTED_QA_EXTENSION_PREFIXES = [
|
||||
"dist/extensions/qa-channel/",
|
||||
"dist/extensions/qa-lab/",
|
||||
@@ -20,7 +23,6 @@ const OMITTED_PRIVATE_QA_PLUGIN_SDK_FILES = new Set([
|
||||
const OMITTED_PRIVATE_QA_DIST_PREFIXES = ["dist/qa-runtime-"];
|
||||
const OMITTED_DIST_SUBTREE_PATTERNS = [
|
||||
/^dist\/extensions\/[^/]+\/node_modules(?:\/|$)/u,
|
||||
/^dist\/extensions\/qa-lab(?:\/|$)/u,
|
||||
/^dist\/extensions\/qa-matrix(?:\/|$)/u,
|
||||
/^dist\/plugin-sdk\/extensions\/qa-lab(?:\/|$)/u,
|
||||
] as const;
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
|
||||
const MATRIX_HELPER_API = bundledDistPluginFile("matrix", "helper-api.js");
|
||||
const QA_CHANNEL_RUNTIME_API = bundledDistPluginFile("qa-channel", "runtime-api.js");
|
||||
const QA_LAB_RUNTIME_API = bundledDistPluginFile("qa-lab", "runtime-api.js");
|
||||
|
||||
describe("update global helpers", () => {
|
||||
let envSnapshot: ReturnType<typeof captureEnv> | undefined;
|
||||
@@ -427,6 +428,12 @@ describe("update global helpers", () => {
|
||||
await expect(collectInstalledGlobalPackageErrors({ packageRoot })).resolves.toContain(
|
||||
`missing bundled runtime sidecar ${QA_CHANNEL_RUNTIME_API}`,
|
||||
);
|
||||
await fs.writeFile(path.join(packageRoot, QA_CHANNEL_RUNTIME_API), "export {};\n", "utf-8");
|
||||
|
||||
await fs.rm(path.join(packageRoot, QA_LAB_RUNTIME_API));
|
||||
await expect(collectInstalledGlobalPackageErrors({ packageRoot })).resolves.toContain(
|
||||
`missing bundled runtime sidecar ${QA_LAB_RUNTIME_API}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@ import {
|
||||
} from "../scripts/openclaw-npm-release-check.ts";
|
||||
import { PACKAGE_DIST_INVENTORY_RELATIVE_PATH } from "../src/infra/package-dist-inventory.ts";
|
||||
|
||||
const LEGACY_UPDATE_COMPAT_PACKED_PATHS = ["dist/extensions/qa-channel/runtime-api.js"] as const;
|
||||
const LEGACY_UPDATE_COMPAT_PACKED_PATHS = [
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
] as const;
|
||||
const REQUIRED_PACKED_PATHS = [
|
||||
PACKAGE_DIST_INVENTORY_RELATIVE_PATH,
|
||||
...LEGACY_UPDATE_COMPAT_PACKED_PATHS,
|
||||
@@ -341,7 +344,6 @@ describe("collectForbiddenPackedPathErrors", () => {
|
||||
]),
|
||||
).toEqual([
|
||||
'npm package must not include private QA channel artifact "dist/extensions/qa-channel/package.json".',
|
||||
'npm package must not include private QA lab artifact "dist/extensions/qa-lab/runtime-api.js".',
|
||||
'npm package must not include private QA lab artifact "dist/extensions/qa-lab/src/cli.js".',
|
||||
'npm package must not include private QA lab type artifact "dist/plugin-sdk/extensions/qa-lab/cli.d.ts".',
|
||||
'npm package must not include private QA runtime chunk "dist/qa-runtime-B9LDtssJ.js".',
|
||||
@@ -349,15 +351,13 @@ describe("collectForbiddenPackedPathErrors", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("allows only the legacy update verifier QA channel runtime sidecar", () => {
|
||||
it("allows legacy update verifier QA runtime sidecars", () => {
|
||||
expect(
|
||||
collectForbiddenPackedPathErrors([
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
]),
|
||||
).toEqual([
|
||||
'npm package must not include private QA lab artifact "dist/extensions/qa-lab/runtime-api.js".',
|
||||
]);
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("rejects root dist chunks that still reference the private qa lab", () => {
|
||||
|
||||
@@ -322,7 +322,6 @@ describe("collectForbiddenPackPaths", () => {
|
||||
"qa/scenarios/index.md",
|
||||
]),
|
||||
).toEqual([
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
"dist/plugin-sdk/extensions/qa-lab/cli.d.ts",
|
||||
"dist/plugin-sdk/qa-lab.js",
|
||||
"dist/plugin-sdk/qa-runtime.js",
|
||||
@@ -392,6 +391,8 @@ describe("collectMissingPackPaths", () => {
|
||||
"dist/index.js",
|
||||
"dist/entry.js",
|
||||
"dist/control-ui/index.html",
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
"dist/extensions/acpx/mcp-proxy.mjs",
|
||||
bundledDistPluginFile("diffs", "assets/viewer-runtime.js"),
|
||||
...requiredBundledPluginPackPaths,
|
||||
|
||||
@@ -215,7 +215,7 @@ describe("bundled plugin postinstall", () => {
|
||||
await expect(fs.stat(staleFile)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
});
|
||||
|
||||
it("restores only postinstall-generated QA lab compat sidecar after pruning old installs", async () => {
|
||||
it("restores only postinstall-generated QA compat sidecars after pruning old installs", async () => {
|
||||
const packageRoot = await createTempDirAsync("openclaw-packaged-install-qa-compat-");
|
||||
const currentFile = path.join(packageRoot, "dist", "entry.js");
|
||||
const stalePackage = path.join(packageRoot, "dist", "extensions", "qa-lab", "package.json");
|
||||
@@ -246,9 +246,12 @@ describe("bundled plugin postinstall", () => {
|
||||
"utf8",
|
||||
),
|
||||
).resolves.toContain("QA channel implementation is not packaged");
|
||||
await expect(
|
||||
fs.readFile(path.join(packageRoot, "dist", "extensions", "qa-lab", "runtime-api.js"), "utf8"),
|
||||
).resolves.toContain("QA lab implementation is not packaged");
|
||||
});
|
||||
|
||||
it("creates only an empty QA channel compat sidecar for fresh installs", async () => {
|
||||
it("creates only empty QA compat sidecars for fresh installs", async () => {
|
||||
const packageRoot = await createTempDirAsync("openclaw-packaged-install-no-qa-compat-");
|
||||
await fs.mkdir(path.join(packageRoot, "dist"), { recursive: true });
|
||||
await fs.writeFile(path.join(packageRoot, "dist", "entry.js"), "export {};\n");
|
||||
@@ -260,7 +263,10 @@ describe("bundled plugin postinstall", () => {
|
||||
removedFiles: ["dist/entry-old.js"],
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
}),
|
||||
).toEqual(["dist/extensions/qa-channel/runtime-api.js"]);
|
||||
).toEqual([
|
||||
"dist/extensions/qa-channel/runtime-api.js",
|
||||
"dist/extensions/qa-lab/runtime-api.js",
|
||||
]);
|
||||
|
||||
await expect(
|
||||
fs.readFile(
|
||||
@@ -270,12 +276,23 @@ describe("bundled plugin postinstall", () => {
|
||||
).resolves.toBe(
|
||||
"// Compatibility stub for older OpenClaw updaters. The QA channel implementation is not packaged.\nexport {};\n",
|
||||
);
|
||||
await expect(
|
||||
fs.readFile(path.join(packageRoot, "dist", "extensions", "qa-lab", "runtime-api.js"), "utf8"),
|
||||
).resolves.toBe(
|
||||
"// Compatibility stub for older OpenClaw updaters. The QA lab implementation is not packaged.\nexport {};\n",
|
||||
);
|
||||
await expect(
|
||||
fs.stat(path.join(packageRoot, "dist", "extensions", "qa-channel", "package.json")),
|
||||
).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(
|
||||
fs.stat(path.join(packageRoot, "dist", "extensions", "qa-channel", "openclaw.plugin.json")),
|
||||
).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(
|
||||
fs.stat(path.join(packageRoot, "dist", "extensions", "qa-lab", "package.json")),
|
||||
).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(
|
||||
fs.stat(path.join(packageRoot, "dist", "extensions", "qa-lab", "openclaw.plugin.json")),
|
||||
).rejects.toMatchObject({ code: "ENOENT" });
|
||||
});
|
||||
|
||||
it("keeps packaged postinstall non-fatal when the dist inventory is missing", async () => {
|
||||
|
||||
Reference in New Issue
Block a user