UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

321 lines (320 loc) • 10.9 kB
import { regEx } from "../../../util/regex.js"; import { logger } from "../../../logger/index.js"; import { coerceArray } from "../../../util/array.js"; import { gte, lt, lte, satisfies } from "@renovatebot/pep440"; import { parse } from "@renovatebot/pep440/lib/specifier.js"; import { parse as parse$1 } from "@renovatebot/pep440/lib/version.js"; //#region lib/modules/versioning/pep440/range.ts const UserPolicyPrecisionMap = { Major: 0, Minor: 1, Micro: 2, Bug: 3, None: Infinity }; const PrecisionUserPolicyMap = { 0: "Major", 1: "Minor", 2: "Micro", 3: "Bug" }; /** * Calculate current update range precision. * @param ranges A {@link Range} consists of current range * @returns A {@link UserPolicy} */ function getRangePrecision(ranges) { const bound = coerceArray(parse$1((ranges[1] || ranges[0]).version)?.release); let rangePrecision = -1; if (ranges.length === 1) rangePrecision = bound.length - 1; if (ranges.length === 2) { const lowerBound = coerceArray(parse$1(ranges[0].version)?.release); rangePrecision = bound.findIndex((el, index) => el > lowerBound[index]); } if (rangePrecision === UserPolicyPrecisionMap.Major && rangePrecision + 1 < bound.length && bound[rangePrecision + 1] === 0) rangePrecision++; // istanbul ignore next if (rangePrecision === -1) rangePrecision = bound.length - 1; return UserPolicyPrecisionMap[PrecisionUserPolicyMap[rangePrecision]]; } /** * @param policy Required range precision * @param newVersion The newly accepted version * @param baseVersion Optional Current upper bound * @returns A string represents a future version upper bound. */ function getFutureVersion(policy, newVersion, baseVersion) { const toRelease = coerceArray(parse$1(newVersion)?.release); return coerceArray(parse$1(baseVersion ?? newVersion)?.release).map((_, index) => { const toPart = toRelease[index] ?? 0; if (index < policy) return toPart; if (index === policy) return toPart + (baseVersion === void 0 ? 0 : 1); return 0; }); } function getPinnedValue(newVersion) { return `==${newVersion}`; } function getNewValue({ currentValue, rangeStrategy, currentVersion, newVersion, isReplacement }) { let ranges; let updatedRange; if (currentValue === currentVersion || isReplacement) return newVersion; if (parse$1(currentValue)) { const vPrefix = regEx(/^(?<prefix>[vV])/).exec(currentValue); if (vPrefix) return `${vPrefix.groups.prefix}${newVersion}`; return newVersion; } try { ranges = parseCurrentRange(currentValue); if (!ranges.length) { logger.warn({ currentValue }, "Empty currentValue"); return currentValue; } } catch (err) { logger.warn({ currentValue, err }, "Unexpected range error"); return null; } if (ranges.some((range) => range.operator === "!=" && range.version === newVersion)) { logger.debug(`Cannot calculate new value as the newVersion:\`${newVersion}\` is excluded from range: \`${currentValue}\``); return null; } switch (rangeStrategy) { case "auto": case "replace": updatedRange = handleReplaceStrategy({ currentValue, rangeStrategy, currentVersion, newVersion }, ranges); break; case "widen": updatedRange = handleWidenStrategy({ currentValue, rangeStrategy, currentVersion, newVersion }, ranges); break; case "bump": updatedRange = handleBumpStrategy({ currentValue, rangeStrategy, currentVersion, newVersion }, ranges); break; default: logger.debug(`Unsupported rangeStrategy: ${rangeStrategy}. Using "replace" instead.`); return getNewValue({ currentValue, rangeStrategy: "replace", currentVersion, newVersion }); } let result = updatedRange.filter(Boolean).join(", "); if (result.includes(", ") && !currentValue.includes(", ")) result = result.replace(regEx(/, /g), ","); const checkedResult = checkRangeAndRemoveUnnecessaryRangeLimit(result, newVersion); // istanbul ignore if if (!satisfies(newVersion, checkedResult)) { logger.warn({ result, newVersion, currentValue }, "pep440: failed to calculate newValue"); return null; } return checkedResult; } function isLessThanRange(input, range) { try { let invertResult = true; const result = range.split(",").map((x) => x.replace(regEx(/\s*/g), "").split(regEx(/(~=|==|!=|<=|>=|<|>|===)/)).slice(1)).map(([op, version]) => { if ([ "!=", "<=", "<" ].includes(op)) return true; invertResult = false; if ([ "~=", "==", ">=", "===" ].includes(op)) return lt(input, version); if (op === ">") return lte(input, version); // istanbul ignore next return false; }).every((res) => res === true); return invertResult ? !result : result; } catch /* istanbul ignore next */ { return false; } } function parseCurrentRange(currentValue) { const ranges = parse(currentValue); if (!ranges) throw new TypeError("Invalid pep440 currentValue"); if (ranges.some((range) => range.operator === "===")) throw new TypeError("PEP440 arbitrary equality (===) not supported"); return ranges; } function handleLowerBound(range, newVersion) { if ([">", ">="].includes(range.operator)) { if (lte(newVersion, range.version)) return `>=${newVersion}`; return range.operator + range.version; } // istanbul ignore next return null; } function handleUpperBound(range, newVersion) { if (range.operator === "<") { if (gte(newVersion, range.version)) { const futureVersion = getFutureVersion(getRangePrecision([range]), newVersion, range.version); return range.operator + futureVersion.join("."); } return range.operator + range.version; } // istanbul ignore next return null; } function updateRangeValue({ currentValue, newVersion }, range) { if (range.operator === "!=") return range.operator + range.version; if (range.prefix) { const futureVersion = getFutureVersion(UserPolicyPrecisionMap.None, newVersion, range.version).join("."); return `${range.operator}${futureVersion}.*`; } if (range.operator === "~=") { const baseVersion = coerceArray(parse$1(range.version)?.release); const futureVersion = coerceArray(parse$1(newVersion)?.release); const baseLen = baseVersion.length; const newVerLen = futureVersion.length; if (baseLen < newVerLen) return range.operator + futureVersion.slice(0, baseVersion.length).join("."); if (baseLen > newVerLen) { for (let i = baseLen - newVerLen - 1; i >= 0; i--) futureVersion.push(0); return range.operator + futureVersion.join("."); } return range.operator + newVersion; } if (["==", "<="].includes(range.operator)) { if (lte(newVersion, range.version)) return range.operator + range.version; return range.operator + newVersion; } let output = handleUpperBound(range, newVersion); if (output) return output; output = handleLowerBound(range, newVersion); if (output) return output; // istanbul ignore next logger.error({ newVersion, currentValue, range }, "pep440: failed to process range"); // istanbul ignore next return null; } /** * Checks for zero specifiers. * returns true if one of the bounds' length is < 3. * @param ranges A {@link Range} array. * @returns A boolean value * Used mainly for cosmetics for the rez versioning syntax. */ function hasZeroSpecifier(ranges) { return ranges.some((range) => { const release = parse$1(range.version)?.release; return release && release.length < 3; }); } function trimTrailingZeros(numbers) { let i = numbers.length - 1; while (numbers[i] === 0) i--; return numbers.slice(0, i + 1); } function divideCompatibleReleaseRange(currentRange) { const currentVersionUpperBound = currentRange.version.split(".").map((num) => parseInt(num, 10)); if (currentVersionUpperBound.length > 1) currentVersionUpperBound.splice(-1); currentVersionUpperBound[currentVersionUpperBound.length - 1] += 1; return [{ operator: ">=", version: currentRange.version }, { operator: "<", version: currentVersionUpperBound.join(".") }]; } function handleWidenStrategy({ currentValue, rangeStrategy, currentVersion, newVersion }, ranges) { if (satisfies(newVersion, currentValue)) return [currentValue]; let rangePrecision = getRangePrecision(ranges); const trimZeros = hasZeroSpecifier(ranges); let newRanges = []; if (ranges.length === 1 && ranges[0].operator === "~=") newRanges = divideCompatibleReleaseRange(ranges[0]); else newRanges = ranges; return newRanges.map((range) => { if (range.operator === "<" && gte(newVersion, range.version)) { const upperBound = coerceArray(parse$1(range.version)?.release); const len = upperBound.length; if (upperBound[len - 1] !== 0) rangePrecision = UserPolicyPrecisionMap[PrecisionUserPolicyMap[len - 1]]; let futureVersion = getFutureVersion(rangePrecision, newVersion, range.version); if (trimZeros) futureVersion = trimTrailingZeros(futureVersion); return range.operator + futureVersion.join("."); } return updateRangeValue({ currentValue, rangeStrategy, currentVersion, newVersion }, range); }); } function handleReplaceStrategy({ currentValue, rangeStrategy, currentVersion, newVersion }, ranges) { if (satisfies(newVersion, currentValue)) return [currentValue]; const trimZeros = hasZeroSpecifier(ranges); return ranges.map((range) => { if (range.operator === "<" && gte(newVersion, range.version)) { let futureVersion = getFutureVersion(getRangePrecision(ranges), newVersion, range.version); if (trimZeros) futureVersion = trimTrailingZeros(futureVersion); return range.operator + futureVersion.join("."); } if ([">", ">="].includes(range.operator)) { if (lte(newVersion, range.version)) return `>=${newVersion}`; let newBase = getFutureVersion(coerceArray(parse$1(range.version)?.release).length - 1, newVersion); if (trimZeros) newBase = trimTrailingZeros(newBase); if (range.operator === ">") { if (newVersion === newBase.join(".") && newBase.length > 1) newBase.pop(); } return range.operator + newBase.join("."); } return updateRangeValue({ currentValue, rangeStrategy, currentVersion, newVersion }, range); }); } function handleBumpStrategy({ currentValue, rangeStrategy, currentVersion, newVersion }, ranges) { return ranges.map((range) => { if (range.operator === ">=") return range.operator + newVersion; return updateRangeValue({ currentValue, rangeStrategy, currentVersion, newVersion }, range); }); } function checkRangeAndRemoveUnnecessaryRangeLimit(rangeInput, newVersion) { let newRange = rangeInput; if (rangeInput.includes(",")) { const newRes = rangeInput.split(","); if (newRes[0].includes(".*") && newRes[0].includes("==") && newRes[1].includes(">=")) { if (satisfies(newVersion, newRes[0])) newRange = newRes[0]; } } else return rangeInput; return newRange; } //#endregion export { getNewValue, getPinnedValue, isLessThanRange }; //# sourceMappingURL=range.js.map