From bb25a8050ce5d7dcb86c5493526dca8d1c4cc57a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 3 Apr 2026 14:25:54 +0100 Subject: [PATCH] test: baseline bundled runtime sidecar paths --- package.json | 2 + ...generate-runtime-sidecar-paths-baseline.ts | 35 ++++++++++++++++ .../lib/bundled-runtime-sidecar-paths.json | 36 ++++++++++++++++ scripts/openclaw-npm-postpublish-verify.ts | 2 +- src/infra/update-global.test.ts | 2 +- src/infra/update-global.ts | 2 +- src/infra/update-runner.test.ts | 2 +- src/plugins/bundled-plugin-metadata.test.ts | 12 ++++++ src/plugins/public-artifacts.ts | 17 +------- src/plugins/runtime-sidecar-paths-baseline.ts | 41 +++++++++++++++++++ src/plugins/runtime-sidecar-paths.ts | 22 ++++++++++ test/openclaw-npm-postpublish-verify.test.ts | 2 +- 12 files changed, 154 insertions(+), 21 deletions(-) create mode 100644 scripts/generate-runtime-sidecar-paths-baseline.ts create mode 100644 scripts/lib/bundled-runtime-sidecar-paths.json create mode 100644 src/plugins/runtime-sidecar-paths-baseline.ts create mode 100644 src/plugins/runtime-sidecar-paths.ts diff --git a/package.json b/package.json index 730b38cbd4b..c0dd575d8ad 100644 --- a/package.json +++ b/package.json @@ -1041,6 +1041,8 @@ "release:openclaw:npm:verify-published": "node --import tsx scripts/openclaw-npm-postpublish-verify.ts", "release:plugins:npm:check": "node --import tsx scripts/plugin-npm-release-check.ts", "release:plugins:npm:plan": "node --import tsx scripts/plugin-npm-release-plan.ts", + "runtime-sidecars:check": "node --import tsx scripts/generate-runtime-sidecar-paths-baseline.ts --check", + "runtime-sidecars:gen": "node --import tsx scripts/generate-runtime-sidecar-paths-baseline.ts --write", "stage:bundled-plugin-runtime-deps": "node scripts/stage-bundled-plugin-runtime-deps.mjs", "start": "node scripts/run-node.mjs", "test": "node scripts/test-projects.mjs", diff --git a/scripts/generate-runtime-sidecar-paths-baseline.ts b/scripts/generate-runtime-sidecar-paths-baseline.ts new file mode 100644 index 00000000000..d40ff30e29a --- /dev/null +++ b/scripts/generate-runtime-sidecar-paths-baseline.ts @@ -0,0 +1,35 @@ +#!/usr/bin/env node +import path from "node:path"; +import { writeBundledRuntimeSidecarPathBaseline } from "../src/plugins/runtime-sidecar-paths-baseline.js"; + +const args = new Set(process.argv.slice(2)); +const checkOnly = args.has("--check"); +const writeMode = args.has("--write"); + +if (checkOnly === writeMode) { + console.error("Use exactly one of --check or --write."); + process.exit(1); +} + +const repoRoot = process.cwd(); +const result = await writeBundledRuntimeSidecarPathBaseline({ + repoRoot, + check: checkOnly, +}); + +if (checkOnly) { + if (result.changed) { + console.error( + [ + "Bundled runtime sidecar path baseline drift detected.", + `Expected current: ${path.relative(repoRoot, result.jsonPath)}`, + "If this bundled plugin runtime-sidecar change is intentional, run `pnpm runtime-sidecars:gen` and commit the updated baseline file.", + "If not intentional, fix the bundled plugin metadata/public surface drift first.", + ].join("\n"), + ); + process.exit(1); + } + console.log(`OK ${path.relative(repoRoot, result.jsonPath)}`); +} else { + console.log(`Wrote ${path.relative(repoRoot, result.jsonPath)}`); +} diff --git a/scripts/lib/bundled-runtime-sidecar-paths.json b/scripts/lib/bundled-runtime-sidecar-paths.json new file mode 100644 index 00000000000..1157d883b94 --- /dev/null +++ b/scripts/lib/bundled-runtime-sidecar-paths.json @@ -0,0 +1,36 @@ +[ + "dist/extensions/acpx/runtime-api.js", + "dist/extensions/bluebubbles/runtime-api.js", + "dist/extensions/browser/runtime-api.js", + "dist/extensions/copilot-proxy/runtime-api.js", + "dist/extensions/diffs/runtime-api.js", + "dist/extensions/discord/runtime-api.js", + "dist/extensions/feishu/runtime-api.js", + "dist/extensions/google/runtime-api.js", + "dist/extensions/googlechat/runtime-api.js", + "dist/extensions/imessage/runtime-api.js", + "dist/extensions/line/runtime-api.js", + "dist/extensions/lobster/runtime-api.js", + "dist/extensions/matrix/helper-api.js", + "dist/extensions/matrix/runtime-api.js", + "dist/extensions/matrix/thread-bindings-runtime.js", + "dist/extensions/mattermost/runtime-api.js", + "dist/extensions/memory-core/runtime-api.js", + "dist/extensions/msteams/runtime-api.js", + "dist/extensions/nextcloud-talk/runtime-api.js", + "dist/extensions/nostr/runtime-api.js", + "dist/extensions/ollama/runtime-api.js", + "dist/extensions/open-prose/runtime-api.js", + "dist/extensions/qqbot/runtime-api.js", + "dist/extensions/signal/runtime-api.js", + "dist/extensions/slack/runtime-api.js", + "dist/extensions/telegram/runtime-api.js", + "dist/extensions/tlon/runtime-api.js", + "dist/extensions/twitch/runtime-api.js", + "dist/extensions/voice-call/runtime-api.js", + "dist/extensions/whatsapp/light-runtime-api.js", + "dist/extensions/whatsapp/runtime-api.js", + "dist/extensions/zai/runtime-api.js", + "dist/extensions/zalo/runtime-api.js", + "dist/extensions/zalouser/runtime-api.js" +] diff --git a/scripts/openclaw-npm-postpublish-verify.ts b/scripts/openclaw-npm-postpublish-verify.ts index 2dd58bb74b2..8ea232fb81f 100644 --- a/scripts/openclaw-npm-postpublish-verify.ts +++ b/scripts/openclaw-npm-postpublish-verify.ts @@ -5,7 +5,7 @@ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; -import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../src/plugins/public-artifacts.ts"; +import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../src/plugins/runtime-sidecar-paths.ts"; import { parseReleaseVersion, resolveNpmCommandInvocation } from "./openclaw-npm-release-check.ts"; type InstalledPackageJson = { diff --git a/src/infra/update-global.test.ts b/src/infra/update-global.test.ts index 58fa99a577d..f1dec69f987 100644 --- a/src/infra/update-global.test.ts +++ b/src/infra/update-global.test.ts @@ -3,7 +3,7 @@ import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { bundledDistPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; -import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/public-artifacts.js"; +import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/runtime-sidecar-paths.js"; import { captureEnv } from "../test-utils/env.js"; import { canResolveRegistryVersionForPackageTarget, diff --git a/src/infra/update-global.ts b/src/infra/update-global.ts index ec7b886f525..f636aa4d572 100644 --- a/src/infra/update-global.ts +++ b/src/infra/update-global.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/public-artifacts.js"; +import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/runtime-sidecar-paths.js"; import { pathExists } from "../utils.js"; import { readPackageVersion } from "./package-json.js"; import { applyPathPrepend } from "./path-prepend.js"; diff --git a/src/infra/update-runner.test.ts b/src/infra/update-runner.test.ts index 11f591126f8..baa08800505 100644 --- a/src/infra/update-runner.test.ts +++ b/src/infra/update-runner.test.ts @@ -3,7 +3,7 @@ import os from "node:os"; import path from "node:path"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { bundledDistPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; -import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/public-artifacts.js"; +import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/runtime-sidecar-paths.js"; import { withEnvAsync } from "../test-utils/env.js"; import { pathExists } from "../utils.js"; import { resolveStableNodePath } from "./stable-node-path.js"; diff --git a/src/plugins/bundled-plugin-metadata.test.ts b/src/plugins/bundled-plugin-metadata.test.ts index b041020c57a..18952c69754 100644 --- a/src/plugins/bundled-plugin-metadata.test.ts +++ b/src/plugins/bundled-plugin-metadata.test.ts @@ -12,6 +12,8 @@ import { pluginTestRepoRoot as repoRoot, writeJson, } from "./generated-plugin-test-helpers.js"; +import { collectBundledRuntimeSidecarPaths } from "./runtime-sidecar-paths-baseline.js"; +import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "./runtime-sidecar-paths.js"; const BUNDLED_PLUGIN_METADATA_TEST_TIMEOUT_MS = 300_000; @@ -59,6 +61,16 @@ describe("bundled plugin metadata", () => { }, ); + it( + "matches the checked-in runtime sidecar path baseline", + { timeout: BUNDLED_PLUGIN_METADATA_TEST_TIMEOUT_MS }, + () => { + expect(BUNDLED_RUNTIME_SIDECAR_PATHS).toEqual( + collectBundledRuntimeSidecarPaths({ rootDir: repoRoot }), + ); + }, + ); + it("captures setup-entry metadata for bundled channel plugins", () => { const discord = listBundledPluginMetadata().find((entry) => entry.dirName === "discord"); expect(discord?.source).toEqual({ source: "./index.ts", built: "index.js" }); diff --git a/src/plugins/public-artifacts.ts b/src/plugins/public-artifacts.ts index c7cdf06ec2b..fb20fbeaa7f 100644 --- a/src/plugins/public-artifacts.ts +++ b/src/plugins/public-artifacts.ts @@ -1,4 +1,4 @@ -import { listBundledPluginMetadata } from "./bundled-plugin-metadata.js"; +import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "./runtime-sidecar-paths.js"; function assertUniqueValues(values: readonly T[], label: string): readonly T[] { const seen = new Set(); @@ -20,21 +20,6 @@ export function getPublicArtifactBasename(relativePath: string): string { return relativePath.split("/").at(-1) ?? relativePath; } -function buildBundledDistArtifactPath(dirName: string, artifact: string): string { - return ["dist", "extensions", dirName, artifact].join("/"); -} - -export const BUNDLED_RUNTIME_SIDECAR_PATHS = assertUniqueValues( - listBundledPluginMetadata() - .flatMap((entry) => - (entry.runtimeSidecarArtifacts ?? []).map((artifact) => - buildBundledDistArtifactPath(entry.dirName, artifact), - ), - ) - .toSorted((left, right) => left.localeCompare(right)), - "bundled runtime sidecar path", -); - const EXTRA_GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES = assertUniqueValues( [ "action-runtime.runtime.js", diff --git a/src/plugins/runtime-sidecar-paths-baseline.ts b/src/plugins/runtime-sidecar-paths-baseline.ts new file mode 100644 index 00000000000..20c9f265e6b --- /dev/null +++ b/src/plugins/runtime-sidecar-paths-baseline.ts @@ -0,0 +1,41 @@ +import fs from "node:fs"; +import path from "node:path"; +import { listBundledPluginMetadata } from "./bundled-plugin-metadata.js"; + +function buildBundledDistArtifactPath(dirName: string, artifact: string): string { + return ["dist", "extensions", dirName, artifact].join("/"); +} + +export function collectBundledRuntimeSidecarPaths(params?: { + rootDir?: string; +}): readonly string[] { + return listBundledPluginMetadata(params) + .flatMap((entry) => + (entry.runtimeSidecarArtifacts ?? []).map((artifact) => + buildBundledDistArtifactPath(entry.dirName, artifact), + ), + ) + .toSorted((left, right) => left.localeCompare(right)); +} + +export async function writeBundledRuntimeSidecarPathBaseline(params: { + repoRoot: string; + check: boolean; +}): Promise<{ changed: boolean; jsonPath: string }> { + const jsonPath = path.join( + params.repoRoot, + "scripts", + "lib", + "bundled-runtime-sidecar-paths.json", + ); + const expectedJson = `${JSON.stringify(collectBundledRuntimeSidecarPaths(), null, 2)}\n`; + const currentJson = fs.existsSync(jsonPath) ? fs.readFileSync(jsonPath, "utf8") : ""; + const changed = currentJson !== expectedJson; + + if (!params.check && changed) { + fs.mkdirSync(path.dirname(jsonPath), { recursive: true }); + fs.writeFileSync(jsonPath, expectedJson, "utf8"); + } + + return { changed, jsonPath }; +} diff --git a/src/plugins/runtime-sidecar-paths.ts b/src/plugins/runtime-sidecar-paths.ts new file mode 100644 index 00000000000..4838b73106d --- /dev/null +++ b/src/plugins/runtime-sidecar-paths.ts @@ -0,0 +1,22 @@ +import bundledRuntimeSidecarPaths from "../../scripts/lib/bundled-runtime-sidecar-paths.json" with { type: "json" }; + +function assertUniqueValues(values: readonly T[], label: string): readonly T[] { + const seen = new Set(); + const duplicates = new Set(); + for (const value of values) { + if (seen.has(value)) { + duplicates.add(value); + continue; + } + seen.add(value); + } + if (duplicates.size > 0) { + throw new Error(`Duplicate ${label}: ${Array.from(duplicates).join(", ")}`); + } + return values; +} + +export const BUNDLED_RUNTIME_SIDECAR_PATHS = assertUniqueValues( + bundledRuntimeSidecarPaths, + "bundled runtime sidecar path", +); diff --git a/test/openclaw-npm-postpublish-verify.test.ts b/test/openclaw-npm-postpublish-verify.test.ts index af601259592..54d6f2d9e0c 100644 --- a/test/openclaw-npm-postpublish-verify.test.ts +++ b/test/openclaw-npm-postpublish-verify.test.ts @@ -3,7 +3,7 @@ import { buildPublishedInstallScenarios, collectInstalledPackageErrors, } from "../scripts/openclaw-npm-postpublish-verify.ts"; -import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../src/plugins/public-artifacts.ts"; +import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../src/plugins/runtime-sidecar-paths.ts"; describe("buildPublishedInstallScenarios", () => { it("uses a single fresh scenario for plain stable releases", () => {