Skip to content

Commit

Permalink
feat(manager/npm): support parsing lockfileVersion=3 (#22281)
Browse files Browse the repository at this point in the history
Co-authored-by: Rhys Arkins <rhys@arkins.net>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Sergei Zharinov <zharinov@users.noreply.github.com>
  • Loading branch information
4 people committed Jun 6, 2023
1 parent 9dbc1d0 commit bbd3c60
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 54 deletions.
80 changes: 80 additions & 0 deletions lib/modules/manager/npm/__fixtures__/npm9/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions lib/modules/manager/npm/__fixtures__/npm9/package.json
@@ -0,0 +1,15 @@
{
"name": "npm9",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"chalk": "^2.4.1"
}
}
31 changes: 0 additions & 31 deletions lib/modules/manager/npm/extract/__snapshots__/npm.spec.ts.snap

This file was deleted.

116 changes: 116 additions & 0 deletions lib/modules/manager/npm/extract/locked-versions.spec.ts
@@ -1,3 +1,4 @@
import { logger } from '../../../../../test/util';
import type { PackageFile } from '../../types';
import type { NpmManagerData } from '../types';
import { getLockedVersions } from './locked-versions';
Expand Down Expand Up @@ -550,4 +551,119 @@ describe('modules/manager/npm/extract/locked-versions', () => {
},
]);
});

it('should log warning if unsupported lockfileVersion is found', async () => {
npm.getNpmLock.mockReturnValue({
lockedVersions: {},
lockfileVersion: 99,
});
const packageFiles = [
{
managerData: {
npmLock: 'package-lock.json',
},
extractedConstraints: {},
deps: [
{ depName: 'a', currentValue: '1.0.0' },
{ depName: 'b', currentValue: '2.0.0' },
],
packageFile: 'some-file',
},
];
await getLockedVersions(packageFiles);
expect(packageFiles).toEqual(packageFiles);
expect(logger.logger.warn).toHaveBeenCalledWith(
{
lockfileVersion: 99,
npmLock: 'package-lock.json',
},
'Found unsupported npm lockfile version'
);
});

