mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-22 13:18:09 +00:00
fix(release): tolerate npm propagation after publish
This commit is contained in:
@@ -39,6 +39,7 @@ export type NpmViewFields = {
|
||||
version?: string;
|
||||
distTagVersion?: string;
|
||||
integrity?: string;
|
||||
tarball?: string;
|
||||
};
|
||||
|
||||
type WorkflowRunSummary = {
|
||||
@@ -52,6 +53,10 @@ const DEFAULT_REPO = "openclaw/openclaw";
|
||||
const DEFAULT_CLAWHUB_REGISTRY = "https://clawhub.ai";
|
||||
const CLAWHUB_REQUEST_TIMEOUT_MS = 20_000;
|
||||
const CLAWHUB_RESPONSE_BODY_MAX_BYTES = 1024 * 1024;
|
||||
// Trusted publish can finish before npm registry metadata converges. Keep the
|
||||
// verifier on the same release train instead of forcing a republish/correction.
|
||||
const NPM_VIEW_ATTEMPTS = 30;
|
||||
const NPM_VIEW_RETRY_MAX_DELAY_MS = 10_000;
|
||||
|
||||
function isRecord(value: unknown): value is JsonRecord {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
@@ -83,6 +88,35 @@ function runCommandInherited(command: string, args: string[]): void {
|
||||
});
|
||||
}
|
||||
|
||||
export async function runNpmViewWithRetry(
|
||||
args: string[],
|
||||
options: {
|
||||
attempts?: number;
|
||||
delay?: (delayMs: number) => Promise<void>;
|
||||
run?: (args: string[]) => string;
|
||||
} = {},
|
||||
): Promise<string> {
|
||||
const attempts = options.attempts ?? NPM_VIEW_ATTEMPTS;
|
||||
const delay =
|
||||
options.delay ??
|
||||
((delayMs: number) => new Promise((resolveDelay) => setTimeout(resolveDelay, delayMs)));
|
||||
const run = options.run ?? ((npmArgs: string[]) => runCommand("npm", npmArgs));
|
||||
let lastError: unknown;
|
||||
|
||||
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
||||
try {
|
||||
return run([...args, "--prefer-online"]);
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
}
|
||||
if (attempt < attempts) {
|
||||
await delay(Math.min(attempt * 1000, NPM_VIEW_RETRY_MAX_DELAY_MS));
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
function parseJson(raw: string, label: string): unknown {
|
||||
try {
|
||||
return JSON.parse(raw) as unknown;
|
||||
@@ -99,6 +133,7 @@ export function parseNpmViewFields(raw: string, distTag: string): NpmViewFields
|
||||
version: readString(parsed[0]),
|
||||
distTagVersion: readString(parsed[1]),
|
||||
integrity: readString(parsed[2]),
|
||||
tarball: readString(parsed[3]),
|
||||
};
|
||||
}
|
||||
if (!isRecord(parsed)) {
|
||||
@@ -110,6 +145,7 @@ export function parseNpmViewFields(raw: string, distTag: string): NpmViewFields
|
||||
version: readString(parsed.version),
|
||||
distTagVersion: readString(parsed[`dist-tags.${distTag}`]) ?? readString(distTags?.[distTag]),
|
||||
integrity: readString(parsed["dist.integrity"]) ?? readString(dist?.integrity),
|
||||
tarball: readString(parsed["dist.tarball"]) ?? readString(dist?.tarball),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -269,13 +305,18 @@ async function fetchStatusWithRetry(url: string, method: "GET" | "HEAD"): Promis
|
||||
return response.status;
|
||||
}
|
||||
|
||||
function verifyNpmPackage(packageName: string, version: string, distTag: string): NpmViewFields {
|
||||
const raw = runCommand("npm", [
|
||||
async function verifyNpmPackage(
|
||||
packageName: string,
|
||||
version: string,
|
||||
distTag: string,
|
||||
): Promise<NpmViewFields> {
|
||||
const raw = await runNpmViewWithRetry([
|
||||
"view",
|
||||
`${packageName}@${version}`,
|
||||
"version",
|
||||
`dist-tags.${distTag}`,
|
||||
"dist.integrity",
|
||||
"dist.tarball",
|
||||
"--json",
|
||||
]);
|
||||
const fields = parseNpmViewFields(raw, distTag);
|
||||
@@ -292,6 +333,9 @@ function verifyNpmPackage(packageName: string, version: string, distTag: string)
|
||||
if (fields.integrity === undefined) {
|
||||
throw new Error(`${packageName}: npm dist.integrity missing for ${version}.`);
|
||||
}
|
||||
if (fields.tarball === undefined) {
|
||||
throw new Error(`${packageName}: npm dist.tarball missing for ${version}.`);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
@@ -500,7 +544,7 @@ export async function verifyBetaRelease(
|
||||
lines.push(`GitHub release OK: ${releaseUrl}`);
|
||||
}
|
||||
|
||||
const openclawNpm = verifyNpmPackage("openclaw", args.version, args.distTag);
|
||||
const openclawNpm = await verifyNpmPackage("openclaw", args.version, args.distTag);
|
||||
lines.push(`openclaw npm OK: ${args.version} (${args.distTag})`);
|
||||
|
||||
if (!args.skipPostpublish) {
|
||||
@@ -522,7 +566,7 @@ export async function verifyBetaRelease(
|
||||
packages: npmPlugins,
|
||||
});
|
||||
for (const plugin of npmPlugins) {
|
||||
verifyNpmPackage(plugin.packageName, args.version, args.distTag);
|
||||
await verifyNpmPackage(plugin.packageName, args.version, args.distTag);
|
||||
}
|
||||
lines.push(`plugin npm OK: ${npmPlugins.length}`);
|
||||
|
||||
@@ -644,6 +688,7 @@ export async function verifyBetaRelease(
|
||||
npmDistTag: args.distTag,
|
||||
pluginSelection: args.pluginSelection,
|
||||
openclawNpmIntegrity: openclawNpm.integrity,
|
||||
openclawNpmTarball: openclawNpm.tarball,
|
||||
githubReleaseUrl: releaseUrl ?? null,
|
||||
pluginNpmPackageCount: npmPlugins.length,
|
||||
clawHubPackageCount: clawHubPlugins.length,
|
||||
|
||||
Reference in New Issue
Block a user