mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 08:52:21 +00:00
fix: plugin publishing no longer fails on invalid temp paths (#3344)
* fix: keep plugin inspector workspaces writable * test: cover inspector temp fallback by platform --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -1,12 +1,65 @@
|
||||
/* @vitest-environment node */
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildPublishInspectorRunCheckOptions,
|
||||
createPackageInspectorWorkspace,
|
||||
normalizeInspectorReportForPublish,
|
||||
} from "./packageInspectorNode";
|
||||
|
||||
const originalPlatform = process.platform;
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, "platform", { value: originalPlatform });
|
||||
});
|
||||
|
||||
describe("package inspector publish normalization", () => {
|
||||
it("falls back to /tmp when the configured temp directory is unavailable", async () => {
|
||||
Object.defineProperty(process, "platform", { value: "linux" });
|
||||
const attemptedPrefixes: string[] = [];
|
||||
const createTempDir = async (prefix: string) => {
|
||||
attemptedPrefixes.push(prefix);
|
||||
if (attemptedPrefixes.length === 1) {
|
||||
throw Object.assign(new Error("configured temp directory is unavailable"), {
|
||||
code: "ENOENT",
|
||||
});
|
||||
}
|
||||
return `${prefix}workspace`;
|
||||
};
|
||||
|
||||
await expect(
|
||||
createPackageInspectorWorkspace("/home/sbx_user1051", createTempDir),
|
||||
).resolves.toBe(path.join("/tmp", "clawhub-plugin-inspector-workspace"));
|
||||
expect(attemptedPrefixes).toEqual([
|
||||
path.join("/home/sbx_user1051", "clawhub-plugin-inspector-"),
|
||||
path.join("/tmp", "clawhub-plugin-inspector-"),
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not use the POSIX fallback on Windows", async () => {
|
||||
Object.defineProperty(process, "platform", { value: "win32" });
|
||||
const error = Object.assign(new Error("configured temp directory is unavailable"), {
|
||||
code: "ENOENT",
|
||||
});
|
||||
const createTempDir = async () => {
|
||||
throw error;
|
||||
};
|
||||
|
||||
await expect(createPackageInspectorWorkspace("C:\\Temp", createTempDir)).rejects.toBe(error);
|
||||
});
|
||||
|
||||
it("does not hide unrelated workspace creation errors", async () => {
|
||||
const error = Object.assign(new Error("temporary storage failed"), { code: "EIO" });
|
||||
const createTempDir = async () => {
|
||||
throw error;
|
||||
};
|
||||
|
||||
await expect(createPackageInspectorWorkspace("/home/sbx_user1051", createTempDir)).rejects.toBe(
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
it("targets latest stable OpenClaw for publish-time inspection", () => {
|
||||
expect(buildPublishInspectorRunCheckOptions("/tmp/plugin", "2026-07-30T00:00:00.000Z")).toEqual(
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use node";
|
||||
|
||||
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { v } from "convex/values";
|
||||
@@ -42,6 +42,9 @@ type InspectorReport = {
|
||||
};
|
||||
|
||||
const AUTHOR_REMEDIATION_DOCS_BASE = "https://docs.openclaw.ai/clawhub/plugin-validation-fixes";
|
||||
const PACKAGE_INSPECTOR_TEMP_PREFIX = "clawhub-plugin-inspector-";
|
||||
const SERVERLESS_TEMP_DIR = "/tmp";
|
||||
const TEMP_DIR_FALLBACK_ERROR_CODES = new Set(["EACCES", "ENOENT", "ENOTDIR", "EPERM", "EROFS"]);
|
||||
|
||||
const LEGACY_AUTHOR_REMEDIATION_SUMMARIES = {
|
||||
"channel-env-vars":
|
||||
@@ -144,6 +147,27 @@ export function buildPublishInspectorRunCheckOptions(root: string, generatedAt:
|
||||
};
|
||||
}
|
||||
|
||||
export async function createPackageInspectorWorkspace(
|
||||
preferredTempDir = tmpdir(),
|
||||
createTempDir: (prefix: string) => Promise<string> = mkdtemp,
|
||||
) {
|
||||
try {
|
||||
return await createTempDir(path.join(preferredTempDir, PACKAGE_INSPECTOR_TEMP_PREFIX));
|
||||
} catch (error) {
|
||||
const code =
|
||||
typeof error === "object" && error !== null && "code" in error ? error.code : undefined;
|
||||
if (
|
||||
process.platform === "win32" ||
|
||||
path.resolve(preferredTempDir) === SERVERLESS_TEMP_DIR ||
|
||||
typeof code !== "string" ||
|
||||
!TEMP_DIR_FALLBACK_ERROR_CODES.has(code)
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
return await createTempDir(path.join(SERVERLESS_TEMP_DIR, PACKAGE_INSPECTOR_TEMP_PREFIX));
|
||||
}
|
||||
}
|
||||
|
||||
export const runPackageInspectorForPublishInternal = internalAction({
|
||||
args: {
|
||||
packageName: v.string(),
|
||||
@@ -163,12 +187,9 @@ export const runPackageInspectorForPublishInternal = internalAction({
|
||||
metadata: inspectorMetadataValidator,
|
||||
}),
|
||||
handler: async (ctx, args) => {
|
||||
const root = path.join(
|
||||
tmpdir(),
|
||||
`clawhub-plugin-inspector-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
||||
);
|
||||
let root: string | undefined;
|
||||
try {
|
||||
await mkdir(root, { recursive: true });
|
||||
root = await createPackageInspectorWorkspace();
|
||||
for (const file of args.files) {
|
||||
const blob = await ctx.storage.get(file.storageId);
|
||||
if (!blob) {
|
||||
@@ -209,7 +230,9 @@ export const runPackageInspectorForPublishInternal = internalAction({
|
||||
},
|
||||
};
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
if (root) {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user