fix: rely on ClawHub plugin publish checks

This commit is contained in:
Patrick Erichsen
2026-06-11 11:45:06 -07:00
parent 575cae59d4
commit 9827490f5f
4 changed files with 0 additions and 199 deletions
@@ -197,12 +197,6 @@ jobs:
exit 1
fi
- name: Verify OpenClaw ClawHub package ownership
if: steps.plan.outputs.has_candidates == 'true'
env:
CLAWHUB_REGISTRY: ${{ env.CLAWHUB_REGISTRY }}
run: node --import tsx scripts/plugin-clawhub-owner-preflight.ts .local/plugin-clawhub-release-plan.json
validate_release_publish_approval:
name: Validate release publish approval
needs: preview_plugins_clawhub
-75
View File
@@ -2,7 +2,6 @@
import { execFileSync } from "node:child_process";
import { resolve } from "node:path";
import { validateExternalCodePluginPackageJson } from "../../packages/plugin-package-contract/src/index.ts";
import { readBoundedResponseText } from "./bounded-response.ts";
import {
collectExtensionPackageJsonCandidates,
collectChangedPathsFromGitRange,
@@ -64,19 +63,12 @@ type PluginReleasePlan = {
skippedPublished: PluginReleasePlanItem[];
};
type ClawHubPackageOwnerDetail = {
owner?: {
handle?: unknown;
} | null;
};
type ClawHubPublishablePluginPackageFilters = {
extensionIds?: readonly string[];
packageNames?: readonly string[];
};
const CLAWHUB_DEFAULT_REGISTRY = "https://clawhub.ai";
const CLAWHUB_RESPONSE_BODY_MAX_BYTES = 1024 * 1024;
const SAFE_EXTENSION_ID_RE = /^[a-z0-9][a-z0-9._-]*$/;
const CLAWHUB_SHARED_RELEASE_INPUT_PATHS = [
".github/workflows/plugin-clawhub-release.yml",
@@ -88,7 +80,6 @@ const CLAWHUB_SHARED_RELEASE_INPUT_PATHS = [
"scripts/lib/npm-publish-plan.mjs",
"scripts/lib/plugin-npm-release.ts",
"scripts/lib/plugin-clawhub-release.ts",
"scripts/plugin-clawhub-owner-preflight.ts",
"scripts/openclaw-npm-release-check.ts",
"scripts/plugin-clawhub-publish.sh",
"scripts/plugin-clawhub-release-check.ts",
@@ -114,19 +105,6 @@ function formatClawHubPackageArtifactName(
return `clawhub-package-${safeName}-${plugin.version}`;
}
async function readClawHubPackageOwnerDetail(
response: Response,
packageName: string,
): Promise<ClawHubPackageOwnerDetail> {
return JSON.parse(
await readBoundedResponseText(
response,
`ClawHub package owner response for ${packageName}`,
CLAWHUB_RESPONSE_BODY_MAX_BYTES,
),
) as ClawHubPackageOwnerDetail;
}
export function collectClawHubPublishablePluginPackages(
rootDir = resolve("."),
filters: ClawHubPublishablePluginPackageFilters = {},
@@ -379,59 +357,6 @@ async function isPluginVersionPublishedOnClawHub(
);
}
export async function collectClawHubOpenClawOwnerErrors(params: {
plugins: readonly Pick<PublishablePluginPackage, "packageName">[];
requiredOwnerHandle?: string;
registryBaseUrl?: string;
fetchImpl?: typeof fetch;
}): Promise<string[]> {
const fetchImpl = params.fetchImpl ?? fetch;
const requiredOwnerHandle = params.requiredOwnerHandle ?? "openclaw";
const errors: string[] = [];
await Promise.all(
params.plugins.map(async (plugin) => {
if (!plugin.packageName.startsWith("@openclaw/")) {
return;
}
const url = new URL(
`/api/v1/packages/${encodeURIComponent(plugin.packageName)}`,
getRegistryBaseUrl(params.registryBaseUrl),
);
const response = await fetchImpl(url, {
method: "GET",
headers: {
Accept: "application/json",
},
});
if (response.status === 404) {
errors.push(
`${plugin.packageName}: ClawHub package row must already exist under @${requiredOwnerHandle} before OpenClaw release publish.`,
);
return;
}
if (!response.ok) {
errors.push(
`${plugin.packageName}: failed to query ClawHub owner: ${response.status} ${response.statusText}`,
);
return;
}
const detail = await readClawHubPackageOwnerDetail(response, plugin.packageName);
const ownerHandle = typeof detail.owner?.handle === "string" ? detail.owner.handle : null;
if (ownerHandle !== requiredOwnerHandle) {
errors.push(
`${plugin.packageName}: ClawHub package owner must be @${requiredOwnerHandle}; got ${ownerHandle ? `@${ownerHandle}` : "<missing>"}.`,
);
}
}),
);
return errors.toSorted();
}
export async function collectPluginClawHubReleasePlan(params?: {
rootDir?: string;
selection?: string[];
-45
View File
@@ -1,45 +0,0 @@
#!/usr/bin/env -S node --import tsx
// Plugin Clawhub Owner Preflight script supports OpenClaw repository automation.
import { readFileSync } from "node:fs";
import { pathToFileURL } from "node:url";
import { collectClawHubOpenClawOwnerErrors } from "./lib/plugin-clawhub-release.ts";
type ReleasePlanFile = {
candidates?: Array<{
packageName?: unknown;
}>;
};
export async function runClawHubOwnerPreflight(argv: string[]) {
const planPath = argv[0];
if (!planPath) {
throw new Error("usage: plugin-clawhub-owner-preflight.ts <release-plan.json>");
}
const parsed = JSON.parse(readFileSync(planPath, "utf8")) as ReleasePlanFile;
const candidates = (parsed.candidates ?? [])
.filter(
(candidate): candidate is { packageName: string } =>
typeof candidate.packageName === "string",
)
.map((candidate) => ({ packageName: candidate.packageName }));
const errors = await collectClawHubOpenClawOwnerErrors({ plugins: candidates });
if (errors.length > 0) {
throw new Error(
`ClawHub OpenClaw package ownership preflight failed:\n${errors.map((error) => `- ${error}`).join("\n")}`,
);
}
console.log(`ClawHub OpenClaw owner preflight passed for ${candidates.length} candidate(s).`);
}
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
try {
await runClawHubOwnerPreflight(process.argv.slice(2));
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}
}
-73
View File
@@ -12,7 +12,6 @@ import { delimiter, join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
collectClawHubPublishablePluginPackages,
collectClawHubOpenClawOwnerErrors,
collectClawHubVersionGateErrors,
collectPluginClawHubReleasePathsFromGitRange,
collectPluginClawHubReleasePlan,
@@ -381,78 +380,6 @@ describe("collectPluginClawHubReleasePlan", () => {
});
});
describe("collectClawHubOpenClawOwnerErrors", () => {
it("requires OpenClaw-scoped release candidates to already belong to the OpenClaw publisher", async () => {
const errors = await collectClawHubOpenClawOwnerErrors({
plugins: [
{ packageName: "@openclaw/demo-plugin" },
{ packageName: "@openclaw/missing-plugin" },
{ packageName: "@other/safe-plugin" },
],
registryBaseUrl: "https://clawhub.ai",
fetchImpl: async (url) => {
const pathname = new URL(url instanceof Request ? url.url : url).pathname;
if (pathname.includes("%40openclaw%2Fmissing-plugin")) {
return new Response("not found", { status: 404 });
}
return new Response(
JSON.stringify({
owner: { handle: "steipete" },
}),
{ status: 200, headers: { "Content-Type": "application/json" } },
);
},
});
expect(errors).toEqual([
"@openclaw/demo-plugin: ClawHub package owner must be @openclaw; got @steipete.",
"@openclaw/missing-plugin: ClawHub package row must already exist under @openclaw before OpenClaw release publish.",
]);
});
it("passes when OpenClaw-scoped release candidates belong to the OpenClaw publisher", async () => {
const errors = await collectClawHubOpenClawOwnerErrors({
plugins: [{ packageName: "@openclaw/demo-plugin" }],
registryBaseUrl: "https://clawhub.ai",
fetchImpl: async () =>
new Response(JSON.stringify({ owner: { handle: "openclaw" } }), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
});
expect(errors).toStrictEqual([]);
});
it("bounds ClawHub owner metadata response bodies", async () => {
await expect(
collectClawHubOpenClawOwnerErrors({
plugins: [{ packageName: "@openclaw/demo-plugin" }],
registryBaseUrl: "https://clawhub.ai",
fetchImpl: async () =>
new Response("{}", {
status: 200,
headers: { "content-length": String(1024 * 1024 + 1) },
}),
}),
).rejects.toThrow(
"ClawHub package owner response for @openclaw/demo-plugin response body exceeded 1048576 bytes.",
);
});
it("bounds streamed ClawHub owner metadata bodies", async () => {
await expect(
collectClawHubOpenClawOwnerErrors({
plugins: [{ packageName: "@openclaw/demo-plugin" }],
registryBaseUrl: "https://clawhub.ai",
fetchImpl: async () => new Response("x".repeat(1024 * 1024 + 1), { status: 200 }),
}),
).rejects.toThrow(
"ClawHub package owner response for @openclaw/demo-plugin response body exceeded 1048576 bytes.",
);
});
});
describe("plugin-clawhub-publish.sh", () => {
it("previews the publish command through the ClawHub CLI dry-run preflight", () => {
const repoDir = createTempPluginRepo();