CLI: support versioned plugin updates (#49998)
Merged via squash. Prepared head SHA: 545ea60fa26bb742376237ca83c65665133bcf7c Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com> Reviewed-by: @huntharo
This commit is contained in:
parent
7fb142d115
commit
401ffb59f5
@ -164,6 +164,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- WhatsApp/active-listener: pin the active listener registry to a `globalThis` singleton so split WhatsApp bundle chunks share one listener map and outbound sends stop missing the registered session. (#47433) Thanks @clawdia67.
|
- WhatsApp/active-listener: pin the active listener registry to a `globalThis` singleton so split WhatsApp bundle chunks share one listener map and outbound sends stop missing the registered session. (#47433) Thanks @clawdia67.
|
||||||
- Plugins/WhatsApp: share split-load singleton state for plugin command registration and active WhatsApp listeners so duplicate module graphs no longer lose native plugin commands or outbound listener state. (#50418) Thanks @huntharo.
|
- Plugins/WhatsApp: share split-load singleton state for plugin command registration and active WhatsApp listeners so duplicate module graphs no longer lose native plugin commands or outbound listener state. (#50418) Thanks @huntharo.
|
||||||
- Onboarding/custom providers: keep Azure AI Foundry `*.services.ai.azure.com` custom endpoints on the selected compatibility path instead of forcing Responses, so chat-completions Foundry models still work after setup. Fixes #50528. (#50535) Thanks @obviyus.
|
- Onboarding/custom providers: keep Azure AI Foundry `*.services.ai.azure.com` custom endpoints on the selected compatibility path instead of forcing Responses, so chat-completions Foundry models still work after setup. Fixes #50528. (#50535) Thanks @obviyus.
|
||||||
|
- Plugins/update: let `openclaw plugins update <npm-spec>` target tracked npm installs by dist-tag or exact version, and preserve the recorded npm spec for later id-based updates. (#49998) Thanks @huntharo.
|
||||||
|
|
||||||
### Breaking
|
### Breaking
|
||||||
|
|
||||||
|
|||||||
@ -138,14 +138,24 @@ state dir extensions root (`$OPENCLAW_STATE_DIR/extensions/<id>`). Use
|
|||||||
### Update
|
### Update
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openclaw plugins update <id>
|
openclaw plugins update <id-or-npm-spec>
|
||||||
openclaw plugins update --all
|
openclaw plugins update --all
|
||||||
openclaw plugins update <id> --dry-run
|
openclaw plugins update <id-or-npm-spec> --dry-run
|
||||||
|
openclaw plugins update @openclaw/voice-call@beta
|
||||||
```
|
```
|
||||||
|
|
||||||
Updates apply to tracked installs in `plugins.installs`, currently npm and
|
Updates apply to tracked installs in `plugins.installs`, currently npm and
|
||||||
marketplace installs.
|
marketplace installs.
|
||||||
|
|
||||||
|
When you pass a plugin id, OpenClaw reuses the recorded install spec for that
|
||||||
|
plugin. That means previously stored dist-tags such as `@beta` and exact pinned
|
||||||
|
versions continue to be used on later `update <id>` runs.
|
||||||
|
|
||||||
|
For npm installs, you can also pass an explicit npm package spec with a dist-tag
|
||||||
|
or exact version. OpenClaw resolves that package name back to the tracked plugin
|
||||||
|
record, updates that installed plugin, and records the new npm spec for future
|
||||||
|
id-based updates.
|
||||||
|
|
||||||
When a stored integrity hash exists and the fetched artifact hash changes,
|
When a stored integrity hash exists and the fetched artifact hash changes,
|
||||||
OpenClaw prints a warning and asks for confirmation before proceeding. Use
|
OpenClaw prints a warning and asks for confirmation before proceeding. Use
|
||||||
global `--yes` to bypass prompts in CI/non-interactive runs.
|
global `--yes` to bypass prompts in CI/non-interactive runs.
|
||||||
|
|||||||
@ -286,7 +286,7 @@ openclaw plugins install ./plugin.zip # install from a local zip
|
|||||||
openclaw plugins install -l ./extensions/voice-call # link (no copy) for dev
|
openclaw plugins install -l ./extensions/voice-call # link (no copy) for dev
|
||||||
openclaw plugins install @openclaw/voice-call # install from npm
|
openclaw plugins install @openclaw/voice-call # install from npm
|
||||||
openclaw plugins install @openclaw/voice-call --pin # store exact resolved name@version
|
openclaw plugins install @openclaw/voice-call --pin # store exact resolved name@version
|
||||||
openclaw plugins update <id>
|
openclaw plugins update <id-or-npm-spec>
|
||||||
openclaw plugins update --all
|
openclaw plugins update --all
|
||||||
openclaw plugins enable <id>
|
openclaw plugins enable <id>
|
||||||
openclaw plugins disable <id>
|
openclaw plugins disable <id>
|
||||||
|
|||||||
@ -379,6 +379,140 @@ describe("plugins cli", () => {
|
|||||||
expect(runtimeLogs.at(-1)).toBe("No tracked plugins to update.");
|
expect(runtimeLogs.at(-1)).toBe("No tracked plugins to update.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("maps an explicit unscoped npm dist-tag update to the tracked plugin id", async () => {
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
installs: {
|
||||||
|
"openclaw-codex-app-server": {
|
||||||
|
source: "npm",
|
||||||
|
spec: "openclaw-codex-app-server",
|
||||||
|
installPath: "/tmp/openclaw-codex-app-server",
|
||||||
|
resolvedName: "openclaw-codex-app-server",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
loadConfig.mockReturnValue(config);
|
||||||
|
updateNpmInstalledPlugins.mockResolvedValue({
|
||||||
|
config,
|
||||||
|
changed: false,
|
||||||
|
outcomes: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await runCommand(["plugins", "update", "openclaw-codex-app-server@beta"]);
|
||||||
|
|
||||||
|
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
config,
|
||||||
|
pluginIds: ["openclaw-codex-app-server"],
|
||||||
|
specOverrides: {
|
||||||
|
"openclaw-codex-app-server": "openclaw-codex-app-server@beta",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("maps an explicit scoped npm dist-tag update to the tracked plugin id", async () => {
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
installs: {
|
||||||
|
"voice-call": {
|
||||||
|
source: "npm",
|
||||||
|
spec: "@openclaw/voice-call",
|
||||||
|
installPath: "/tmp/voice-call",
|
||||||
|
resolvedName: "@openclaw/voice-call",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
loadConfig.mockReturnValue(config);
|
||||||
|
updateNpmInstalledPlugins.mockResolvedValue({
|
||||||
|
config,
|
||||||
|
changed: false,
|
||||||
|
outcomes: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await runCommand(["plugins", "update", "@openclaw/voice-call@beta"]);
|
||||||
|
|
||||||
|
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
config,
|
||||||
|
pluginIds: ["voice-call"],
|
||||||
|
specOverrides: {
|
||||||
|
"voice-call": "@openclaw/voice-call@beta",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("maps an explicit npm version update to the tracked plugin id", async () => {
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
installs: {
|
||||||
|
"openclaw-codex-app-server": {
|
||||||
|
source: "npm",
|
||||||
|
spec: "openclaw-codex-app-server",
|
||||||
|
installPath: "/tmp/openclaw-codex-app-server",
|
||||||
|
resolvedName: "openclaw-codex-app-server",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
loadConfig.mockReturnValue(config);
|
||||||
|
updateNpmInstalledPlugins.mockResolvedValue({
|
||||||
|
config,
|
||||||
|
changed: false,
|
||||||
|
outcomes: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await runCommand(["plugins", "update", "openclaw-codex-app-server@0.2.0-beta.4"]);
|
||||||
|
|
||||||
|
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
config,
|
||||||
|
pluginIds: ["openclaw-codex-app-server"],
|
||||||
|
specOverrides: {
|
||||||
|
"openclaw-codex-app-server": "openclaw-codex-app-server@0.2.0-beta.4",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps using the recorded npm tag when update is invoked by plugin id", async () => {
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
installs: {
|
||||||
|
"openclaw-codex-app-server": {
|
||||||
|
source: "npm",
|
||||||
|
spec: "openclaw-codex-app-server@beta",
|
||||||
|
installPath: "/tmp/openclaw-codex-app-server",
|
||||||
|
resolvedName: "openclaw-codex-app-server",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
loadConfig.mockReturnValue(config);
|
||||||
|
updateNpmInstalledPlugins.mockResolvedValue({
|
||||||
|
config,
|
||||||
|
changed: false,
|
||||||
|
outcomes: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await runCommand(["plugins", "update", "openclaw-codex-app-server"]);
|
||||||
|
|
||||||
|
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
config,
|
||||||
|
pluginIds: ["openclaw-codex-app-server"],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(updateNpmInstalledPlugins).not.toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
specOverrides: expect.anything(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("writes updated config when updater reports changes", async () => {
|
it("writes updated config when updater reports changes", async () => {
|
||||||
const cfg = {
|
const cfg = {
|
||||||
plugins: {
|
plugins: {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { loadConfig, writeConfigFile } from "../config/config.js";
|
|||||||
import { resolveStateDir } from "../config/paths.js";
|
import { resolveStateDir } from "../config/paths.js";
|
||||||
import type { PluginInstallRecord } from "../config/types.plugins.js";
|
import type { PluginInstallRecord } from "../config/types.plugins.js";
|
||||||
import { resolveArchiveKind } from "../infra/archive.js";
|
import { resolveArchiveKind } from "../infra/archive.js";
|
||||||
|
import { parseRegistryNpmSpec } from "../infra/npm-registry-spec.js";
|
||||||
import { type BundledPluginSource, findBundledPluginSource } from "../plugins/bundled-sources.js";
|
import { type BundledPluginSource, findBundledPluginSource } from "../plugins/bundled-sources.js";
|
||||||
import { enablePluginInConfig } from "../plugins/enable.js";
|
import { enablePluginInConfig } from "../plugins/enable.js";
|
||||||
import { installPluginFromNpmSpec, installPluginFromPath } from "../plugins/install.js";
|
import { installPluginFromNpmSpec, installPluginFromPath } from "../plugins/install.js";
|
||||||
@ -227,6 +228,56 @@ function createPluginInstallLogger(): { info: (msg: string) => void; warn: (msg:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractInstalledNpmPackageName(install: PluginInstallRecord): string | undefined {
|
||||||
|
if (install.source !== "npm") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const resolvedName = install.resolvedName?.trim();
|
||||||
|
if (resolvedName) {
|
||||||
|
return resolvedName;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
(install.spec ? parseRegistryNpmSpec(install.spec)?.name : undefined) ??
|
||||||
|
(install.resolvedSpec ? parseRegistryNpmSpec(install.resolvedSpec)?.name : undefined)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePluginUpdateSelection(params: {
|
||||||
|
installs: Record<string, PluginInstallRecord>;
|
||||||
|
rawId?: string;
|
||||||
|
all?: boolean;
|
||||||
|
}): { pluginIds: string[]; specOverrides?: Record<string, string> } {
|
||||||
|
if (params.all) {
|
||||||
|
return { pluginIds: Object.keys(params.installs) };
|
||||||
|
}
|
||||||
|
if (!params.rawId) {
|
||||||
|
return { pluginIds: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedSpec = parseRegistryNpmSpec(params.rawId);
|
||||||
|
if (!parsedSpec || parsedSpec.selectorKind === "none") {
|
||||||
|
return { pluginIds: [params.rawId] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const matches = Object.entries(params.installs).filter(([, install]) => {
|
||||||
|
return extractInstalledNpmPackageName(install) === parsedSpec.name;
|
||||||
|
});
|
||||||
|
if (matches.length !== 1) {
|
||||||
|
return { pluginIds: [params.rawId] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const [pluginId] = matches[0];
|
||||||
|
if (!pluginId) {
|
||||||
|
return { pluginIds: [params.rawId] };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
pluginIds: [pluginId],
|
||||||
|
specOverrides: {
|
||||||
|
[pluginId]: parsedSpec.raw,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function logSlotWarnings(warnings: string[]) {
|
function logSlotWarnings(warnings: string[]) {
|
||||||
if (warnings.length === 0) {
|
if (warnings.length === 0) {
|
||||||
return;
|
return;
|
||||||
@ -1032,7 +1083,12 @@ export function registerPluginsCli(program: Command) {
|
|||||||
.action(async (id: string | undefined, opts: PluginUpdateOptions) => {
|
.action(async (id: string | undefined, opts: PluginUpdateOptions) => {
|
||||||
const cfg = loadConfig();
|
const cfg = loadConfig();
|
||||||
const installs = cfg.plugins?.installs ?? {};
|
const installs = cfg.plugins?.installs ?? {};
|
||||||
const targets = opts.all ? Object.keys(installs) : id ? [id] : [];
|
const selection = resolvePluginUpdateSelection({
|
||||||
|
installs,
|
||||||
|
rawId: id,
|
||||||
|
all: opts.all,
|
||||||
|
});
|
||||||
|
const targets = selection.pluginIds;
|
||||||
|
|
||||||
if (targets.length === 0) {
|
if (targets.length === 0) {
|
||||||
if (opts.all) {
|
if (opts.all) {
|
||||||
@ -1046,6 +1102,7 @@ export function registerPluginsCli(program: Command) {
|
|||||||
const result = await updateNpmInstalledPlugins({
|
const result = await updateNpmInstalledPlugins({
|
||||||
config: cfg,
|
config: cfg,
|
||||||
pluginIds: targets,
|
pluginIds: targets,
|
||||||
|
specOverrides: selection.specOverrides,
|
||||||
dryRun: opts.dryRun,
|
dryRun: opts.dryRun,
|
||||||
logger: {
|
logger: {
|
||||||
info: (msg) => defaultRuntime.log(msg),
|
info: (msg) => defaultRuntime.log(msg),
|
||||||
|
|||||||
@ -161,6 +161,129 @@ describe("updateNpmInstalledPlugins", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reuses a recorded npm dist-tag spec for id-based updates", async () => {
|
||||||
|
installPluginFromNpmSpecMock.mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
pluginId: "openclaw-codex-app-server",
|
||||||
|
targetDir: "/tmp/openclaw-codex-app-server",
|
||||||
|
version: "0.2.0-beta.4",
|
||||||
|
extensions: ["index.ts"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await updateNpmInstalledPlugins({
|
||||||
|
config: {
|
||||||
|
plugins: {
|
||||||
|
installs: {
|
||||||
|
"openclaw-codex-app-server": {
|
||||||
|
source: "npm",
|
||||||
|
spec: "openclaw-codex-app-server@beta",
|
||||||
|
installPath: "/tmp/openclaw-codex-app-server",
|
||||||
|
resolvedName: "openclaw-codex-app-server",
|
||||||
|
resolvedSpec: "openclaw-codex-app-server@0.2.0-beta.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginIds: ["openclaw-codex-app-server"],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(installPluginFromNpmSpecMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
spec: "openclaw-codex-app-server@beta",
|
||||||
|
expectedPluginId: "openclaw-codex-app-server",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(result.config.plugins?.installs?.["openclaw-codex-app-server"]).toMatchObject({
|
||||||
|
source: "npm",
|
||||||
|
spec: "openclaw-codex-app-server@beta",
|
||||||
|
installPath: "/tmp/openclaw-codex-app-server",
|
||||||
|
version: "0.2.0-beta.4",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses and persists an explicit npm spec override during updates", async () => {
|
||||||
|
installPluginFromNpmSpecMock.mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
pluginId: "openclaw-codex-app-server",
|
||||||
|
targetDir: "/tmp/openclaw-codex-app-server",
|
||||||
|
version: "0.2.0-beta.4",
|
||||||
|
extensions: ["index.ts"],
|
||||||
|
npmResolution: {
|
||||||
|
name: "openclaw-codex-app-server",
|
||||||
|
version: "0.2.0-beta.4",
|
||||||
|
resolvedSpec: "openclaw-codex-app-server@0.2.0-beta.4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await updateNpmInstalledPlugins({
|
||||||
|
config: {
|
||||||
|
plugins: {
|
||||||
|
installs: {
|
||||||
|
"openclaw-codex-app-server": {
|
||||||
|
source: "npm",
|
||||||
|
spec: "openclaw-codex-app-server",
|
||||||
|
installPath: "/tmp/openclaw-codex-app-server",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginIds: ["openclaw-codex-app-server"],
|
||||||
|
specOverrides: {
|
||||||
|
"openclaw-codex-app-server": "openclaw-codex-app-server@beta",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(installPluginFromNpmSpecMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
spec: "openclaw-codex-app-server@beta",
|
||||||
|
expectedPluginId: "openclaw-codex-app-server",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(result.config.plugins?.installs?.["openclaw-codex-app-server"]).toMatchObject({
|
||||||
|
source: "npm",
|
||||||
|
spec: "openclaw-codex-app-server@beta",
|
||||||
|
installPath: "/tmp/openclaw-codex-app-server",
|
||||||
|
version: "0.2.0-beta.4",
|
||||||
|
resolvedSpec: "openclaw-codex-app-server@0.2.0-beta.4",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips recorded integrity checks when an explicit npm version override changes the spec", async () => {
|
||||||
|
installPluginFromNpmSpecMock.mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
pluginId: "openclaw-codex-app-server",
|
||||||
|
targetDir: "/tmp/openclaw-codex-app-server",
|
||||||
|
version: "0.2.0-beta.4",
|
||||||
|
extensions: ["index.ts"],
|
||||||
|
});
|
||||||
|
|
||||||
|
await updateNpmInstalledPlugins({
|
||||||
|
config: {
|
||||||
|
plugins: {
|
||||||
|
installs: {
|
||||||
|
"openclaw-codex-app-server": {
|
||||||
|
source: "npm",
|
||||||
|
spec: "openclaw-codex-app-server@0.2.0-beta.3",
|
||||||
|
integrity: "sha512-old",
|
||||||
|
installPath: "/tmp/openclaw-codex-app-server",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginIds: ["openclaw-codex-app-server"],
|
||||||
|
specOverrides: {
|
||||||
|
"openclaw-codex-app-server": "openclaw-codex-app-server@0.2.0-beta.4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(installPluginFromNpmSpecMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
spec: "openclaw-codex-app-server@0.2.0-beta.4",
|
||||||
|
expectedIntegrity: undefined,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("migrates legacy unscoped install keys when a scoped npm package updates", async () => {
|
it("migrates legacy unscoped install keys when a scoped npm package updates", async () => {
|
||||||
installPluginFromNpmSpecMock.mockResolvedValue({
|
installPluginFromNpmSpecMock.mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|||||||
@ -291,6 +291,7 @@ export async function updateNpmInstalledPlugins(params: {
|
|||||||
pluginIds?: string[];
|
pluginIds?: string[];
|
||||||
skipIds?: Set<string>;
|
skipIds?: Set<string>;
|
||||||
dryRun?: boolean;
|
dryRun?: boolean;
|
||||||
|
specOverrides?: Record<string, string>;
|
||||||
onIntegrityDrift?: (params: PluginUpdateIntegrityDriftParams) => boolean | Promise<boolean>;
|
onIntegrityDrift?: (params: PluginUpdateIntegrityDriftParams) => boolean | Promise<boolean>;
|
||||||
}): Promise<PluginUpdateSummary> {
|
}): Promise<PluginUpdateSummary> {
|
||||||
const logger = params.logger ?? {};
|
const logger = params.logger ?? {};
|
||||||
@ -329,7 +330,14 @@ export async function updateNpmInstalledPlugins(params: {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.source === "npm" && !record.spec) {
|
const effectiveSpec =
|
||||||
|
record.source === "npm" ? (params.specOverrides?.[pluginId] ?? record.spec) : undefined;
|
||||||
|
const expectedIntegrity =
|
||||||
|
record.source === "npm" && effectiveSpec === record.spec
|
||||||
|
? expectedIntegrityForUpdate(record.spec, record.integrity)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (record.source === "npm" && !effectiveSpec) {
|
||||||
outcomes.push({
|
outcomes.push({
|
||||||
pluginId,
|
pluginId,
|
||||||
status: "skipped",
|
status: "skipped",
|
||||||
@ -371,11 +379,11 @@ export async function updateNpmInstalledPlugins(params: {
|
|||||||
probe =
|
probe =
|
||||||
record.source === "npm"
|
record.source === "npm"
|
||||||
? await installPluginFromNpmSpec({
|
? await installPluginFromNpmSpec({
|
||||||
spec: record.spec!,
|
spec: effectiveSpec!,
|
||||||
mode: "update",
|
mode: "update",
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
expectedPluginId: pluginId,
|
expectedPluginId: pluginId,
|
||||||
expectedIntegrity: expectedIntegrityForUpdate(record.spec, record.integrity),
|
expectedIntegrity,
|
||||||
onIntegrityDrift: createPluginUpdateIntegrityDriftHandler({
|
onIntegrityDrift: createPluginUpdateIntegrityDriftHandler({
|
||||||
pluginId,
|
pluginId,
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
@ -408,7 +416,7 @@ export async function updateNpmInstalledPlugins(params: {
|
|||||||
record.source === "npm"
|
record.source === "npm"
|
||||||
? formatNpmInstallFailure({
|
? formatNpmInstallFailure({
|
||||||
pluginId,
|
pluginId,
|
||||||
spec: record.spec!,
|
spec: effectiveSpec!,
|
||||||
phase: "check",
|
phase: "check",
|
||||||
result: probe,
|
result: probe,
|
||||||
})
|
})
|
||||||
@ -452,10 +460,10 @@ export async function updateNpmInstalledPlugins(params: {
|
|||||||
result =
|
result =
|
||||||
record.source === "npm"
|
record.source === "npm"
|
||||||
? await installPluginFromNpmSpec({
|
? await installPluginFromNpmSpec({
|
||||||
spec: record.spec!,
|
spec: effectiveSpec!,
|
||||||
mode: "update",
|
mode: "update",
|
||||||
expectedPluginId: pluginId,
|
expectedPluginId: pluginId,
|
||||||
expectedIntegrity: expectedIntegrityForUpdate(record.spec, record.integrity),
|
expectedIntegrity,
|
||||||
onIntegrityDrift: createPluginUpdateIntegrityDriftHandler({
|
onIntegrityDrift: createPluginUpdateIntegrityDriftHandler({
|
||||||
pluginId,
|
pluginId,
|
||||||
dryRun: false,
|
dryRun: false,
|
||||||
@ -487,7 +495,7 @@ export async function updateNpmInstalledPlugins(params: {
|
|||||||
record.source === "npm"
|
record.source === "npm"
|
||||||
? formatNpmInstallFailure({
|
? formatNpmInstallFailure({
|
||||||
pluginId,
|
pluginId,
|
||||||
spec: record.spec!,
|
spec: effectiveSpec!,
|
||||||
phase: "update",
|
phase: "update",
|
||||||
result: result,
|
result: result,
|
||||||
})
|
})
|
||||||
@ -512,7 +520,7 @@ export async function updateNpmInstalledPlugins(params: {
|
|||||||
next = recordPluginInstall(next, {
|
next = recordPluginInstall(next, {
|
||||||
pluginId: resolvedPluginId,
|
pluginId: resolvedPluginId,
|
||||||
source: "npm",
|
source: "npm",
|
||||||
spec: record.spec,
|
spec: effectiveSpec,
|
||||||
installPath: result.targetDir,
|
installPath: result.targetDir,
|
||||||
version: nextVersion,
|
version: nextVersion,
|
||||||
...buildNpmResolutionInstallFields(result.npmResolution),
|
...buildNpmResolutionInstallFields(result.npmResolution),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user