diff --git a/lib/manager/npm/post-update/lerna.ts b/lib/manager/npm/post-update/lerna.ts index 26bc4762aa6c74..58c25d7673c00f 100644 --- a/lib/manager/npm/post-update/lerna.ts +++ b/lib/manager/npm/post-update/lerna.ts @@ -79,7 +79,8 @@ export async function generateLockFiles( lernaCommand = lernaCommand.replace('--ignore-scripts ', ''); } lernaCommand += cmdOptions; - const tagConstraint = await getNodeConstraint(config); + const allowUnstable = true; // lerna will pick the default installed npm@6 unless we use node@>=15 + const tagConstraint = await getNodeConstraint(config, allowUnstable); const execOptions: ExecOptions = { cwd, extraEnv: { diff --git a/lib/manager/npm/post-update/node-version.spec.ts b/lib/manager/npm/post-update/node-version.spec.ts index 9f045c63f1891e..a73bf616aec38d 100644 --- a/lib/manager/npm/post-update/node-version.spec.ts +++ b/lib/manager/npm/post-update/node-version.spec.ts @@ -28,6 +28,33 @@ describe('getNodeConstraint', () => { const node16IsStable = isStable('16.100.0'); expect(isAugmentedRange || node16IsStable).toBe(true); }); + it('forces node 15 if v2 lockfile detected and constraint allows', async () => { + fs.readLocalFile.mockResolvedValueOnce(null); + fs.readLocalFile.mockResolvedValueOnce(null); + fs.readLocalFile.mockResolvedValueOnce('{"lockfileVersion":2}'); + const res = await getNodeConstraint({ + ...config, + constraints: { node: '>= 12.16.0' }, + }); + const isAugmentedRange = res === '>=15'; + const node16IsStable = isStable('16.100.0'); + expect(isAugmentedRange || node16IsStable).toBe(true); + }); + it('forces node 15 if v2 lockfile detected and no constraint', async () => { + fs.readLocalFile.mockResolvedValueOnce(null); + fs.readLocalFile.mockResolvedValueOnce(null); + fs.readLocalFile.mockResolvedValueOnce('{"lockfileVersion":2}'); + const res = await getNodeConstraint( + { + ...config, + constraints: {}, + }, + true + ); + const isAugmentedRange = res === '>=15'; + const node16IsStable = isStable('16.100.0'); + expect(isAugmentedRange || node16IsStable).toBe(true); + }); it('returns .node-version value', async () => { fs.readLocalFile = jest.fn(); fs.readLocalFile.mockResolvedValueOnce(null); diff --git a/lib/manager/npm/post-update/node-version.ts b/lib/manager/npm/post-update/node-version.ts index 3684aebb8bfa75..57be51522c4673 100644 --- a/lib/manager/npm/post-update/node-version.ts +++ b/lib/manager/npm/post-update/node-version.ts @@ -29,13 +29,22 @@ function getPackageJsonConstraint(config: PostUpdateConfig): string | null { } export async function getNodeConstraint( - config: PostUpdateConfig + config: PostUpdateConfig, + allowUnstable = false ): Promise | null { const { packageFile } = config; let constraint = (await getNodeFile(getSiblingFileName(packageFile, '.nvmrc'))) || (await getNodeFile(getSiblingFileName(packageFile, '.node-version'))) || getPackageJsonConstraint(config); + let lockfileVersion = 1; + try { + const lockFileName = getSiblingFileName(packageFile, 'package-lock.json'); + lockfileVersion = JSON.parse(await readLocalFile(lockFileName, 'utf8')) + .lockfileVersion; + } catch (err) { + // do nothing + } // Avoid using node 15 if node 14 also satisfies the same constraint // Remove this once node 16 is LTS if (constraint) { @@ -43,12 +52,19 @@ export async function getNodeConstraint( validRange(constraint) && satisfies('14.100.0', constraint) && satisfies('15.100.0', constraint) && - !isStable('16.100.0') && // this should return false as soon as Node 16 is LTS - validRange(`${constraint} <15`) + !isStable('16.100.0') ) { - logger.debug('Augmenting constraint to avoid node 15'); - constraint = `${constraint} <15`; + if (lockfileVersion === 2) { + logger.debug('Forcing node 15 to ensure lockfileVersion=2 is used'); + constraint = '>=15'; + } else if (validRange(`${constraint} <15`)) { + logger.debug('Augmenting constraint to avoid node 15'); + constraint = `${constraint} <15`; + } } + } else if (allowUnstable && lockfileVersion === 2) { + logger.debug('Using node >=15 for lockfileVersion=2'); + constraint = '>=15'; } else { logger.debug('No node constraint found - using latest'); }