mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-26 07:20:39 +00:00
0d5d7a8669
Refactors `getNewValue` logic to replace it with an optional `getPinnedValue()` where rangeStrategy=pin. This way each versioning module doesn't need to add and test extensive "pin" logic. This should not be breaking, but out of an abundance of caution, we'll mark it as such.
192 lines
5.1 KiB
TypeScript
192 lines
5.1 KiB
TypeScript
import {
|
|
eq,
|
|
gt,
|
|
maxSatisfying,
|
|
minSatisfying,
|
|
satisfies,
|
|
valid,
|
|
} from '@renovatebot/ruby-semver';
|
|
import { logger } from '../../../logger';
|
|
import type { RangeStrategy } from '../../../types/versioning';
|
|
import { regEx } from '../../../util/regex';
|
|
import type { NewValueConfig, VersioningApi } from '../types';
|
|
import { isSingleOperator, isValidOperator } from './operator';
|
|
import { ltr, parse as parseRange } from './range';
|
|
import { bump, replace, widen } from './strategies';
|
|
import { parse as parseVersion } from './version';
|
|
|
|
export const id = 'ruby';
|
|
export const displayName = 'Ruby';
|
|
export const urls = [
|
|
'https://guides.rubygems.org/patterns/',
|
|
'https://bundler.io/guides/gemfile.html',
|
|
'https://www.devalot.com/articles/2012/04/gem-versions.html',
|
|
];
|
|
export const supportsRanges = true;
|
|
export const supportedRangeStrategies: RangeStrategy[] = [
|
|
'bump',
|
|
'widen',
|
|
'replace',
|
|
];
|
|
|
|
function vtrim<T = unknown>(version: T): string | T {
|
|
if (typeof version === 'string') {
|
|
return version.replace(regEx(/^v/), '').replace(regEx(/('|")/g), '');
|
|
}
|
|
return version;
|
|
}
|
|
|
|
const equals = (left: string, right: string): boolean =>
|
|
eq(vtrim(left), vtrim(right));
|
|
|
|
const getMajor = (version: string): number =>
|
|
parseVersion(vtrim(version)).major;
|
|
const getMinor = (version: string): number =>
|
|
parseVersion(vtrim(version)).minor;
|
|
const getPatch = (version: string): number =>
|
|
parseVersion(vtrim(version)).patch;
|
|
|
|
export const isVersion = (version: string): boolean => !!valid(vtrim(version));
|
|
const isGreaterThan = (left: string, right: string): boolean =>
|
|
gt(vtrim(left), vtrim(right));
|
|
const isLessThanRange = (version: string, range: string): boolean =>
|
|
!!ltr(vtrim(version), vtrim(range));
|
|
|
|
const isSingleVersion = (range: string): boolean => {
|
|
const { version, operator } = parseRange(vtrim(range));
|
|
|
|
return operator
|
|
? isVersion(version) && isSingleOperator(operator)
|
|
: isVersion(version);
|
|
};
|
|
|
|
function isStable(version: string): boolean {
|
|
const v = vtrim(version);
|
|
return parseVersion(v).prerelease ? false : isVersion(v);
|
|
}
|
|
|
|
export const isValid = (input: string): boolean =>
|
|
input
|
|
.split(',')
|
|
.map((piece) => vtrim(piece.trim()))
|
|
.every((range) => {
|
|
const { version, operator } = parseRange(range);
|
|
|
|
return operator
|
|
? isVersion(version) && isValidOperator(operator)
|
|
: isVersion(version);
|
|
});
|
|
|
|
export const matches = (version: string, range: string): boolean =>
|
|
satisfies(vtrim(version), vtrim(range));
|
|
|
|
function getSatisfyingVersion(
|
|
versions: string[],
|
|
range: string,
|
|
): string | null {
|
|
return maxSatisfying(versions.map(vtrim), vtrim(range));
|
|
}
|
|
|
|
function minSatisfyingVersion(
|
|
versions: string[],
|
|
range: string,
|
|
): string | null {
|
|
return minSatisfying(versions.map(vtrim), vtrim(range));
|
|
}
|
|
|
|
const getPinnedValue = (value: string): string => {
|
|
return vtrim(value);
|
|
};
|
|
|
|
const getNewValue = ({
|
|
currentValue,
|
|
rangeStrategy,
|
|
currentVersion,
|
|
newVersion,
|
|
}: NewValueConfig): string | null => {
|
|
let newValue = null;
|
|
if (isVersion(currentValue)) {
|
|
newValue =
|
|
currentValue.startsWith('v') && !newVersion.startsWith('v')
|
|
? `v${newVersion}`
|
|
: newVersion;
|
|
} else if (currentValue.replace(regEx(/^=\s*/), '') === currentVersion) {
|
|
newValue = currentValue.replace(currentVersion, newVersion);
|
|
} else {
|
|
switch (rangeStrategy) {
|
|
case 'update-lockfile':
|
|
if (satisfies(newVersion, vtrim(currentValue))) {
|
|
newValue = vtrim(currentValue);
|
|
} else {
|
|
return getNewValue({
|
|
currentValue,
|
|
rangeStrategy: 'replace',
|
|
currentVersion,
|
|
newVersion,
|
|
});
|
|
}
|
|
break;
|
|
case 'bump':
|
|
newValue = bump({ range: vtrim(currentValue), to: vtrim(newVersion) });
|
|
break;
|
|
case 'auto':
|
|
case 'replace':
|
|
newValue = replace({
|
|
range: vtrim(currentValue),
|
|
to: vtrim(newVersion),
|
|
});
|
|
break;
|
|
case 'widen':
|
|
newValue = widen({
|
|
range: vtrim(currentValue),
|
|
to: vtrim(newVersion),
|
|
});
|
|
break;
|
|
// istanbul ignore next
|
|
default:
|
|
logger.warn({ rangeStrategy }, 'Unsupported range strategy');
|
|
}
|
|
}
|
|
if (newValue && regEx(/^('|")/).exec(currentValue)) {
|
|
const delimiter = currentValue[0];
|
|
return newValue
|
|
.split(',')
|
|
.map((element) =>
|
|
element.replace(
|
|
regEx(`^(?<whitespace>\\s*)`),
|
|
`$<whitespace>${delimiter}`,
|
|
),
|
|
)
|
|
.map(
|
|
(element) =>
|
|
element.replace(/(?<whitespace>\s*)$/, `${delimiter}$<whitespace>`), // TODO #12875 adds ' at front when re2 is used
|
|
)
|
|
.join(',');
|
|
}
|
|
return newValue;
|
|
};
|
|
|
|
export const sortVersions = (left: string, right: string): number =>
|
|
gt(vtrim(left), vtrim(right)) ? 1 : -1;
|
|
|
|
export const api: VersioningApi = {
|
|
equals,
|
|
getMajor,
|
|
getMinor,
|
|
getPatch,
|
|
isCompatible: isVersion,
|
|
isGreaterThan,
|
|
isLessThanRange,
|
|
isSingleVersion,
|
|
isStable,
|
|
isValid,
|
|
isVersion,
|
|
matches,
|
|
getSatisfyingVersion,
|
|
minSatisfyingVersion,
|
|
getPinnedValue,
|
|
getNewValue,
|
|
sortVersions,
|
|
};
|
|
export default api;
|