Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(npm): update npmrc before executing corepack cmd #32733

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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');
}
}
}