fix(release): plugin npm preflight no longer hangs on stalled registries (#110862)

* fix(release): bound plugin npm freshness lookups

* fix(release): fail closed on npm view timeouts

* test(release): fix npm timeout mock types
This commit is contained in:
xingzhou
2026-07-19 16:10:49 +08:00
committed by GitHub
parent 3a5da4faa8
commit 59124313f4
2 changed files with 116 additions and 6 deletions
+27 -5
View File
@@ -112,6 +112,7 @@ type PublishablePluginPackageCandidate<TPackageJson extends PluginPackageJson =
};
export const OPENCLAW_PLUGIN_NPM_REPOSITORY_URL = "https://github.com/openclaw/openclaw";
const PLUGIN_NPM_VIEW_TIMEOUT_MS = 60_000;
export function collectRequiredLatestDependencies(packageJson: PluginPackageJson): {
dependencies: RequiredLatestDependency[];
@@ -643,16 +644,34 @@ export function assertPluginReleaseVersionFloors(
export type NpmLatestVersionResolver = (packageName: string) => string;
function isNpmViewTimeoutError(error: unknown): error is Error & { code: "ETIMEDOUT" } {
return error instanceof Error && "code" in error && error.code === "ETIMEDOUT";
}
function runNpmView(args: string[]): string {
const tempDir = mkdtempSync(join(tmpdir(), "openclaw-plugin-npm-view-"));
const userconfigPath = join(tempDir, "npmrc");
writeFileSync(userconfigPath, "");
try {
return execFileSync("npm", ["view", ...args, "--userconfig", userconfigPath], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
}).trim();
try {
return execFileSync("npm", ["view", ...args, "--userconfig", userconfigPath], {
encoding: "utf8",
killSignal: "SIGKILL",
stdio: ["ignore", "pipe", "pipe"],
timeout: PLUGIN_NPM_VIEW_TIMEOUT_MS,
}).trim();
} catch (error) {
if (isNpmViewTimeoutError(error)) {
throw Object.assign(
new Error(`npm view timed out after ${PLUGIN_NPM_VIEW_TIMEOUT_MS}ms.`, {
cause: error,
}),
{ code: "ETIMEDOUT" as const },
);
}
throw error;
}
} finally {
rmSync(tempDir, { recursive: true, force: true });
}
@@ -721,7 +740,10 @@ function isPluginVersionPublished(packageName: string, version: string): boolean
try {
runNpmView([`${packageName}@${version}`, "version"]);
return true;
} catch {
} catch (error) {
if (isNpmViewTimeoutError(error)) {
throw error;
}
return false;
}
}