describe('lockfileVersion 3', () => {
it('uses package-lock.json with npm v9.0.0', async () => {
npm.getNpmLock.mockReturnValue({
lockedVersions: {
a: '1.0.0',
b: '2.0.0',
c: '3.0.0',
},
lockfileVersion: 3,
});
const packageFiles = [
{
managerData: {
npmLock: 'package-lock.json',
},
extractedConstraints: {},
deps: [
{ depName: 'a', currentValue: '1.0.0' },
{ depName: 'b', currentValue: '2.0.0' },
],
packageFile: 'some-file',
},
];
await getLockedVersions(packageFiles);
expect(packageFiles).toEqual([
{
extractedConstraints: {
npm: '>=7',
},
deps: [
{ currentValue: '1.0.0', depName: 'a', lockedVersion: '1.0.0' },
{ currentValue: '2.0.0', depName: 'b', lockedVersion: '2.0.0' },
],
packageFile: 'some-file',
lockFiles: ['package-lock.json'],
managerData: {
npmLock: 'package-lock.json',
},
},
]);
});

it('uses package-lock.json with npm v7.0.0', async () => {
npm.getNpmLock.mockReturnValue({
lockedVersions: {
a: '1.0.0',
b: '2.0.0',
c: '3.0.0',
},
lockfileVersion: 3,
});
const packageFiles = [
{
managerData: {
npmLock: 'package-lock.json',
},
extractedConstraints: {
npm: '^9.0.0',
},
deps: [
{ depName: 'a', currentValue: '1.0.0' },
{ depName: 'b', currentValue: '2.0.0' },
],
packageFile: 'some-file',
},
];
await getLockedVersions(packageFiles);
expect(packageFiles).toEqual([
{
extractedConstraints: {
npm: '^9.0.0',
},
deps: [
{ currentValue: '1.0.0', depName: 'a', lockedVersion: '1.0.0' },
{ currentValue: '2.0.0', depName: 'b', lockedVersion: '2.0.0' },
],
packageFile: 'some-file',
lockFiles: ['package-lock.json'],
managerData: {
npmLock: 'package-lock.json',
},
},
]);
});
});
});
18 changes: 17 additions & 1 deletion lib/modules/manager/npm/extract/locked-versions.ts
Expand Up @@ -61,7 +61,13 @@ export async function getLockedVersions(
lockFiles.push(npmLock);
if (!lockFileCache[npmLock]) {
logger.trace('Retrieving/parsing ' + npmLock);
lockFileCache[npmLock] = await getNpmLock(npmLock);
const cache = await getNpmLock(npmLock);
// istanbul ignore if
if (!cache) {
logger.warn({ npmLock }, 'Npm: unable to get lockfile');
return;
}
lockFileCache[npmLock] = cache;
}

const { lockfileVersion } = lockFileCache[npmLock];
Expand All @@ -88,6 +94,16 @@ export async function getLockedVersions(
} else {
npm = '<9';
}
} else if (lockfileVersion === 3) {
if (!packageFile.extractedConstraints?.npm) {
npm = '>=7';
}
} else {
logger.warn(
{ lockfileVersion, npmLock },
'Found unsupported npm lockfile version'
);
return;
}
if (npm) {
packageFile.extractedConstraints ??= {};
Expand Down
57 changes: 50 additions & 7 deletions lib/modules/manager/npm/extract/npm.spec.ts
Expand Up @@ -6,7 +6,7 @@ jest.mock('../../../../util/fs');

describe('modules/manager/npm/extract/npm', () => {
describe('.getNpmLock()', () => {
it('returns empty if failed to parse', async () => {
it('returns null if failed to parse', async () => {
fs.readLocalFile.mockResolvedValueOnce('abcd');
const res = await getNpmLock('package.json');
expect(Object.keys(res.lockedVersions!)).toHaveLength(0);
Expand All @@ -16,23 +16,66 @@ describe('modules/manager/npm/extract/npm', () => {
const plocktest1Lock = Fixtures.get('plocktest1/package-lock.json', '..');
fs.readLocalFile.mockResolvedValueOnce(plocktest1Lock as never);
const res = await getNpmLock('package.json');
expect(res).toMatchSnapshot();
expect(Object.keys(res.lockedVersions!)).toHaveLength(7);
expect(res).toEqual({
lockedVersions: {
'ansi-styles': '3.2.1',
chalk: '2.4.1',
'color-convert': '1.9.1',
'color-name': '1.1.3',
'escape-string-regexp': '1.0.5',
'has-flag': '3.0.0',
'supports-color': '5.4.0',
},
lockfileVersion: 1,
});
});

it('extracts npm 7 lockfile', async () => {
const npm7Lock = Fixtures.get('npm7/package-lock.json', '..');
fs.readLocalFile.mockResolvedValueOnce(npm7Lock as never);
const res = await getNpmLock('package.json');
expect(res).toMatchSnapshot();
expect(Object.keys(res.lockedVersions!)).toHaveLength(7);
expect(res.lockfileVersion).toBe(2);
expect(res).toEqual({
lockedVersions: {
'ansi-styles': '3.2.1',
chalk: '2.4.1',
'color-convert': '1.9.1',
'color-name': '1.1.3',
'escape-string-regexp': '1.0.5',
'has-flag': '3.0.0',
'supports-color': '5.4.0',
},
lockfileVersion: 2,
});
});

it('returns empty if no deps', async () => {
it('extracts npm 9 lockfile', async () => {
const npm9Lock = Fixtures.get('npm9/package-lock.json', '..');
fs.readLocalFile.mockResolvedValueOnce(npm9Lock);
const res = await getNpmLock('package.json');
expect(res).toEqual({
lockedVersions: {
'ansi-styles': '3.2.1',
chalk: '2.4.2',
'color-convert': '1.9.3',
'color-name': '1.1.3',
'escape-string-regexp': '1.0.5',
'has-flag': '3.0.0',
'supports-color': '5.5.0',
},
lockfileVersion: 3,
});
});

it('returns null if no deps', async () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const res = await getNpmLock('package.json');
expect(Object.keys(res.lockedVersions!)).toHaveLength(0);
});

it('returns null on read error', async () => {
fs.readLocalFile.mockResolvedValueOnce(null);
const res = await getNpmLock('package.json');
expect(Object.keys(res.lockedVersions!)).toHaveLength(0);
});
});
});

0 comments on commit bbd3c60

Please sign in to comment.