-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(versioning): same major (#28418)
Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com> Co-authored-by: Michael Kriese <michael.kriese@visualon.de> Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> Co-authored-by: Rhys Arkins <rhys@arkins.net>
- Loading branch information
1 parent
ab39248
commit e6a29bb
Showing
5 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import sameMajor from '.'; | ||
|
||
describe('modules/versioning/same-major/index', () => { | ||
describe('.isGreaterThan(version, other)', () => { | ||
it('should return true', () => { | ||
expect(sameMajor.isGreaterThan('4.0.0', '3.0.0')).toBeTrue(); // greater | ||
}); | ||
|
||
it('should return false', () => { | ||
expect(sameMajor.isGreaterThan('2.0.2', '3.1.0')).toBeFalse(); // less | ||
expect(sameMajor.isGreaterThan('3.1.0', '3.0.0')).toBeFalse(); // same major -> equal | ||
expect(sameMajor.isGreaterThan('3.0.0', '3.0.0')).toBeFalse(); // equal | ||
expect(sameMajor.isGreaterThan('a', '3.0.0')).toBeFalse(); // invalid versions | ||
}); | ||
}); | ||
|
||
describe('.matches(version, range)', () => { | ||
it('should return true when version has same major', () => { | ||
expect(sameMajor.matches('1.0.1', '1.0.0')).toBeTrue(); | ||
expect(sameMajor.matches('1.0.0', '1.0.0')).toBeTrue(); | ||
}); | ||
|
||
it('should return false when version has different major', () => { | ||
expect(sameMajor.matches('2.0.1', '1.0.0')).toBeFalse(); | ||
}); | ||
|
||
it('should return false when version is out of range', () => { | ||
expect(sameMajor.matches('1.2.3', '1.2.4')).toBeFalse(); | ||
expect(sameMajor.matches('2.0.0', '1.2.4')).toBeFalse(); | ||
expect(sameMajor.matches('3.2.4', '1.2.4')).toBeFalse(); | ||
}); | ||
|
||
it('should return false when version is invalid', () => { | ||
expect(sameMajor.matches('1.0.0', 'xxx')).toBeFalse(); | ||
}); | ||
}); | ||
|
||
describe('.getSatisfyingVersion(versions, range)', () => { | ||
it('should return max satisfying version in range', () => { | ||
expect( | ||
sameMajor.getSatisfyingVersion( | ||
['1.0.0', '1.0.4', '1.3.0', '2.0.0'], | ||
'1.0.3', | ||
), | ||
).toBe('1.3.0'); | ||
}); | ||
}); | ||
|
||
describe('.minSatisfyingVersion(versions, range)', () => { | ||
it('should return min satisfying version in range', () => { | ||
expect( | ||
sameMajor.minSatisfyingVersion( | ||
['1.0.0', '1.0.4', '1.3.0', '2.0.0'], | ||
'1.0.3', | ||
), | ||
).toBe('1.0.4'); | ||
}); | ||
}); | ||
|
||
describe('.isLessThanRange(version, range)', () => { | ||
it('should return true', () => { | ||
expect(sameMajor.isLessThanRange?.('2.0.2', '3.0.0')).toBeTrue(); | ||
}); | ||
|
||
it('should return false', () => { | ||
expect(sameMajor.isLessThanRange?.('4.0.0', '3.0.0')).toBeFalse(); | ||
expect(sameMajor.isLessThanRange?.('3.1.0', '3.0.0')).toBeFalse(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { logger } from '../../../logger'; | ||
import { api as semverCoerced } from '../semver-coerced'; | ||
import type { VersioningApi } from '../types'; | ||
|
||
export const id = 'same-major'; | ||
export const displayName = 'Same Major Versioning'; | ||
export const urls = []; | ||
export const supportsRanges = false; | ||
|
||
/** | ||
* | ||
* Converts input to range if it's a version. eg. X.Y.Z -> '>=X.Y.Z <X+1' | ||
* If the input is already a range, it returns the input. | ||
*/ | ||
function massageVersion(input: string): string { | ||
// istanbul ignore if: same-major versioning should not be used with ranges as it defeats the purpose | ||
if (!semverCoerced.isSingleVersion(input)) { | ||
logger.warn( | ||
{ version: input }, | ||
'Same major versioning expects a single version but got a range. Please switch to a different versioning as this may lead to unexpected behaviour.', | ||
); | ||
return input; | ||
} | ||
|
||
// we are sure to get a major because of the isSingleVersion check | ||
const major = semverCoerced.getMajor(input)!; | ||
return `>=${input} <${major + 1}`; | ||
} | ||
|
||
// for same major versioning one version is greater than the other if its major is greater | ||
function isGreaterThan(version: string, other: string): boolean { | ||
const versionMajor = semverCoerced.getMajor(version)!; | ||
const otherMajor = semverCoerced.getMajor(other)!; | ||
|
||
if (!versionMajor || !otherMajor) { | ||
return false; | ||
} | ||
|
||
return versionMajor > otherMajor; | ||
} | ||
|
||
function matches(version: string, range: string): boolean { | ||
return semverCoerced.matches(version, massageVersion(range)); | ||
} | ||
|
||
function getSatisfyingVersion( | ||
versions: string[], | ||
range: string, | ||
): string | null { | ||
return semverCoerced.getSatisfyingVersion(versions, massageVersion(range)); | ||
} | ||
|
||
function minSatisfyingVersion( | ||
versions: string[], | ||
range: string, | ||
): string | null { | ||
return semverCoerced.minSatisfyingVersion(versions, massageVersion(range)); | ||
} | ||
|
||
function isLessThanRange(version: string, range: string): boolean { | ||
return semverCoerced.isLessThanRange!(version, massageVersion(range)); | ||
} | ||
|
||
export const api: VersioningApi = { | ||
...semverCoerced, | ||
matches, | ||
getSatisfyingVersion, | ||
minSatisfyingVersion, | ||
isLessThanRange, | ||
isGreaterThan, | ||
}; | ||
export default api; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
The 'Same Major' versioning is designed to handle the case where a version needs to treated as a "greate than or equal to" constraint. | ||
Specifically, the case where the version say, `X.Y.Z` signifies a range of compatibility from greater than or equal to `X.Y.Z` to less than `X+1`. | ||
|
||
This process uses Semver-Coerced versioning beneath the surface, single versions (e.g., `X.Y.Z`) are converted to a range like `X+1` and then passed to the corresponding semver-coerced method. | ||
|
||
This method is handy when managing dependencies like dotnet-sdk's rollForward settings. | ||
Let's say a project uses dotnet-sdk version `3.1.0`. | ||
It needs to be compatible with any version in the `3.x.x` range but _not_ with versions in the next major version, like `4.x.x`. | ||
|
||
For example: | ||
|
||
```json | ||
{ | ||
"sdk": { | ||
"version": "6.0.300", | ||
"rollForward": "major" | ||
} | ||
} | ||
``` | ||
|
||
The roll-forward policy to use when selecting an SDK version, either as a fallback when a specific SDK version is missing or as a directive to use a higher version. In this case with `major` it means that select the latest version with the same major. | ||
ie. `>= 6.0.300 < 7.0.0` | ||
|
||
For such cases, the users would not want Renovate to create an update PR for any version within the range `>= 6.0.300 < 7.0.0` as it would not change the behaviour on their end, since it is handled by the manager already. | ||
|
||
Note: | ||
You should create a discussion before using this versioning as this is an experimental support and might have some edge cases unhandled. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters