From 56f79b972572a9e8586d51b7b7fc85754b693ade Mon Sep 17 00:00:00 2001 From: RahulGautamSingh Date: Wed, 27 Nov 2024 14:25:51 +0530 Subject: [PATCH] fix(npm): update npmrc before executing corepack cmd (#32733) --- lib/modules/manager/npm/artifacts.spec.ts | 16 ++++- lib/modules/manager/npm/artifacts.ts | 12 +++- lib/modules/manager/npm/post-update/index.ts | 63 +++----------------- lib/modules/manager/npm/utils.ts | 60 +++++++++++++++++++ 4 files changed, 92 insertions(+), 59 deletions(-) diff --git a/lib/modules/manager/npm/artifacts.spec.ts b/lib/modules/manager/npm/artifacts.spec.ts index 7cb52f4dbf3c11..c241b15189df82 100644 --- a/lib/modules/manager/npm/artifacts.spec.ts +++ b/lib/modules/manager/npm/artifacts.spec.ts @@ -9,6 +9,7 @@ import { GlobalConfig } from '../../../config/global'; import type { RepoGlobalConfig } from '../../../config/types'; import * as docker from '../../../util/exec/docker'; import type { UpdateArtifactsConfig, Upgrade } from '../types'; +import * as rules from './post-update/rules'; import { updateArtifacts } from '.'; jest.mock('../../../util/exec/env'); @@ -38,6 +39,8 @@ const validDepUpdate = { } satisfies Upgrade>; describe('modules/manager/npm/artifacts', () => { + const spyProcessHostRules = jest.spyOn(rules, 'processHostRules'); + beforeEach(() => { env.getChildProcessEnv.mockReturnValue({ ...envMock.basic, @@ -46,6 +49,10 @@ describe('modules/manager/npm/artifacts', () => { }); GlobalConfig.set(adminConfig); docker.resetPrefetchedImages(); + spyProcessHostRules.mockReturnValue({ + additionalNpmrcContent: [], + additionalYarnRcYml: undefined, + }); }); it('returns null if no packageManager updates present', async () => { @@ -98,6 +105,7 @@ describe('modules/manager/npm/artifacts', () => { it('returns updated package.json', async () => { fs.readLocalFile + .mockResolvedValueOnce('# dummy') // for npmrc .mockResolvedValueOnce('{}') // for node constraints .mockResolvedValue('some new content'); // for updated package.json const execSnapshots = mockExecAll(); @@ -124,7 +132,9 @@ describe('modules/manager/npm/artifacts', () => { it('supports docker mode', async () => { GlobalConfig.set(dockerAdminConfig); const execSnapshots = mockExecAll(); - fs.readLocalFile.mockResolvedValueOnce('some new content'); + fs.readLocalFile + .mockResolvedValueOnce('# dummy') // for npmrc + .mockResolvedValueOnce('some new content'); const res = await updateArtifacts({ packageFileName: 'package.json', @@ -171,7 +181,9 @@ describe('modules/manager/npm/artifacts', () => { it('supports install mode', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); const execSnapshots = mockExecAll(); - fs.readLocalFile.mockResolvedValueOnce('some new content'); + fs.readLocalFile + .mockResolvedValueOnce('# dummy') // for npmrc + .mockResolvedValueOnce('some new content'); const res = await updateArtifacts({ packageFileName: 'package.json', diff --git a/lib/modules/manager/npm/artifacts.ts b/lib/modules/manager/npm/artifacts.ts index 082dc4348821eb..408da129fcc3bb 100644 --- a/lib/modules/manager/npm/artifacts.ts +++ b/lib/modules/manager/npm/artifacts.ts @@ -6,7 +6,13 @@ import { readLocalFile, writeLocalFile } from '../../../util/fs'; import { regEx } from '../../../util/regex'; import type { UpdateArtifact, UpdateArtifactsResult } from '../types'; import { getNodeToolConstraint } from './post-update/node-version'; +import { processHostRules } from './post-update/rules'; import { lazyLoadPackageJson } from './post-update/utils'; +import { + getNpmrcContent, + resetNpmrcContent, + updateNpmrcContent, +} from './utils'; // eg. 8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589 const versionWithHashRegString = '^(?.*)\\+(?.*)'; @@ -43,6 +49,8 @@ export async function updateArtifacts({ // Asumming that corepack only needs to modify the package.json file in the root folder // As it should not be regular practice to have different package managers in different workspaces const pkgFileDir = upath.dirname(packageFileName); + const { additionalNpmrcContent } = processHostRules(); + const npmrcContent = await getNpmrcContent(pkgFileDir); const lazyPkgJson = lazyLoadPackageJson(pkgFileDir); const cmd = `corepack use ${depName}@${newVersion}`; @@ -66,9 +74,10 @@ export async function updateArtifacts({ userConfiguredEnv: config.env, }; + await updateNpmrcContent(pkgFileDir, npmrcContent, additionalNpmrcContent); try { await exec(cmd, execOptions); - + await resetNpmrcContent(pkgFileDir, npmrcContent); const newPackageFileContent = await readLocalFile(packageFileName, 'utf8'); if ( !newPackageFileContent || @@ -88,6 +97,7 @@ export async function updateArtifacts({ ]; } catch (err) { logger.warn({ err }, 'Error updating package.json'); + await resetNpmrcContent(pkgFileDir, npmrcContent); return [ { artifactError: { diff --git a/lib/modules/manager/npm/post-update/index.ts b/lib/modules/manager/npm/post-update/index.ts index c83a3971443eee..d7f559dc34bb57 100644 --- a/lib/modules/manager/npm/post-update/index.ts +++ b/lib/modules/manager/npm/post-update/index.ts @@ -6,7 +6,6 @@ import { logger } from '../../../../logger'; import { ExternalHostError } from '../../../../types/errors/external-host-error'; import { getChildProcessEnv } from '../../../../util/exec/env'; import { - deleteLocalFile, ensureCacheDir, getSiblingFileName, readLocalFile, @@ -23,7 +22,13 @@ import { scm } from '../../../platform/scm'; import type { PackageFile, PostUpdateConfig, Upgrade } from '../../types'; import { getZeroInstallPaths } from '../extract/yarn'; import type { NpmManagerData } from '../types'; -import { composeLockFile, parseLockFile } from '../utils'; +import { + composeLockFile, + getNpmrcContent, + parseLockFile, + resetNpmrcContent, + updateNpmrcContent, +} from '../utils'; import * as npm from './npm'; import * as pnpm from './pnpm'; import { processHostRules } from './rules'; @@ -245,60 +250,6 @@ export async function writeUpdatedPackageFiles( } } -async function getNpmrcContent(dir: string): Promise { - const npmrcFilePath = upath.join(dir, '.npmrc'); - let originalNpmrcContent: string | null = null; - try { - originalNpmrcContent = await readLocalFile(npmrcFilePath, 'utf8'); - } catch /* istanbul ignore next */ { - originalNpmrcContent = null; - } - if (originalNpmrcContent) { - logger.debug(`npmrc file ${npmrcFilePath} found in repository`); - } - return originalNpmrcContent; -} - -async function updateNpmrcContent( - dir: string, - originalContent: string | null, - additionalLines: string[], -): Promise { - const npmrcFilePath = upath.join(dir, '.npmrc'); - const newNpmrc = originalContent - ? [originalContent, ...additionalLines] - : additionalLines; - try { - const newContent = newNpmrc.join('\n'); - if (newContent !== originalContent) { - logger.debug(`Writing updated .npmrc file to ${npmrcFilePath}`); - await writeLocalFile(npmrcFilePath, `${newContent}\n`); - } - } catch /* istanbul ignore next */ { - logger.warn('Unable to write custom npmrc file'); - } -} - -async function resetNpmrcContent( - dir: string, - originalContent: string | null, -): Promise { - const npmrcFilePath = upath.join(dir, '.npmrc'); - if (originalContent) { - try { - await writeLocalFile(npmrcFilePath, originalContent); - } catch /* istanbul ignore next */ { - logger.warn('Unable to reset npmrc to original contents'); - } - } else { - try { - await deleteLocalFile(npmrcFilePath); - } catch /* istanbul ignore next */ { - logger.warn('Unable to delete custom npmrc'); - } - } -} - // istanbul ignore next async function updateYarnOffline( lockFileDir: string, diff --git a/lib/modules/manager/npm/utils.ts b/lib/modules/manager/npm/utils.ts index 55d757d475bc88..ed58e4b202c10f 100644 --- a/lib/modules/manager/npm/utils.ts +++ b/lib/modules/manager/npm/utils.ts @@ -1,5 +1,11 @@ import detectIndent from 'detect-indent'; +import upath from 'upath'; import { logger } from '../../../logger'; +import { + deleteLocalFile, + readLocalFile, + writeLocalFile, +} from '../../../util/fs'; import type { LockFile, ParseLockFileResult } from './types'; export function parseLockFile(lockFile: string): ParseLockFileResult { @@ -18,3 +24,57 @@ export function parseLockFile(lockFile: string): ParseLockFileResult { export function composeLockFile(lockFile: LockFile, indent: string): string { return JSON.stringify(lockFile, null, indent) + '\n'; } + +export async function getNpmrcContent(dir: string): Promise { + const npmrcFilePath = upath.join(dir, '.npmrc'); + let originalNpmrcContent: string | null = null; + try { + originalNpmrcContent = await readLocalFile(npmrcFilePath, 'utf8'); + } catch /* istanbul ignore next */ { + originalNpmrcContent = null; + } + if (originalNpmrcContent) { + logger.debug(`npmrc file ${npmrcFilePath} found in repository`); + } + return originalNpmrcContent; +} + +export async function updateNpmrcContent( + dir: string, + originalContent: string | null, + additionalLines: string[], +): Promise { + const npmrcFilePath = upath.join(dir, '.npmrc'); + const newNpmrc = originalContent + ? [originalContent, ...additionalLines] + : additionalLines; + try { + const newContent = newNpmrc.join('\n'); + if (newContent !== originalContent) { + logger.debug(`Writing updated .npmrc file to ${npmrcFilePath}`); + await writeLocalFile(npmrcFilePath, `${newContent}\n`); + } + } catch /* istanbul ignore next */ { + logger.warn('Unable to write custom npmrc file'); + } +} + +export async function resetNpmrcContent( + dir: string, + originalContent: string | null, +): Promise { + const npmrcFilePath = upath.join(dir, '.npmrc'); + if (originalContent) { + try { + await writeLocalFile(npmrcFilePath, originalContent); + } catch /* istanbul ignore next */ { + logger.warn('Unable to reset npmrc to original contents'); + } + } else { + try { + await deleteLocalFile(npmrcFilePath); + } catch /* istanbul ignore next */ { + logger.warn('Unable to delete custom npmrc'); + } + } +}