test(plugins): deduplicate publication fixtures (#124312)

This commit is contained in:
Peter Steinberger
2026-08-15 17:16:07 -07:00
committed by GitHub
parent 38e0ae6ec8
commit eb13f5719f
4 changed files with 114 additions and 223 deletions
@@ -0,0 +1,62 @@
import { writeFileSync } from "node:fs";
import { join } from "node:path";
import { writeJsonFile } from "./temp-repo.js";
type PublishablePluginSurface = "npm" | "clawhub" | "both" | "clawhub-disabled";
type PublishablePluginFixtureOptions = {
extensionId?: string;
version: string;
publishTo: PublishablePluginSurface;
bundledDist?: boolean;
dependency?: {
packageName: string;
version: string;
requireLatest?: boolean;
};
};
export function writePublishablePluginFixture(
repoDir: string,
options: PublishablePluginFixtureOptions,
) {
const extensionId = options.extensionId ?? "demo-plugin";
const packageName = `@openclaw/${extensionId}`;
const packageDir = join(repoDir, "extensions", extensionId);
const publishToNpm = options.publishTo === "npm" || options.publishTo === "both";
const publishToClawHub = options.publishTo === "clawhub" || options.publishTo === "both";
const release = {
...(publishToNpm ? { publishToNpm: true } : {}),
...(publishToClawHub ? { publishToClawHub: true } : {}),
...(options.publishTo === "clawhub-disabled" ? { publishToClawHub: false } : {}),
...(options.dependency?.requireLatest
? { requireLatestDependencies: [options.dependency.packageName] }
: {}),
};
writeJsonFile(join(packageDir, "package.json"), {
name: packageName,
version: options.version,
type: "module",
repository: {
type: "git",
url: "https://github.com/openclaw/openclaw",
},
...(options.dependency
? { dependencies: { [options.dependency.packageName]: options.dependency.version } }
: {}),
openclaw: {
extensions: ["./index.ts"],
compat: { pluginApi: `>=${options.version}` },
build: {
openclawVersion: options.version,
...(options.bundledDist ? { bundledDist: true } : {}),
},
install: { npmSpec: packageName },
release,
},
});
const exportName = extensionId.replaceAll(/[-.]/g, "_");
writeFileSync(join(packageDir, "index.ts"), `export const ${exportName} = 1;\n`);
writeFileSync(join(packageDir, "README.md"), "# Demo plugin\n");
return { extensionId, packageDir, packageName };
}
+26 -53
View File
@@ -31,7 +31,8 @@ import {
OPENCLAW_PLUGIN_NPM_REPOSITORY_URL,
} from "../scripts/lib/plugin-npm-release.ts";
import { runPluginClawHubReleaseCheck } from "../scripts/plugin-clawhub-release-check.ts";
import { cleanupTempDirs, makeTempRepoRoot } from "./helpers/temp-repo.js";
import { writePublishablePluginFixture } from "./helpers/publishable-plugin-fixture.js";
import { cleanupTempDirs, makeTempRepoRoot, writeJsonFile } from "./helpers/temp-repo.js";
const tempDirs: string[] = [];
@@ -2109,59 +2110,31 @@ function createTempPluginRepo(
);
writeFileSync(join(repoDir, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n");
for (const currentExtensionId of extensionIds) {
mkdirSync(join(repoDir, "extensions", currentExtensionId), { recursive: true });
writeFileSync(
join(repoDir, "extensions", currentExtensionId, "package.json"),
JSON.stringify(
{
name: `@openclaw/${currentExtensionId}`,
version: "2026.4.1",
type: "module",
repository: {
type: "git",
url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL,
},
...(options.requiredLatestDependencyVersion
? {
dependencies: {
"demo-runtime": options.requiredLatestDependencyVersion,
},
}
: {}),
openclaw: {
extensions: ["./index.ts"],
...(options.includeClawHubContract === false
? {}
: {
compat: {
pluginApi: ">=2026.4.1",
},
build: {
openclawVersion: "2026.4.1",
},
}),
install: {
npmSpec: `@openclaw/${currentExtensionId}`,
const fixture = writePublishablePluginFixture(repoDir, {
extensionId: currentExtensionId,
version: "2026.4.1",
publishTo: options.publishToClawHub === false ? "clawhub-disabled" : "clawhub",
...(options.requiredLatestDependencyVersion
? {
dependency: {
packageName: "demo-runtime",
version: options.requiredLatestDependencyVersion,
requireLatest: true,
},
release: {
publishToClawHub: options.publishToClawHub ?? true,
...(options.requiredLatestDependencyVersion
? {
requireLatestDependencies: ["demo-runtime"],
}
: {}),
},
},
},
null,
2,
),
);
writeFileSync(
join(repoDir, "extensions", currentExtensionId, "index.ts"),
`export const ${currentExtensionId.replaceAll(/[-.]/g, "_")} = 1;\n`,
);
writeFileSync(join(repoDir, "extensions", currentExtensionId, "README.md"), "# Demo plugin\n");
}
: {}),
});
if (options.includeClawHubContract === false) {
const manifestPath = join(fixture.packageDir, "package.json");
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as {
openclaw?: { build?: unknown; compat?: unknown };
};
if (manifest.openclaw) {
delete manifest.openclaw.compat;
delete manifest.openclaw.build;
}
writeJsonFile(manifestPath, manifest);
}
}
git(repoDir, ["init", "-b", "main"]);
+23 -140
View File
@@ -1,5 +1,5 @@
// Plugin npm release tests validate plugin npm release artifacts.
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { mkdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { bundledPluginFile, bundledPluginRoot } from "openclaw/plugin-sdk/test-fixtures";
import { afterEach, describe, expect, it, vi } from "vitest";
@@ -20,6 +20,7 @@ import {
resolveSelectedPublishablePluginPackages,
type PublishablePluginPackage,
} from "../scripts/lib/plugin-npm-release.ts";
import { writePublishablePluginFixture } from "./helpers/publishable-plugin-fixture.js";
import { cleanupTempDirs, makeTempRepoRoot, writeJsonFile } from "./helpers/temp-repo.js";
type ExecFileSync = typeof import("node:child_process").execFileSync;
@@ -169,12 +170,6 @@ function externalPluginContract(version: string) {
};
}
function writePluginReadme(repoDir: string, extensionId: string): void {
const packageDir = join(repoDir, "extensions", extensionId);
mkdirSync(packageDir, { recursive: true });
writeFileSync(join(packageDir, "README.md"), `# ${extensionId}\n`);
}
describe("collectPublishablePluginPackageErrors", () => {
it("accepts a valid publishable plugin package candidate", () => {
expect(
@@ -500,26 +495,9 @@ describe("collectPluginReleaseDependencyFreshnessErrors", () => {
describe("collectPluginReleasePlan", () => {
it("fails closed when the published-version lookup times out", () => {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-");
mkdirSync(join(repoDir, "extensions", "demo-plugin"), { recursive: true });
writePluginReadme(repoDir, "demo-plugin");
writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.4.10",
type: "module",
repository: {
type: "git",
url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL,
},
openclaw: {
extensions: ["./index.ts"],
...externalPluginContract("2026.4.10"),
install: {
npmSpec: "@openclaw/demo-plugin",
},
release: {
publishToNpm: true,
},
},
publishTo: "npm",
});
childProcessMock.execFileSyncOverride = ((command: string, args?: readonly string[]) => {
expect(command).toBe("npm");
@@ -542,20 +520,10 @@ describe("collectPluginReleasePlan", () => {
describe("collectPublishablePluginPackages", () => {
it("defers explicitly bundled plugins from npm and ClawHub release plans", () => {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-");
mkdirSync(join(repoDir, "extensions", "demo-plugin"), { recursive: true });
writePluginReadme(repoDir, "demo-plugin");
writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.4.10",
type: "module",
repository: { type: "git", url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL },
openclaw: {
extensions: ["./index.ts"],
...externalPluginContract("2026.4.10"),
build: { openclawVersion: "2026.4.10", bundledDist: true },
install: { npmSpec: "@openclaw/demo-plugin" },
release: { publishToClawHub: true, publishToNpm: true },
},
publishTo: "both",
bundledDist: true,
});
expect(collectPublishablePluginPackages(repoDir)).toStrictEqual([]);
@@ -599,26 +567,9 @@ describe("collectPublishablePluginPackages", () => {
it("collects publishable npm plugins from extension package manifests", () => {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-");
mkdirSync(join(repoDir, "extensions", "demo-plugin"), { recursive: true });
writePluginReadme(repoDir, "demo-plugin");
writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.4.10",
type: "module",
repository: {
type: "git",
url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL,
},
openclaw: {
extensions: ["./index.ts"],
...externalPluginContract("2026.4.10"),
install: {
npmSpec: "@openclaw/demo-plugin",
},
release: {
publishToNpm: true,
},
},
publishTo: "npm",
});
expect(collectPublishablePluginPackages(repoDir)).toEqual([
@@ -637,18 +588,9 @@ describe("collectPublishablePluginPackages", () => {
it("uses extended-stable for every publishable plugin at the exact root version", () => {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-");
writeJsonFile(join(repoDir, "package.json"), { version: "2026.7.33" });
writePluginReadme(repoDir, "demo-plugin");
writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.7.33",
type: "module",
repository: { type: "git", url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL },
openclaw: {
extensions: ["./index.ts"],
...externalPluginContract("2026.7.33"),
install: { npmSpec: "@openclaw/demo-plugin" },
release: { publishToNpm: true },
},
publishTo: "npm",
});
expect(
@@ -659,18 +601,9 @@ describe("collectPublishablePluginPackages", () => {
it("rejects extended-stable plugins whose version differs from core", () => {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-");
writeJsonFile(join(repoDir, "package.json"), { version: "2026.7.34" });
writePluginReadme(repoDir, "demo-plugin");
writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.7.33",
type: "module",
repository: { type: "git", url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL },
openclaw: {
extensions: ["./index.ts"],
...externalPluginContract("2026.7.33"),
install: { npmSpec: "@openclaw/demo-plugin" },
release: { publishToNpm: true },
},
publishTo: "npm",
});
expect(() =>
@@ -680,29 +613,13 @@ describe("collectPublishablePluginPackages", () => {
it("collects exact release dependencies that must match npm latest", () => {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-");
mkdirSync(join(repoDir, "extensions", "demo-plugin"), { recursive: true });
writePluginReadme(repoDir, "demo-plugin");
writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.4.10",
type: "module",
repository: {
type: "git",
url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL,
},
dependencies: {
"demo-runtime": "1.2.3",
},
openclaw: {
extensions: ["./index.ts"],
...externalPluginContract("2026.4.10"),
install: {
npmSpec: "@openclaw/demo-plugin",
},
release: {
publishToNpm: true,
requireLatestDependencies: ["demo-runtime"],
},
publishTo: "npm",
dependency: {
packageName: "demo-runtime",
version: "1.2.3",
requireLatest: true,
},
});
@@ -727,26 +644,9 @@ describe("collectPublishablePluginPackages", () => {
it("does not validate unselected publishable plugin manifests", () => {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-");
mkdirSync(join(repoDir, "extensions", "demo-plugin"), { recursive: true });
writePluginReadme(repoDir, "demo-plugin");
writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.4.10-beta.1",
type: "module",
repository: {
type: "git",
url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL,
},
openclaw: {
extensions: ["./index.ts"],
...externalPluginContract("2026.4.10-beta.1"),
install: {
npmSpec: "@openclaw/demo-plugin",
},
release: {
publishToNpm: true,
},
},
publishTo: "npm",
});
mkdirSync(join(repoDir, "extensions", "private-plugin"), { recursive: true });
writeJsonFile(join(repoDir, "extensions", "private-plugin", "package.json"), {
@@ -807,26 +707,9 @@ describe("collectPublishablePluginPackages", () => {
it("publishes alpha plugin packages to the alpha dist-tag", () => {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-");
mkdirSync(join(repoDir, "extensions", "demo-plugin"), { recursive: true });
writePluginReadme(repoDir, "demo-plugin");
writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.4.10-alpha.1",
type: "module",
repository: {
type: "git",
url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL,
},
openclaw: {
extensions: ["./index.ts"],
...externalPluginContract("2026.4.10-alpha.1"),
install: {
npmSpec: "@openclaw/demo-plugin",
},
release: {
publishToNpm: true,
},
},
publishTo: "npm",
});
expect(collectPublishablePluginPackages(repoDir)).toEqual([
@@ -1,12 +1,11 @@
// Plugin release pretag pack check tests cover its script-local target and command routing.
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { OPENCLAW_PLUGIN_NPM_REPOSITORY_URL } from "../../scripts/lib/plugin-npm-release.ts";
import {
collectPluginReleasePretagPackTargets,
runPluginReleasePretagPackCheck,
} from "../../scripts/plugin-release-pretag-pack-check.ts";
import { writePublishablePluginFixture } from "../helpers/publishable-plugin-fixture.js";
import { cleanupTempDirs, makeTempRepoRoot, writeJsonFile } from "../helpers/temp-repo.js";
const { execFileSyncMock } = vi.hoisted(() => ({
@@ -36,37 +35,11 @@ afterEach(() => {
function createDualPublishPluginRepo() {
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-pretag-pack-");
const packageDir = join(repoDir, "extensions", "demo-plugin");
mkdirSync(packageDir, { recursive: true });
writeJsonFile(join(repoDir, "package.json"), { name: "openclaw-test-root", type: "module" });
writeJsonFile(join(packageDir, "package.json"), {
name: "@openclaw/demo-plugin",
writePublishablePluginFixture(repoDir, {
version: "2026.4.10",
type: "module",
repository: {
type: "git",
url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL,
},
openclaw: {
extensions: ["./index.ts"],
compat: {
pluginApi: ">=2026.4.10",
},
build: {
openclawVersion: "2026.4.10",
},
install: {
npmSpec: "@openclaw/demo-plugin",
},
release: {
publishToClawHub: true,
publishToNpm: true,
},
},
publishTo: "both",
});
writeFileSync(join(packageDir, "README.md"), "# Demo plugin\n");
writeFileSync(join(packageDir, "index.ts"), "export const demo = 1;\n");
return repoDir;
}