diff --git a/src/plugins/install.test.ts b/src/plugins/install.test.ts index 3f7eaedb15c..85abb964c81 100644 --- a/src/plugins/install.test.ts +++ b/src/plugins/install.test.ts @@ -2880,21 +2880,28 @@ describe("installPluginFromNpmSpec", () => { expect(requests[0]?.request.kind).toBe("plugin-npm"); }); - it("reports install mode to policy when update-mode reactivates retained-only generations", async () => { + it("reports install mode to policy when update-mode reactivates retained generations", async () => { const root = suiteTempRootTracker.makeTempDir(); const npmDir = path.join(root, "npm"); const extensionsDir = path.join(root, "extensions"); const packageName = "@acme/policy-generation-plugin"; const legacyProjectRoot = resolvePluginNpmProjectDir({ npmDir, packageName }); const generationProjectRoot = resolvePluginNpmGenerationProjectDir({ + npmDir, + packageName, + generationKey: [packageName, "1.2.3", `${packageName}@1.2.3`, "sha512-test", "abc123"].join( + "\n", + ), + }); + const activeGenerationProjectRoot = resolvePluginNpmGenerationProjectDir({ npmDir, packageName, generationKey: [ packageName, - "1.2.3", - `${packageName}@1.2.3`, - "sha512-plugin-test", - "pluginshasum", + "2.0.0", + `${packageName}@2.0.0`, + "sha512-active", + "active123", ].join("\n"), }); const legacyPackageDir = path.join( @@ -2907,6 +2914,11 @@ describe("installPluginFromNpmSpec", () => { "node_modules", ...packageName.split("/"), ); + const activeGenerationPackageDir = path.join( + activeGenerationProjectRoot, + "node_modules", + ...packageName.split("/"), + ); for (const packageDir of [legacyPackageDir, generationPackageDir]) { fs.mkdirSync(packageDir, { recursive: true }); await markRetainedManagedNpmInstall({ @@ -2916,6 +2928,7 @@ describe("installPluginFromNpmSpec", () => { reason: "test-retained-generation", }); } + fs.mkdirSync(activeGenerationPackageDir, { recursive: true }); const { scriptPath, logPath } = writeInstallOnlyBlockingPolicyScript(root); mockNpmViewMetadata({ name: packageName, diff --git a/src/plugins/install.ts b/src/plugins/install.ts index 24d026859c0..5cb315a5687 100644 --- a/src/plugins/install.ts +++ b/src/plugins/install.ts @@ -1149,7 +1149,7 @@ function resolveManagedNpmInstallRoot(params: { }); const npmRoot = resolveManagedNpmRootForInstall(params); const installRoot = resolveManagedNpmRootPackageDir(npmRoot, params.packageName); - if (!params.useGeneration || !hasRetainedManagedNpmInstallMarker(installRoot)) { + if (!hasRetainedManagedNpmInstallMarker(installRoot)) { return npmRoot; } // Never mutate a retained tree: an older process may still hold lazy imports @@ -1206,6 +1206,7 @@ async function resolveManagedNpmGenerationUseForInstall(params: { npmBaseDir: string; packageName: string; requestedMode: "install" | "update"; + npmResolution?: NpmSpecResolution; }): Promise<"none" | "update" | "retained-install"> { const packageDirs = await listManagedNpmPackageDirsForPackage({ runtime: params.runtime, @@ -1218,6 +1219,20 @@ async function resolveManagedNpmGenerationUseForInstall(params: { if (packageDirs.length > 0 && !hasNonRetainedPackageDir) { return "retained-install"; } + const generationUse = + params.requestedMode === "update" && hasNonRetainedPackageDir ? "update" : "none"; + if (params.npmResolution) { + const candidateRoot = resolveManagedNpmRootForInstall({ + npmBaseDir: params.npmBaseDir, + packageName: params.packageName, + npmResolution: params.npmResolution, + useGeneration: generationUse !== "none", + }); + const candidatePackageDir = resolveManagedNpmRootPackageDir(candidateRoot, params.packageName); + if (hasRetainedManagedNpmInstallMarker(candidatePackageDir)) { + return "retained-install"; + } + } if (params.requestedMode === "update") { return hasNonRetainedPackageDir ? "update" : "none"; } @@ -1332,6 +1347,7 @@ async function installPluginFromManagedNpmRoot( npmBaseDir, packageName: params.packageName, requestedMode: mode, + npmResolution: params.npmResolution, }); const npmRoot = resolveManagedNpmInstallRoot({ npmBaseDir, @@ -3093,6 +3109,7 @@ export async function installPluginFromNpmSpec( npmBaseDir, packageName: parsedSpec.name, requestedMode: mode, + npmResolution, }); const npmRoot = resolveManagedNpmRootForInstall({ npmBaseDir, @@ -3243,6 +3260,7 @@ export async function installPluginFromNpmPackArchive( npmBaseDir, packageName, requestedMode: mode, + npmResolution, }); const npmProjectRoot = resolveManagedNpmRootForInstall({ npmBaseDir,