Skip to content

Commit

Permalink
feat(versioning:regex): support build number (#11115)
Browse files Browse the repository at this point in the history
  • Loading branch information
viceice committed Aug 5, 2021
1 parent 80b3db4 commit 2f30a9a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 21 deletions.
47 changes: 46 additions & 1 deletion lib/versioning/loose/generic.ts
@@ -1,3 +1,4 @@
import is from '@sindresorhus/is';
import type { NewValueConfig, VersioningApi } from '../types';

export interface GenericVersion {
Expand Down Expand Up @@ -133,7 +134,51 @@ export abstract class GenericVersioningApi<
: null;
}

protected abstract _compare(version: string, other: string): number;
protected _compare(version: string, other: string): number {
const left = this._parse(version);
const right = this._parse(other);

// istanbul ignore if
if (!(left && right)) {
return 1;
}

// support variable length compare
const length = Math.max(left.release.length, right.release.length);
for (let i = 0; i < length; i += 1) {
// 2.1 and 2.1.0 are equivalent
const part1 = left.release[i] ?? 0;
const part2 = right.release[i] ?? 0;
if (part1 !== part2) {
return part1 - part2;
}
}

if (
is.nonEmptyString(left.prerelease) &&
is.nonEmptyString(right.prerelease)
) {
const pre = left.prerelease.localeCompare(right.prerelease);

if (pre !== 0) {
return pre;
}
} else if (is.nonEmptyString(left.prerelease)) {
return -1;
} else if (is.nonEmptyString(right.prerelease)) {
return 1;
}

return this._compareOther(left, right);
}

/*
* virtual
*/
// eslint-disable-next-line class-methods-use-this
protected _compareOther(_left: T, _right: T): number {
return 0;
}

protected abstract _parse(version: string): T | null;

Expand Down
29 changes: 29 additions & 0 deletions lib/versioning/regex/index.spec.ts
Expand Up @@ -396,4 +396,33 @@ describe('regex', () => {
expect(regex.matches('1.2.4a1-foo', '1.2.3a1-bar')).toBe(false);
});
});

describe('Supported 4th number as build', () => {
it('supports Bitnami docker versioning', () => {
const re = get(
'regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(:?-(?<compatibility>.*-r)(?<build>\\d+))?$'
);

expect(re.isValid('12.7.0-debian-10-r69')).toBe(true);
expect(re.isValid('12.7.0-debian-10-r100')).toBe(true);

expect(
re.isCompatible('12.7.0-debian-10-r69', '12.7.0-debian-10-r100')
).toBe(true);

expect(
re.isGreaterThan('12.7.0-debian-10-r69', '12.7.0-debian-10-r100')
).toBe(false);
expect(
re.isGreaterThan('12.7.0-debian-10-r169', '12.7.0-debian-10-r100')
).toBe(true);

expect(re.matches('12.7.0-debian-9-r69', '12.7.0-debian-10-r69')).toBe(
true
);
expect(re.matches('12.7.0-debian-9-r69', '12.7.0-debian-10-r68')).toBe(
true
);
});
});
});
36 changes: 16 additions & 20 deletions lib/versioning/regex/index.ts
@@ -1,4 +1,5 @@
import { compare, ltr, maxSatisfying, minSatisfying, satisfies } from 'semver';
import is from '@sindresorhus/is';
import { ltr, maxSatisfying, minSatisfying, satisfies } from 'semver';
import { CONFIG_VALIDATION } from '../../constants/error-messages';
import { regEx } from '../../util/regex';
import { GenericVersion, GenericVersioningApi } from '../loose/generic';
Expand All @@ -10,8 +11,6 @@ export const urls = [];
export const supportsRanges = false;

export interface RegExpVersion extends GenericVersion {
/** prereleases are treated in the standard semver manner, if present */
prerelease: string;
/**
* compatibility, if present, are treated as a compatibility layer: we will
* never try to update to a version with a different compatibility.
Expand All @@ -22,7 +21,7 @@ export interface RegExpVersion extends GenericVersion {
// convenience method for passing a Version object into any semver.* method.
function asSemver(version: RegExpVersion): string {
let vstring = `${version.release[0]}.${version.release[1]}.${version.release[2]}`;
if (typeof version.prerelease !== 'undefined') {
if (is.nonEmptyString(version.prerelease)) {
vstring += `-${version.prerelease}`;
}
return vstring;
Expand All @@ -38,6 +37,8 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
// RegExp('^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(-(?<compatibility>.*))?$')
// * matches the versioning approach used by the Python images on DockerHub:
// 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;

constructor(new_config: string) {
Expand Down Expand Up @@ -66,13 +67,6 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
this._config = regEx(new_config);
}

protected _compare(version: string, other: string): number {
return compare(
asSemver(this._parse(version)),
asSemver(this._parse(other))
);
}

// convenience method for passing a string into a Version given current config.
protected _parse(version: string): RegExpVersion | null {
const match = this._config.exec(version);
Expand All @@ -81,12 +75,18 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
}

const groups = match.groups;
const release = [
typeof groups.major === 'undefined' ? 0 : Number(groups.major),
typeof groups.minor === 'undefined' ? 0 : Number(groups.minor),
typeof groups.patch === 'undefined' ? 0 : Number(groups.patch),
];

if (groups.build) {
release.push(Number(groups.build));
}

return {
release: [
typeof groups.major === 'undefined' ? 0 : Number(groups.major),
typeof groups.minor === 'undefined' ? 0 : Number(groups.minor),
typeof groups.patch === 'undefined' ? 0 : Number(groups.patch),
],
release,
prerelease: groups.prerelease,
compatibility: groups.compatibility,
};
Expand All @@ -98,10 +98,6 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
);
}

isStable(version: string): boolean {
return typeof this._parse(version).prerelease === 'undefined';
}

isLessThanRange(version: string, range: string): boolean {
return ltr(asSemver(this._parse(version)), asSemver(this._parse(range)));
}
Expand Down
14 changes: 14 additions & 0 deletions lib/versioning/regex/readme.md
Expand Up @@ -34,3 +34,17 @@ Here is another example, this time for handling `python` Docker images, which us
]
}
```

Here is another example, this time for handling Bitnami Docker images, which use build indicators as well as version suffixes for compatibility:

```json
{
"packageRules": [
{
"matchDatasources": ["docker"],
"matchPackagePrefixes": ["bitnami/"],
"versioning": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(:?-(?<compatibility>.*-r)(?<build>\\d+))?$"
}
]
}
```

0 comments on commit 2f30a9a

Please sign in to comment.