diff --git a/lib/modules/versioning/index.spec.ts b/lib/modules/versioning/index.spec.ts index 9fe12ce0d6c38df71470f3a5f37f6c3e079f6f6c..017d198e1b4af1a2b43907d6e6f13ea0f6581e78 100644 --- a/lib/modules/versioning/index.spec.ts +++ b/lib/modules/versioning/index.spec.ts @@ -73,6 +73,7 @@ describe('modules/versioning/index', () => { describe('should return the same interface', () => { const optionalFunctions = [ + 'allowUnstableMajorUpgrades', 'isLessThanRange', 'valueToVersion', 'constructor', diff --git a/lib/modules/versioning/node/index.ts b/lib/modules/versioning/node/index.ts index d243a4425982a2530d0460e11aee315d099339f1..35eb921153862552d0a94395406433264c518cda 100644 --- a/lib/modules/versioning/node/index.ts +++ b/lib/modules/versioning/node/index.ts @@ -85,6 +85,7 @@ export const api: VersioningApi = { matches, getSatisfyingVersion, minSatisfyingVersion, + allowUnstableMajorUpgrades: true, }; export default api; diff --git a/lib/modules/versioning/types.ts b/lib/modules/versioning/types.ts index a1808b08cb1c4516b5877012c12821bc70af8c00..6c9807d7c30e6c669572cd5369d647132067636d 100644 --- a/lib/modules/versioning/types.ts +++ b/lib/modules/versioning/types.ts @@ -106,6 +106,11 @@ export interface VersioningApi { * @param superRange - the dom range */ subset?(subRange: string, superRange: string): boolean | undefined; + + /** + * Return whether unstable-to-unstable upgrades within the same major version are allowed. + */ + allowUnstableMajorUpgrades?: boolean; } export interface VersioningApiConstructor { diff --git a/lib/workers/repository/process/lookup/filter.ts b/lib/workers/repository/process/lookup/filter.ts index 50d1959f0df3dbaea3b90a8962b6132dfa5c5e7d..e94cfbca158e2f7bfc744c4e0111a7970a8bf20c 100644 --- a/lib/workers/repository/process/lookup/filter.ts +++ b/lib/workers/repository/process/lookup/filter.ts @@ -131,12 +131,21 @@ export function filterVersions( // if current is unstable then allow unstable in the current major only // Allow unstable only in current major - return filteredVersions.filter( - (v) => - isVersionStable(v.version) || - (versioning.getMajor(v.version) === versioning.getMajor(currentVersion) && - versioning.getMinor(v.version) === - versioning.getMinor(currentVersion) && - versioning.getPatch(v.version) === versioning.getPatch(currentVersion)) - ); + return filteredVersions.filter((v) => { + if (isVersionStable(v.version)) { + return true; + } + if ( + versioning.getMajor(v.version) !== versioning.getMajor(currentVersion) + ) { + return false; + } + if (versioning.allowUnstableMajorUpgrades) { + return true; + } + return ( + versioning.getMinor(v.version) === versioning.getMinor(currentVersion) && + versioning.getPatch(v.version) === versioning.getPatch(currentVersion) + ); + }); } diff --git a/lib/workers/repository/process/lookup/index.spec.ts b/lib/workers/repository/process/lookup/index.spec.ts index 6a3122b53838c5e5018a3a4ffec838bc88f950b5..473179f0186995835a55178ef8fecfb8fe85d7b9 100644 --- a/lib/workers/repository/process/lookup/index.spec.ts +++ b/lib/workers/repository/process/lookup/index.spec.ts @@ -13,6 +13,7 @@ import { PackagistDatasource } from '../../../../modules/datasource/packagist'; import { PypiDatasource } from '../../../../modules/datasource/pypi'; import { id as dockerVersioningId } from '../../../../modules/versioning/docker'; import { id as gitVersioningId } from '../../../../modules/versioning/git'; +import { id as nodeVersioningId } from '../../../../modules/versioning/node'; import { id as npmVersioningId } from '../../../../modules/versioning/npm'; import { id as pep440VersioningId } from '../../../../modules/versioning/pep440'; import { id as poetryVersioningId } from '../../../../modules/versioning/poetry'; @@ -45,6 +46,11 @@ describe('workers/repository/process/lookup/index', () => { 'getReleases' ); + const getGithubTags = jest.spyOn( + GithubTagsDatasource.prototype, + 'getReleases' + ); + const getDockerReleases = jest.spyOn( DockerDatasource.prototype, 'getReleases' @@ -999,6 +1005,23 @@ describe('workers/repository/process/lookup/index', () => { ]); }); + it('should allow unstable versions in same major for node', async () => { + config.currentValue = '20.3.0'; + config.packageName = 'node'; + config.datasource = GithubTagsDatasource.id; + config.versioning = nodeVersioningId; + getGithubTags.mockResolvedValueOnce({ + releases: [ + { version: '20.3.0' }, + { version: '20.3.1' }, + { version: '21.0.0' }, + ], + }); + expect((await lookup.lookupUpdates(config)).updates).toMatchObject([ + { newValue: '20.3.1', updateType: 'patch' }, + ]); + }); + it('should return pendingChecks', async () => { config.currentValue = '1.4.4'; config.packageName = 'some/action';