fix: fail update on plugin sync errors

This commit is contained in:
Peter Steinberger
2026-04-26 09:00:58 +01:00
parent 4823288b3b
commit a434133aac
6 changed files with 207 additions and 23 deletions

View File

@@ -71,8 +71,10 @@ function createNpmInstallConfig(params: {
spec: string;
installPath: string;
integrity?: string;
shasum?: string;
resolvedName?: string;
resolvedSpec?: string;
resolvedVersion?: string;
}) {
return {
plugins: {
@@ -82,8 +84,10 @@ function createNpmInstallConfig(params: {
spec: params.spec,
installPath: params.installPath,
...(params.integrity ? { integrity: params.integrity } : {}),
...(params.shasum ? { shasum: params.shasum } : {}),
...(params.resolvedName ? { resolvedName: params.resolvedName } : {}),
...(params.resolvedSpec ? { resolvedSpec: params.resolvedSpec } : {}),
...(params.resolvedVersion ? { resolvedVersion: params.resolvedVersion } : {}),
},
},
},
@@ -412,6 +416,55 @@ describe("updateNpmInstalledPlugins", () => {
]);
});
it("refreshes legacy npm install records before skipping unchanged artifacts", async () => {
const installPath = createInstalledPackageDir({
name: "@martian-engineering/lossless-claw",
version: "0.9.0",
});
mockNpmViewMetadata({
name: "@martian-engineering/lossless-claw",
version: "0.9.0",
integrity: "sha512-same",
shasum: "same",
});
installPluginFromNpmSpecMock.mockResolvedValue(
createSuccessfulNpmUpdateResult({
pluginId: "lossless-claw",
targetDir: installPath,
version: "0.9.0",
npmResolution: {
name: "@martian-engineering/lossless-claw",
version: "0.9.0",
resolvedSpec: "@martian-engineering/lossless-claw@0.9.0",
},
}),
);
const result = await updateNpmInstalledPlugins({
config: createNpmInstallConfig({
pluginId: "lossless-claw",
spec: "@martian-engineering/lossless-claw",
installPath,
}),
pluginIds: ["lossless-claw"],
});
expect(installPluginFromNpmSpecMock).toHaveBeenCalledTimes(1);
expect(result.changed).toBe(true);
expect(result.outcomes[0]).toMatchObject({
pluginId: "lossless-claw",
status: "unchanged",
currentVersion: "0.9.0",
nextVersion: "0.9.0",
});
expect(result.config.plugins?.installs?.["lossless-claw"]).toMatchObject({
source: "npm",
resolvedName: "@martian-engineering/lossless-claw",
resolvedVersion: "0.9.0",
resolvedSpec: "@martian-engineering/lossless-claw@0.9.0",
});
});
it("expands home-relative install paths before checking installed npm versions", async () => {
const home = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-update-home-"));
tempDirs.push(home);
@@ -436,8 +489,10 @@ describe("updateNpmInstalledPlugins", () => {
spec: "@martian-engineering/lossless-claw",
installPath: "~/.openclaw/extensions/lossless-claw",
resolvedName: "@martian-engineering/lossless-claw",
resolvedVersion: "0.9.0",
resolvedSpec: "@martian-engineering/lossless-claw@0.9.0",
integrity: "sha512-same",
shasum: "same",
}),
pluginIds: ["lossless-claw"],
});

View File

@@ -115,10 +115,6 @@ type InstallIntegrityDrift = {
};
};
function stringFieldMatches(recorded: string | undefined, resolved: string | undefined): boolean {
return !recorded || (resolved !== undefined && recorded === resolved);
}
function shouldSkipUnchangedNpmInstall(params: {
currentVersion?: string;
record: {
@@ -136,12 +132,28 @@ function shouldSkipUnchangedNpmInstall(params: {
if (params.currentVersion !== params.metadata.version) {
return false;
}
if (
!params.record.resolvedName ||
!params.record.resolvedSpec ||
!params.record.resolvedVersion
) {
return false;
}
if (!params.metadata.name || !params.metadata.resolvedSpec) {
return false;
}
if (params.metadata.integrity && !params.record.integrity) {
return false;
}
if (params.metadata.shasum && !params.record.shasum) {
return false;
}
return (
stringFieldMatches(params.record.integrity, params.metadata.integrity) &&
stringFieldMatches(params.record.shasum, params.metadata.shasum) &&
stringFieldMatches(params.record.resolvedName, params.metadata.name) &&
stringFieldMatches(params.record.resolvedSpec, params.metadata.resolvedSpec) &&
stringFieldMatches(params.record.resolvedVersion, params.metadata.version)
(!params.metadata.integrity || params.record.integrity === params.metadata.integrity) &&
(!params.metadata.shasum || params.record.shasum === params.metadata.shasum) &&
params.record.resolvedName === params.metadata.name &&
params.record.resolvedSpec === params.metadata.resolvedSpec &&
params.record.resolvedVersion === params.metadata.version
);
}