Skip to content

Commit

Permalink
fix(npm): update npmrc before executing corepack cmd (#32733)
Browse files Browse the repository at this point in the history
  • Loading branch information
RahulGautamSingh authored Nov 27, 2024
1 parent c4f4934 commit 56f79b9
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 59 deletions.
16 changes: 14 additions & 2 deletions lib/modules/manager/npm/artifacts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -38,6 +39,8 @@ const validDepUpdate = {
} satisfies Upgrade<Record<string, unknown>>;

describe('modules/manager/npm/artifacts', () => {
const spyProcessHostRules = jest.spyOn(rules, 'processHostRules');

beforeEach(() => {
env.getChildProcessEnv.mockReturnValue({
...envMock.basic,
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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();
Expand All @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
12 changes: 11 additions & 1 deletion lib/modules/manager/npm/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '^(?<version>.*)\\+(?<hash>.*)';
Expand Down Expand Up @@ -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}`;

Expand All @@ -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 ||
Expand All @@ -88,6 +97,7 @@ export async function updateArtifacts({
];
} catch (err) {
logger.warn({ err }, 'Error updating package.json');
await resetNpmrcContent(pkgFileDir, npmrcContent);
return [
{
artifactError: {
Expand Down
63 changes: 7 additions & 56 deletions lib/modules/manager/npm/post-update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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';
Expand Down Expand Up @@ -245,60 +250,6 @@ export async function writeUpdatedPackageFiles(
}
}

async function getNpmrcContent(dir: string): Promise<string | null> {
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<void> {
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<void> {
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,
Expand Down
60 changes: 60 additions & 0 deletions lib/modules/manager/npm/utils.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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<string | null> {
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<void> {
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<void> {
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');
}
}
}

0 comments on commit 56f79b9

Please sign in to comment.