test: allow npm qa compat sidecars

This commit is contained in:
Peter Steinberger
2026-04-11 16:46:25 +01:00
parent 9656ae649c
commit bbc3849e24
2 changed files with 52 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ import {
collectRootDistBundledRuntimeMirrors,
collectRuntimeDependencySpecs,
} from "./lib/bundled-plugin-root-runtime-mirrors.mjs";
import { NPM_UPDATE_COMPAT_SIDECAR_PATHS } from "./lib/npm-update-compat-sidecars.mjs";
import { parseReleaseVersion, resolveNpmCommandInvocation } from "./openclaw-npm-release-check.ts";
type InstalledPackageJson = {
@@ -40,6 +41,13 @@ type InstalledBundledExtensionManifestRecord = {
};
const MAX_BUNDLED_EXTENSION_MANIFEST_BYTES = 1024 * 1024;
const NPM_UPDATE_COMPAT_EXTENSION_DIRS = new Set(
[...NPM_UPDATE_COMPAT_SIDECAR_PATHS].map((relativePath) => {
const pathParts = relativePath.split("/");
pathParts.pop();
return pathParts.join("/");
}),
);
export type PublishedInstallScenario = {
name: string;
@@ -129,6 +137,20 @@ function collectExpectedBundledExtensionPackageIds(
return ids;
}
function isNpmUpdateCompatOnlyExtensionDir(params: {
extensionId: string;
packageRoot: string;
}): boolean {
const relativeExtensionDir = `dist/extensions/${params.extensionId}`;
if (!NPM_UPDATE_COMPAT_EXTENSION_DIRS.has(relativeExtensionDir)) {
return false;
}
return [...NPM_UPDATE_COMPAT_SIDECAR_PATHS]
.filter((relativePath) => relativePath.startsWith(`${relativeExtensionDir}/`))
.every((relativePath) => existsSync(join(params.packageRoot, relativePath)));
}
function readBundledExtensionPackageJsons(packageRoot: string): {
manifests: InstalledBundledExtensionManifestRecord[];
errors: string[];
@@ -150,6 +172,9 @@ function readBundledExtensionPackageJsons(packageRoot: string): {
const extensionDirPath = join(extensionsDir, entry.name);
const packageJsonPath = join(extensionsDir, entry.name, "package.json");
if (!existsSync(packageJsonPath)) {
if (isNpmUpdateCompatOnlyExtensionDir({ extensionId: entry.name, packageRoot })) {
continue;
}
if (expectedPackageIds === null || expectedPackageIds.has(entry.name)) {
errors.push(`installed bundled extension manifest missing: ${packageJsonPath}.`);
}

View File

@@ -258,6 +258,33 @@ describe("collectInstalledMirroredRootDependencyManifestErrors", () => {
}
});
it("allows npm update compatibility sidecar directories without package.json", () => {
const packageRoot = makeInstalledPackageRoot();
try {
writePackageFile(packageRoot, "package.json", {
version: "2026.4.10",
dependencies: {},
});
mkdirSync(join(packageRoot, "dist/extensions/qa-channel"), { recursive: true });
mkdirSync(join(packageRoot, "dist/extensions/qa-lab"), { recursive: true });
writeFileSync(
join(packageRoot, "dist/extensions/qa-channel/runtime-api.js"),
"export {};\n",
"utf8",
);
writeFileSync(
join(packageRoot, "dist/extensions/qa-lab/runtime-api.js"),
"export {};\n",
"utf8",
);
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([]);
} finally {
rmSync(packageRoot, { recursive: true, force: true });
}
});
it("rejects bundled extension manifests that are not regular files", () => {
const packageRoot = makeInstalledPackageRoot();
const outsideManifestRoot = makeInstalledPackageRoot();