mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
fix: isolate npm plugin installs per package (#87647)
* fix: isolate npm plugin installs per package * test: assert isolated npm plugin projects in upgrade survivor * test: assert plugin lifecycle npm project roots * test: resolve npm project deps in live assertions * fix: resolve codex bins from isolated npm projects * docs: document isolated npm plugin projects * ci: configure testbox workflow for crabbox * fix: stabilize npm project fingerprint * fix: keep fetch runtime import side-effect free * test: keep dynamic live model unit hermetic * ci: handle empty node toolcache roots * test: make nounset toolcache probe deterministic
This commit is contained in:
committed by
GitHub
parent
2b587be44d
commit
ea682182d0
@@ -4,6 +4,7 @@ import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
listManagedPluginNpmRoots: vi.fn(),
|
||||
repairMissingConfiguredPluginInstalls: vi.fn(),
|
||||
relinkOpenClawPeerDependenciesInManagedNpmRoot: vi.fn(),
|
||||
runPluginPayloadSmokeCheck: vi.fn(),
|
||||
@@ -16,6 +17,9 @@ vi.mock("../../plugins/plugin-peer-link.js", () => ({
|
||||
relinkOpenClawPeerDependenciesInManagedNpmRoot:
|
||||
mocks.relinkOpenClawPeerDependenciesInManagedNpmRoot,
|
||||
}));
|
||||
vi.mock("../../plugins/npm-project-roots.js", () => ({
|
||||
listManagedPluginNpmRoots: mocks.listManagedPluginNpmRoots,
|
||||
}));
|
||||
vi.mock("./plugin-payload-validation.js", () => ({
|
||||
runPluginPayloadSmokeCheck: mocks.runPluginPayloadSmokeCheck,
|
||||
}));
|
||||
@@ -33,6 +37,9 @@ describe("runPostCorePluginConvergence", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mocks.listManagedPluginNpmRoots.mockImplementation((npmRoot: string) =>
|
||||
Promise.resolve([npmRoot]),
|
||||
);
|
||||
mocks.repairMissingConfiguredPluginInstalls.mockResolvedValue({
|
||||
changes: [],
|
||||
warnings: [],
|
||||
@@ -148,28 +155,43 @@ describe("runPostCorePluginConvergence", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("repairs managed npm openclaw peer links before payload smoke checks", async () => {
|
||||
it("repairs managed npm openclaw peer links in every managed npm project before payload smoke checks", async () => {
|
||||
mocks.repairMissingConfiguredPluginInstalls.mockResolvedValue({
|
||||
changes: [],
|
||||
warnings: [],
|
||||
records: { codex: { source: "npm", installPath: "/p/codex" } },
|
||||
});
|
||||
mocks.relinkOpenClawPeerDependenciesInManagedNpmRoot.mockResolvedValue({
|
||||
checked: 1,
|
||||
attempted: 1,
|
||||
repaired: 1,
|
||||
skipped: 0,
|
||||
});
|
||||
mocks.listManagedPluginNpmRoots.mockResolvedValue([
|
||||
"/tmp/openclaw-state/npm",
|
||||
"/tmp/openclaw-state/npm/projects/codex",
|
||||
]);
|
||||
mocks.relinkOpenClawPeerDependenciesInManagedNpmRoot
|
||||
.mockResolvedValueOnce({
|
||||
checked: 0,
|
||||
attempted: 0,
|
||||
repaired: 0,
|
||||
skipped: 0,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
checked: 1,
|
||||
attempted: 1,
|
||||
repaired: 1,
|
||||
skipped: 0,
|
||||
});
|
||||
|
||||
const result = await runPostCorePluginConvergence({
|
||||
cfg: { plugins: { entries: { codex: { enabled: true } } } } as unknown as OpenClawConfig,
|
||||
env: { OPENCLAW_STATE_DIR: "/tmp/openclaw-state" },
|
||||
});
|
||||
|
||||
expect(mocks.relinkOpenClawPeerDependenciesInManagedNpmRoot).toHaveBeenCalledWith({
|
||||
expect(mocks.relinkOpenClawPeerDependenciesInManagedNpmRoot).toHaveBeenNthCalledWith(1, {
|
||||
npmRoot: "/tmp/openclaw-state/npm",
|
||||
logger: {},
|
||||
});
|
||||
expect(mocks.relinkOpenClawPeerDependenciesInManagedNpmRoot).toHaveBeenNthCalledWith(2, {
|
||||
npmRoot: "/tmp/openclaw-state/npm/projects/codex",
|
||||
logger: {},
|
||||
});
|
||||
expect(result.changes).toEqual([
|
||||
"Repaired OpenClaw host peer link(s) for 1 managed npm plugin package(s).",
|
||||
]);
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { PluginInstallRecord } from "../../config/types.plugins.js";
|
||||
import { normalizePluginsConfig, resolveEffectiveEnableState } from "../../plugins/config-state.js";
|
||||
import { resolveDefaultPluginNpmDir } from "../../plugins/install-paths.js";
|
||||
import { listManagedPluginNpmRoots } from "../../plugins/npm-project-roots.js";
|
||||
import { relinkOpenClawPeerDependenciesInManagedNpmRoot } from "../../plugins/plugin-peer-link.js";
|
||||
import { pruneStaleLocalBundledPluginInstallRecords } from "../../plugins/stale-local-bundled-plugin-install-records.js";
|
||||
import {
|
||||
@@ -49,16 +50,20 @@ async function repairManagedNpmOpenClawPeerLinks(params: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): Promise<{ changes: string[]; warnings: PostCoreConvergenceWarning[] }> {
|
||||
try {
|
||||
const result = await relinkOpenClawPeerDependenciesInManagedNpmRoot({
|
||||
npmRoot: resolveDefaultPluginNpmDir(params.env),
|
||||
logger: {},
|
||||
});
|
||||
const npmRoots = await listManagedPluginNpmRoots(resolveDefaultPluginNpmDir(params.env));
|
||||
const results = await Promise.all(
|
||||
npmRoots.map((npmRoot) =>
|
||||
relinkOpenClawPeerDependenciesInManagedNpmRoot({
|
||||
npmRoot,
|
||||
logger: {},
|
||||
}),
|
||||
),
|
||||
);
|
||||
const repaired = results.reduce((total, result) => total + result.repaired, 0);
|
||||
return {
|
||||
changes:
|
||||
result.repaired > 0
|
||||
? [
|
||||
`Repaired OpenClaw host peer link(s) for ${result.repaired} managed npm plugin package(s).`,
|
||||
]
|
||||
repaired > 0
|
||||
? [`Repaired OpenClaw host peer link(s) for ${repaired} managed npm plugin package(s).`]
|
||||
: [],
|
||||
warnings: [],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user