From 8da6b67607b247aeef7148d9f35cce2cd6dbaed7 Mon Sep 17 00:00:00 2001 From: Arnab Saha Date: Thu, 7 May 2026 21:51:27 -0700 Subject: [PATCH] fix(doctor): clean up post-upgrade probe test temp dirs and skip plugins with unreadable package.json --- src/commands/doctor-post-upgrade.test.ts | 168 ++++++++++++----------- src/commands/doctor-post-upgrade.ts | 10 +- 2 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/commands/doctor-post-upgrade.test.ts b/src/commands/doctor-post-upgrade.test.ts index 71fe8d852c2..0b73261d04a 100644 --- a/src/commands/doctor-post-upgrade.test.ts +++ b/src/commands/doctor-post-upgrade.test.ts @@ -11,93 +11,101 @@ async function makeFixtureRoot(prefix: string): Promise { describe("runPostUpgradeProbes — plugin.entry_unresolved", () => { it("flags an enabled plugin whose declared entry does not exist on disk", async () => { const root = await makeFixtureRoot("entry-unresolved"); - const pluginDir = path.join(root, "user-plugins", "ghost"); - await fs.mkdir(pluginDir, { recursive: true }); - // Plugin package.json declares ./dist/index.js but no dist/. - await fs.writeFile( - path.join(pluginDir, "package.json"), - JSON.stringify({ - name: "ghost", - version: "0.0.1", - type: "module", - openclaw: { extensions: ["./dist/index.js"] }, - }), - "utf-8", - ); - await fs.writeFile( - path.join(pluginDir, "openclaw.plugin.json"), - JSON.stringify({ id: "ghost" }), - "utf-8", - ); + try { + const pluginDir = path.join(root, "user-plugins", "ghost"); + await fs.mkdir(pluginDir, { recursive: true }); + // Plugin package.json declares ./dist/index.js but no dist/. + await fs.writeFile( + path.join(pluginDir, "package.json"), + JSON.stringify({ + name: "ghost", + version: "0.0.1", + type: "module", + openclaw: { extensions: ["./dist/index.js"] }, + }), + "utf-8", + ); + await fs.writeFile( + path.join(pluginDir, "openclaw.plugin.json"), + JSON.stringify({ id: "ghost" }), + "utf-8", + ); - const installsPath = path.join(root, "plugins", "installs.json"); - await fs.mkdir(path.dirname(installsPath), { recursive: true }); - await fs.writeFile( - installsPath, - JSON.stringify({ - version: 1, - plugins: [ - { - pluginId: "ghost", - manifestPath: path.join(pluginDir, "openclaw.plugin.json"), - rootDir: pluginDir, - enabled: true, - packageJson: { path: "package.json" }, - }, - ], - }), - "utf-8", - ); + const installsPath = path.join(root, "plugins", "installs.json"); + await fs.mkdir(path.dirname(installsPath), { recursive: true }); + await fs.writeFile( + installsPath, + JSON.stringify({ + version: 1, + plugins: [ + { + pluginId: "ghost", + manifestPath: path.join(pluginDir, "openclaw.plugin.json"), + rootDir: pluginDir, + enabled: true, + packageJson: { path: "package.json" }, + }, + ], + }), + "utf-8", + ); - const report = await runPostUpgradeProbes({ installsPath }); - const finding = report.findings.find((f) => f.code === "plugin.entry_unresolved"); - expect(finding).toBeDefined(); - expect(finding?.level).toBe("error"); - expect(finding?.plugin).toBe("ghost"); - expect(finding?.entry).toBe("./dist/index.js"); + const report = await runPostUpgradeProbes({ installsPath }); + const finding = report.findings.find((f) => f.code === "plugin.entry_unresolved"); + expect(finding).toBeDefined(); + expect(finding?.level).toBe("error"); + expect(finding?.plugin).toBe("ghost"); + expect(finding?.entry).toBe("./dist/index.js"); + } finally { + await fs.rm(root, { recursive: true, force: true }); + } }); it("emits no entry_unresolved findings when the entry resolves", async () => { const root = await makeFixtureRoot("entry-ok"); - const pluginDir = path.join(root, "user-plugins", "good"); - await fs.mkdir(path.join(pluginDir, "dist"), { recursive: true }); - await fs.writeFile(path.join(pluginDir, "dist", "index.js"), "export default {};", "utf-8"); - await fs.writeFile( - path.join(pluginDir, "package.json"), - JSON.stringify({ - name: "good", - version: "0.0.1", - type: "module", - openclaw: { extensions: ["./dist/index.js"] }, - }), - "utf-8", - ); - await fs.writeFile( - path.join(pluginDir, "openclaw.plugin.json"), - JSON.stringify({ id: "good" }), - "utf-8", - ); + try { + const pluginDir = path.join(root, "user-plugins", "good"); + await fs.mkdir(path.join(pluginDir, "dist"), { recursive: true }); + await fs.writeFile(path.join(pluginDir, "dist", "index.js"), "export default {};", "utf-8"); + await fs.writeFile( + path.join(pluginDir, "package.json"), + JSON.stringify({ + name: "good", + version: "0.0.1", + type: "module", + openclaw: { extensions: ["./dist/index.js"] }, + }), + "utf-8", + ); + await fs.writeFile( + path.join(pluginDir, "openclaw.plugin.json"), + JSON.stringify({ id: "good" }), + "utf-8", + ); - const installsPath = path.join(root, "plugins", "installs.json"); - await fs.mkdir(path.dirname(installsPath), { recursive: true }); - await fs.writeFile( - installsPath, - JSON.stringify({ - version: 1, - plugins: [ - { - pluginId: "good", - manifestPath: path.join(pluginDir, "openclaw.plugin.json"), - rootDir: pluginDir, - enabled: true, - packageJson: { path: "package.json" }, - }, - ], - }), - "utf-8", - ); + const installsPath = path.join(root, "plugins", "installs.json"); + await fs.mkdir(path.dirname(installsPath), { recursive: true }); + await fs.writeFile( + installsPath, + JSON.stringify({ + version: 1, + plugins: [ + { + pluginId: "good", + manifestPath: path.join(pluginDir, "openclaw.plugin.json"), + rootDir: pluginDir, + enabled: true, + packageJson: { path: "package.json" }, + }, + ], + }), + "utf-8", + ); - const report = await runPostUpgradeProbes({ installsPath }); - expect(report.findings.filter((f) => f.code === "plugin.entry_unresolved")).toHaveLength(0); + const report = await runPostUpgradeProbes({ installsPath }); + expect(report.findings.filter((f) => f.code === "plugin.entry_unresolved")).toHaveLength(0); + } finally { + await fs.rm(root, { recursive: true, force: true }); + } }); }); diff --git a/src/commands/doctor-post-upgrade.ts b/src/commands/doctor-post-upgrade.ts index 3fb7d3842ec..5430c09d761 100644 --- a/src/commands/doctor-post-upgrade.ts +++ b/src/commands/doctor-post-upgrade.ts @@ -36,7 +36,15 @@ export async function runPostUpgradeProbes(params: { for (const record of installs.plugins) { if (!record.enabled) continue; const pkgRelPath = record.packageJson?.path ?? "package.json"; - const pkg = await readInstalledPackageJson(record.rootDir, pkgRelPath); + let pkg: { openclaw?: { extensions?: string[] } }; + try { + pkg = await readInstalledPackageJson(record.rootDir, pkgRelPath); + } catch (err) { + process.stderr.write( + `[doctor-post-upgrade] could not read package.json for ${record.pluginId} at ${record.rootDir}: ${err instanceof Error ? err.message : String(err)}\n`, + ); + continue; + } const entries = pkg.openclaw?.extensions ?? []; for (const entry of entries) { const absEntry = path.resolve(record.rootDir, entry);