mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-11 14:36:25 +00:00
feat: add versioning for Hermit package manager (#16256)
* feat: add versioning for Hermit package manager * Update lib/modules/versioning/hermit/index.ts Co-authored-by: Jamie Magee <jamie.magee@gmail.com> * Update lib/modules/versioning/hermit/index.ts Co-authored-by: Jamie Magee <jamie.magee@gmail.com> * Update index.ts index.spec.ts and readme.md according to PR comments * fix: fix versioning test double negation and _parseVersion function which is just for testing * fix: simplify hermit versioning implementation as suggested * fix: use _compare to simplify versioning implementation * fix: reword version in hermit versioning and make _isChannel & _getChannel static * fix: remove duplicated title in test and make _config readonly Co-authored-by: Jamie Magee <jamie.magee@gmail.com> Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
This commit is contained in:
parent
5bfa68b8d3
commit
605f35c45c
5 changed files with 348 additions and 1 deletions
|
@ -8,6 +8,7 @@ import * as git from './git';
|
|||
import * as gradle from './gradle';
|
||||
import * as hashicorp from './hashicorp';
|
||||
import * as helm from './helm';
|
||||
import * as hermit from './hermit';
|
||||
import * as hex from './hex';
|
||||
import * as ivy from './ivy';
|
||||
import * as loose from './loose';
|
||||
|
@ -40,6 +41,7 @@ api.set('git', git.api);
|
|||
api.set('gradle', gradle.api);
|
||||
api.set('hashicorp', hashicorp.api);
|
||||
api.set('helm', helm.api);
|
||||
api.set('hermit', hermit.api);
|
||||
api.set('hex', hex.api);
|
||||
api.set('ivy', ivy.api);
|
||||
api.set('loose', loose.api);
|
||||
|
|
196
lib/modules/versioning/hermit/index.spec.ts
Normal file
196
lib/modules/versioning/hermit/index.spec.ts
Normal file
|
@ -0,0 +1,196 @@
|
|||
import { HermitVersioning } from './index';
|
||||
|
||||
describe('modules/versioning/hermit/index', () => {
|
||||
const versioning = new HermitVersioning();
|
||||
|
||||
test.each`
|
||||
version | expected
|
||||
${'1'} | ${true}
|
||||
${'1.2'} | ${true}
|
||||
${'@1'} | ${false}
|
||||
${'@1.2'} | ${false}
|
||||
${'@1.2.3'} | ${false}
|
||||
${'@latest'} | ${false}
|
||||
${'@stable'} | ${false}
|
||||
`('isStable("$version") === $expected', ({ version, expected }) => {
|
||||
expect(versioning.isStable(version)).toBe(expected);
|
||||
});
|
||||
|
||||
test.each`
|
||||
version | expected
|
||||
${'1'} | ${true}
|
||||
${'1rc1'} | ${true}
|
||||
${'1-foo'} | ${true}
|
||||
${'1+bar'} | ${true}
|
||||
${'1.2'} | ${true}
|
||||
${'1.2-foo'} | ${true}
|
||||
${'1.2+bar'} | ${true}
|
||||
${'1.2.3'} | ${true}
|
||||
${'1.2.3rc1'} | ${true}
|
||||
${'1.2.3-foo'} | ${true}
|
||||
${'1.2.3+bar'} | ${true}
|
||||
${'17.0.1_12'} | ${true}
|
||||
${'17.0.1_12+m1'} | ${true}
|
||||
${'17.0.1_12+m1'} | ${true}
|
||||
${'11.0.11_9-zulu11.48.21'} | ${true}
|
||||
${'1.2-kotlin.3'} | ${true}
|
||||
${'@1'} | ${true}
|
||||
${'@1.2'} | ${true}
|
||||
${'@1.2.3'} | ${true}
|
||||
${'@latest'} | ${true}
|
||||
${'@stable'} | ${true}
|
||||
`('isValid("$version") === $expected', ({ version, expected }) => {
|
||||
expect(versioning.isValid(version)).toBe(expected);
|
||||
});
|
||||
|
||||
test.each`
|
||||
version | major | minor | patch
|
||||
${'17'} | ${17} | ${0} | ${0}
|
||||
${'17.2'} | ${17} | ${2} | ${0}
|
||||
${'17.2.3a1'} | ${17} | ${2} | ${3}
|
||||
${'17.2.3-foo'} | ${17} | ${2} | ${3}
|
||||
${'17.2.3+m1'} | ${17} | ${2} | ${3}
|
||||
${'@17'} | ${17} | ${null} | ${null}
|
||||
${'@17.2'} | ${17} | ${2} | ${null}
|
||||
${'@stable'} | ${null} | ${null} | ${null}
|
||||
`(
|
||||
'getMajor, getMinor, getPatch for "$version"',
|
||||
({ version, major, minor, patch }) => {
|
||||
expect(versioning.getMajor(version)).toBe(major);
|
||||
expect(versioning.getMinor(version)).toBe(minor);
|
||||
expect(versioning.getPatch(version)).toBe(patch);
|
||||
}
|
||||
);
|
||||
|
||||
test.each`
|
||||
version | other | expected
|
||||
${'1'} | ${'1.2'} | ${false}
|
||||
${'@1'} | ${'@1.2'} | ${false}
|
||||
${'@1.2'} | ${'@1.2'} | ${true}
|
||||
${'@1.2'} | ${'@1.3'} | ${false}
|
||||
${'@1.2.3'} | ${'@1.2'} | ${false}
|
||||
${'@1.2.3_4'} | ${'@1.2.3'} | ${false}
|
||||
${'@latest'} | ${'@1'} | ${false}
|
||||
${'@stable'} | ${'@stable'} | ${true}
|
||||
${'stable'} | ${'stable'} | ${true}
|
||||
`(
|
||||
'equals("$version", "$other") === $expected',
|
||||
({ version, other, expected }) => {
|
||||
expect(versioning.equals(version, other)).toBe(expected);
|
||||
}
|
||||
);
|
||||
|
||||
test.each`
|
||||
version | other | expected
|
||||
${'@1'} | ${'@1.2'} | ${false}
|
||||
${'@1.2'} | ${'@1.2'} | ${true}
|
||||
${'@1.2.3'} | ${'@1.2'} | ${false}
|
||||
${'@latest'} | ${'@1'} | ${false}
|
||||
${'@stable'} | ${'@stable'} | ${true}
|
||||
`(
|
||||
'matches("$version", "$other") === $expected',
|
||||
({ version, other, expected }) => {
|
||||
expect(versioning.matches(version, other)).toBe(expected);
|
||||
}
|
||||
);
|
||||
|
||||
test.each`
|
||||
version | other | expected
|
||||
${'@1'} | ${'@1.2'} | ${true}
|
||||
${'@1.2'} | ${'@1.2'} | ${false}
|
||||
${'@1.2'} | ${'@1.3'} | ${false}
|
||||
${'@1.2.3'} | ${'@1.2'} | ${false}
|
||||
${'1.2.3'} | ${'@latest'} | ${true}
|
||||
${'@latest'} | ${'@1'} | ${false}
|
||||
${'@stable'} | ${'@latest'} | ${true}
|
||||
${'@latest'} | ${'@stable'} | ${false}
|
||||
`(
|
||||
'isGreaterThan("$version", "$other") === $expected',
|
||||
({ version, other, expected }) => {
|
||||
expect(versioning.isGreaterThan(version, other)).toBe(expected);
|
||||
}
|
||||
);
|
||||
|
||||
test.each`
|
||||
version | other | expected
|
||||
${'@1'} | ${'@1.2'} | ${false}
|
||||
${'@1.2'} | ${'@1.2'} | ${false}
|
||||
${'@1.2.3'} | ${'@1.2'} | ${true}
|
||||
${'@latest'} | ${'@1'} | ${true}
|
||||
${'@stable'} | ${'@latest'} | ${false}
|
||||
${'@latest'} | ${'@stable'} | ${true}
|
||||
`(
|
||||
'isLessThanRange("$version", "$other") === $expected',
|
||||
({ version, other, expected }) => {
|
||||
expect(versioning.isLessThanRange(version, other)).toBe(expected);
|
||||
}
|
||||
);
|
||||
|
||||
it('getSatisfyingVersion', () => {
|
||||
expect(versioning.getSatisfyingVersion(['@1.1.1', '1.2.3'], '1.2.3')).toBe(
|
||||
'1.2.3'
|
||||
);
|
||||
expect(
|
||||
versioning.getSatisfyingVersion(
|
||||
['1.1.1', '@2.2.1', '2.2.2', '3.3.3'],
|
||||
'2.2.2'
|
||||
)
|
||||
).toBe('2.2.2');
|
||||
expect(
|
||||
versioning.getSatisfyingVersion(
|
||||
['1.1.1', '@1.3.3', '2.2.2', '3.3.3'],
|
||||
'1.2.3'
|
||||
)
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('minSatisfyingVersion', () => {
|
||||
expect(versioning.minSatisfyingVersion(['@1.1.1', '1.2.3'], '1.2.3')).toBe(
|
||||
'1.2.3'
|
||||
);
|
||||
expect(
|
||||
versioning.minSatisfyingVersion(
|
||||
['1.1.1', '@1.2.3', '2.2.2', '3.3.3'],
|
||||
'2.2.2'
|
||||
)
|
||||
).toBe('2.2.2');
|
||||
expect(
|
||||
versioning.minSatisfyingVersion(
|
||||
['1.1.1', '@1.2.2', '2.2.2', '3.3.3'],
|
||||
'1.2.3'
|
||||
)
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
describe('sortVersions', () => {
|
||||
it('sorts versions in an ascending order', () => {
|
||||
expect(
|
||||
[
|
||||
'@1',
|
||||
'1.1',
|
||||
'1.2',
|
||||
'1.2.3',
|
||||
'1.3',
|
||||
'@1.2',
|
||||
'@2',
|
||||
'2',
|
||||
'2.1',
|
||||
'@stable',
|
||||
'@latest',
|
||||
].sort((a, b) => versioning.sortVersions(a, b))
|
||||
).toEqual([
|
||||
'@latest',
|
||||
'@stable',
|
||||
'1.1',
|
||||
'1.2',
|
||||
'1.2.3',
|
||||
'@1.2',
|
||||
'1.3',
|
||||
'@1',
|
||||
'2',
|
||||
'2.1',
|
||||
'@2',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
136
lib/modules/versioning/hermit/index.ts
Normal file
136
lib/modules/versioning/hermit/index.ts
Normal file
|
@ -0,0 +1,136 @@
|
|||
import { RegExpVersion, RegExpVersioningApi } from '../regex';
|
||||
import type { VersioningApiConstructor } from '../types';
|
||||
|
||||
export const id = 'hermit';
|
||||
export const displayName = 'Hermit';
|
||||
export const urls = [
|
||||
'https://cashapp.github.io/hermit/packaging/reference/#versions',
|
||||
];
|
||||
export const supportsRanges = false;
|
||||
|
||||
export class HermitVersioning extends RegExpVersioningApi {
|
||||
static versionRegex =
|
||||
'^(?<major>\\d+)(\\.(?<minor>\\d+))?(\\.(?<patch>\\d+))?(_(?<build>\\d+))?([-]?(?<prerelease>[^.+][^+]*))?([+](?<compatibility>[^.-][^+]*))?$';
|
||||
|
||||
public constructor() {
|
||||
super(HermitVersioning.versionRegex);
|
||||
}
|
||||
|
||||
private _isValid(version: string): boolean {
|
||||
return super._parse(version) !== null;
|
||||
}
|
||||
|
||||
protected override _parse(version: string): RegExpVersion | null {
|
||||
const parsed = super._parse(version);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
const channelVer = HermitVersioning._getChannel(version);
|
||||
|
||||
const groups = this._config?.exec(channelVer)?.groups;
|
||||
|
||||
if (!groups) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { major, minor, patch, build, prerelease, compatibility } = groups;
|
||||
const release = [];
|
||||
|
||||
if (major) {
|
||||
release.push(Number.parseInt(major, 10));
|
||||
}
|
||||
|
||||
if (minor) {
|
||||
release.push(Number.parseInt(minor, 10));
|
||||
}
|
||||
if (patch) {
|
||||
release.push(Number.parseInt(patch, 10));
|
||||
}
|
||||
if (build) {
|
||||
release.push(Number.parseInt(build, 10));
|
||||
}
|
||||
|
||||
return {
|
||||
release,
|
||||
prerelease: prerelease,
|
||||
compatibility: compatibility,
|
||||
};
|
||||
}
|
||||
|
||||
private static _isChannel(version: string): boolean {
|
||||
return version.startsWith('@');
|
||||
}
|
||||
|
||||
private static _getChannel(version: string): string {
|
||||
return version.substring(1);
|
||||
}
|
||||
|
||||
override isStable(version: string): boolean {
|
||||
if (this._isValid(version)) {
|
||||
return super.isStable(version);
|
||||
}
|
||||
|
||||
// channel and the rest should be considered unstable version
|
||||
// as channels are changing values
|
||||
return false;
|
||||
}
|
||||
|
||||
override isValid(version: string): boolean {
|
||||
return this._isValid(version) || HermitVersioning._isChannel(version);
|
||||
}
|
||||
|
||||
override isLessThanRange(version: string, range: string): boolean {
|
||||
return this._compare(version, range) < 0;
|
||||
}
|
||||
|
||||
protected override _compare(version: string, other: string): number {
|
||||
if (this._isValid(version) && this._isValid(other)) {
|
||||
return super._compare(version, other);
|
||||
}
|
||||
|
||||
const parsedVersion = this._parse(version);
|
||||
const parsedOther = this._parse(other);
|
||||
|
||||
if (parsedVersion === null || parsedOther === null) {
|
||||
if (parsedVersion === null && parsedOther === null) {
|
||||
return version.localeCompare(other);
|
||||
}
|
||||
return parsedVersion === null ? -1 : 1;
|
||||
}
|
||||
|
||||
const versionReleases = parsedVersion.release;
|
||||
const otherReleases = parsedOther.release;
|
||||
|
||||
const maxLength =
|
||||
versionReleases.length > otherReleases.length
|
||||
? versionReleases.length
|
||||
: otherReleases.length;
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const verVal = versionReleases[i];
|
||||
const otherVal = otherReleases[i];
|
||||
|
||||
if (
|
||||
verVal !== undefined &&
|
||||
otherVal !== undefined &&
|
||||
verVal !== otherVal
|
||||
) {
|
||||
return verVal - otherVal;
|
||||
} else if (verVal === undefined) {
|
||||
return 1;
|
||||
} else if (otherVal === undefined) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
override matches(version: string, range: string): boolean {
|
||||
return this.equals(version, range);
|
||||
}
|
||||
}
|
||||
|
||||
export const api: VersioningApiConstructor = HermitVersioning;
|
||||
|
||||
export default api;
|
13
lib/modules/versioning/hermit/readme.md
Normal file
13
lib/modules/versioning/hermit/readme.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
Hermit versioning is a mix of `version` and `channel`.
|
||||
|
||||
**Version**
|
||||
|
||||
Hermit's package version comes from the packge's original git tag. The version is
|
||||
an extension to semver, with an extra build number to accomondate package
|
||||
versions from OpenJDK, which has a value `15.0.1_9`.
|
||||
|
||||
**Channel**
|
||||
|
||||
[Channel](https://cashapp.github.io/hermit/packaging/reference/#channels) could be hermit generated or user defined.
|
||||
Channel is considered unstable version and normally won't upgrade.
|
||||
If you would like to get out of Channel, you could replace the Channel with a given version number and let it managed by Renovate ongoing.
|
|
@ -39,7 +39,7 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
|
|||
// RegExp('^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(?<prerelease>[^.-]+)?(-(?<compatibility>.*))?$');
|
||||
// * matches the versioning approach used by the Bitnami images on DockerHub:
|
||||
// RegExp('^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(:?-(?<compatibility>.*-r)(?<build>\\d+))?$');
|
||||
private _config: RegExp | null = null;
|
||||
protected readonly _config: RegExp;
|
||||
|
||||
constructor(_new_config: string | undefined) {
|
||||
super();
|
||||
|
|
Loading…
Reference in a new issue