mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-12 06:56:24 +00:00
feat(manager/npm): add support for x-range "all" - "*"
range (#18251)
This commit is contained in:
parent
7c06287c12
commit
6fef1d1650
8 changed files with 140 additions and 6 deletions
|
@ -59,7 +59,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() extracts engine
|
|||
"depName": "foo",
|
||||
"depType": "devDependencies",
|
||||
"prettyDepType": "devDependency",
|
||||
"skipReason": "any-version",
|
||||
},
|
||||
{
|
||||
"currentValue": "file:../foo/bar",
|
||||
|
|
|
@ -353,7 +353,7 @@ describe('modules/manager/npm/extract/index', () => {
|
|||
deps: [
|
||||
{ depName: 'angular', currentValue: '1.6.0' },
|
||||
{ depName: '@angular/cli', currentValue: '1.6.0' },
|
||||
{ depName: 'foo', currentValue: '*', skipReason: 'any-version' },
|
||||
{ depName: 'foo', currentValue: '*' },
|
||||
{
|
||||
depName: 'bar',
|
||||
currentValue: 'file:../foo/bar',
|
||||
|
|
|
@ -271,9 +271,6 @@ export async function extractPackageFile(
|
|||
}
|
||||
if (isValid(dep.currentValue)) {
|
||||
dep.datasource = NpmDatasource.id;
|
||||
if (dep.currentValue === '*') {
|
||||
dep.skipReason = 'any-version';
|
||||
}
|
||||
if (dep.currentValue === '') {
|
||||
dep.skipReason = 'empty';
|
||||
}
|
||||
|
|
|
@ -5,19 +5,44 @@ describe('modules/versioning/npm/index', () => {
|
|||
version | isValid
|
||||
${'17.04.0'} | ${false}
|
||||
${'1.2.3'} | ${true}
|
||||
${'*'} | ${true}
|
||||
${'x'} | ${true}
|
||||
${'X'} | ${true}
|
||||
${'1'} | ${true}
|
||||
${'1.2.3-foo'} | ${true}
|
||||
${'1.2.3foo'} | ${false}
|
||||
${'~1.2.3'} | ${true}
|
||||
${'1.2'} | ${true}
|
||||
${'1.2.x'} | ${true}
|
||||
${'1.2.X'} | ${true}
|
||||
${'1.2.*'} | ${true}
|
||||
${'~1.2.3'} | ${true}
|
||||
${'^1.2.3'} | ${true}
|
||||
${'>1.2.3'} | ${true}
|
||||
${'renovatebot/renovate'} | ${false}
|
||||
${'renovatebot/renovate#main'} | ${false}
|
||||
${'https://github.com/renovatebot/renovate.git'} | ${false}
|
||||
`('isValid("$version") === $isValid', ({ version, isValid }) => {
|
||||
const res = !!semver.isValid(version);
|
||||
const res = semver.isValid(version);
|
||||
expect(res).toBe(isValid);
|
||||
});
|
||||
|
||||
test.each`
|
||||
versions | range | maxSatisfying
|
||||
${['2.3.3.', '2.3.4', '2.4.5', '2.5.1', '3.0.0']} | ${'*'} | ${'3.0.0'}
|
||||
${['2.3.3.', '2.3.4', '2.4.5', '2.5.1', '3.0.0']} | ${'x'} | ${'3.0.0'}
|
||||
${['2.3.3.', '2.3.4', '2.4.5', '2.5.1', '3.0.0']} | ${'X'} | ${'3.0.0'}
|
||||
${['2.3.3.', '2.3.4', '2.4.5', '2.5.1', '3.0.0']} | ${'2'} | ${'2.5.1'}
|
||||
${['2.3.3.', '2.3.4', '2.4.5', '2.5.1', '3.0.0']} | ${'2.*'} | ${'2.5.1'}
|
||||
${['2.3.3.', '2.3.4', '2.4.5', '2.5.1', '3.0.0']} | ${'2.3'} | ${'2.3.4'}
|
||||
${['2.3.3.', '2.3.4', '2.4.5', '2.5.1', '3.0.0']} | ${'2.3.*'} | ${'2.3.4'}
|
||||
`(
|
||||
'getSatisfyingVersion("$versions","$range") === $maxSatisfying',
|
||||
({ versions, range, maxSatisfying }) => {
|
||||
expect(semver.getSatisfyingVersion(versions, range)).toBe(maxSatisfying);
|
||||
}
|
||||
);
|
||||
|
||||
test.each`
|
||||
version | isSingle
|
||||
${'1.2.3'} | ${true}
|
||||
|
@ -59,6 +84,7 @@ describe('modules/versioning/npm/index', () => {
|
|||
${'>= 0.0.1 < 0.0.4'} | ${'bump'} | ${'0.0.4'} | ${'0.0.5'} | ${'>= 0.0.5 < 0.0.6'}
|
||||
${'>= 0.0.1 < 1'} | ${'bump'} | ${'1.0.0'} | ${'1.0.1'} | ${'>= 1.0.1 < 2'}
|
||||
${'>= 0.0.1 < 1'} | ${'bump'} | ${'1.0.0'} | ${'1.0.1'} | ${'>= 1.0.1 < 2'}
|
||||
${'*'} | ${'bump'} | ${'1.0.0'} | ${'1.0.1'} | ${null}
|
||||
${'<=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.3'} | ${'<=1.2.3'}
|
||||
${'<=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.4'} | ${'<=1.2.4'}
|
||||
${'>=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.3'} | ${'>=1.2.3'}
|
||||
|
|
|
@ -3,6 +3,7 @@ import semver from 'semver';
|
|||
import semverUtils from 'semver-utils';
|
||||
import { logger } from '../../../logger';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import { isSemVerXRange } from '../semver/common';
|
||||
import type { NewValueConfig } from '../types';
|
||||
|
||||
const {
|
||||
|
@ -63,6 +64,9 @@ export function getNewValue({
|
|||
currentVersion,
|
||||
newVersion,
|
||||
}: NewValueConfig): string | null {
|
||||
if (rangeStrategy === 'bump' && isSemVerXRange(currentValue)) {
|
||||
return null;
|
||||
}
|
||||
if (rangeStrategy === 'pin' || isVersion(currentValue)) {
|
||||
return newVersion;
|
||||
}
|
||||
|
|
16
lib/modules/versioning/semver/common.spec.ts
Normal file
16
lib/modules/versioning/semver/common.spec.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { isSemVerXRange } from './common';
|
||||
|
||||
describe('modules/versioning/semver/common', () => {
|
||||
test.each`
|
||||
range | expected
|
||||
${'*'} | ${true}
|
||||
${'x'} | ${true}
|
||||
${'X'} | ${true}
|
||||
${''} | ${true}
|
||||
${'1'} | ${false}
|
||||
${'1.2'} | ${false}
|
||||
${'1.2.3'} | ${false}
|
||||
`('isSemVerXRange("range") === $expected', ({ range, expected }) => {
|
||||
expect(isSemVerXRange(range)).toBe(expected);
|
||||
});
|
||||
});
|
10
lib/modules/versioning/semver/common.ts
Normal file
10
lib/modules/versioning/semver/common.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
const SEMVER_X_RANGE = ['*', 'x', 'X', ''] as const;
|
||||
type SemVerXRangeArray = typeof SEMVER_X_RANGE;
|
||||
export type SemVerXRange = SemVerXRangeArray[number];
|
||||
|
||||
/**
|
||||
* https://docs.npmjs.com/cli/v6/using-npm/semver#x-ranges-12x-1x-12-
|
||||
*/
|
||||
export function isSemVerXRange(range: string): range is SemVerXRange {
|
||||
return SEMVER_X_RANGE.includes(range as SemVerXRange);
|
||||
}
|
|
@ -334,6 +334,88 @@ describe('workers/repository/process/lookup/index', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it.each`
|
||||
strategy | updates
|
||||
${'update-lockfile'} | ${[{ isLockfileUpdate: true, newValue: '*', newVersion: '0.9.7', updateType: 'minor' }, { isLockfileUpdate: true, newValue: '*', newVersion: '1.4.1', updateType: 'major' }]}
|
||||
${'pin'} | ${[{ newValue: '0.4.0', updateType: 'pin' }, { newValue: '0.9.7', updateType: 'minor' }, { newValue: '1.4.1', updateType: 'major' }]}
|
||||
`(
|
||||
'supports for x-range-all for replaceStrategy = $strategy (with lockfile)',
|
||||
async ({ strategy, updates }) => {
|
||||
config.currentValue = '*';
|
||||
config.rangeStrategy = strategy;
|
||||
config.lockedVersion = '0.4.0';
|
||||
config.depName = 'q';
|
||||
config.datasource = NpmDatasource.id;
|
||||
httpMock
|
||||
.scope('https://registry.npmjs.org')
|
||||
.get('/q')
|
||||
.reply(200, qJson);
|
||||
expect(await lookup.lookupUpdates(config)).toMatchObject({ updates });
|
||||
}
|
||||
);
|
||||
|
||||
it.each`
|
||||
strategy
|
||||
${'widen'}
|
||||
${'bump'}
|
||||
${'replace'}
|
||||
`(
|
||||
'doesnt offer updates for x-range-all (with lockfile) when replaceStrategy = $strategy',
|
||||
async ({ strategy }) => {
|
||||
config.currentValue = 'x';
|
||||
config.rangeStrategy = strategy;
|
||||
config.lockedVersion = '0.4.0';
|
||||
config.depName = 'q';
|
||||
config.datasource = NpmDatasource.id;
|
||||
httpMock
|
||||
.scope('https://registry.npmjs.org')
|
||||
.get('/q')
|
||||
.reply(200, qJson);
|
||||
expect((await lookup.lookupUpdates(config)).updates).toEqual([]);
|
||||
}
|
||||
);
|
||||
|
||||
it('supports pinning for x-range-all (no lockfile)', async () => {
|
||||
config.currentValue = '*';
|
||||
config.rangeStrategy = 'pin';
|
||||
config.depName = 'q';
|
||||
config.datasource = NpmDatasource.id;
|
||||
httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
|
||||
expect(await lookup.lookupUpdates(config)).toMatchObject({
|
||||
updates: [{ newValue: '1.4.1', updateType: 'pin' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('covers pinning an unsupported x-range-all value', async () => {
|
||||
config.currentValue = '';
|
||||
config.rangeStrategy = 'pin';
|
||||
config.depName = 'q';
|
||||
config.datasource = NpmDatasource.id;
|
||||
httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
|
||||
expect((await lookup.lookupUpdates(config)).updates).toEqual([]);
|
||||
});
|
||||
|
||||
it.each`
|
||||
strategy
|
||||
${'widen'}
|
||||
${'bump'}
|
||||
${'update-lockfile'}
|
||||
${'replace'}
|
||||
`(
|
||||
'doesnt offer updates for x-range-all (no lockfile) when replaceStrategy = $strategy',
|
||||
async ({ strategy }) => {
|
||||
config.currentValue = 'X';
|
||||
config.rangeStrategy = strategy;
|
||||
config.depName = 'q';
|
||||
config.datasource = NpmDatasource.id;
|
||||
httpMock
|
||||
.scope('https://registry.npmjs.org')
|
||||
.get('/q')
|
||||
.reply(200, qJson);
|
||||
expect((await lookup.lookupUpdates(config)).updates).toEqual([]);
|
||||
}
|
||||
);
|
||||
|
||||
it('ignores pinning for ranges when other upgrade exists', async () => {
|
||||
config.currentValue = '~0.9.0';
|
||||
config.rangeStrategy = 'pin';
|
||||
|
|
Loading…
Reference in a new issue