renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
160 lines (159 loc) • 6.46 kB
JavaScript
import { regEx } from "../../../util/regex.js";
import { GenericVersioningApi } from "../generic.js";
const versionRegex = regEx(/^v?(?<major>[0-9]+)(?:\.(?<minor>[0-9]+))?(?:\.(?<patch>[0-9]+))?(?<extra>(?:\.[0-9]+)*)(?<letter>[a-z])?(?:(?<prereleaseType>_alpha|_beta|_pre|_rc)(?<prereleaseNum>[0-9]*))?(?:(?<packageFixType>_cvs|_svn|_git|_hg|_p)(?<packageFixNum>[0-9]*))?(?:-r(?<releaseNum>[0-9]+))?$/);
const alphaNumRegex = regEx(/([a-zA-Z]+)|(\d+)/g);
var ApkVersioningApi = class extends GenericVersioningApi {
/**
* Parse APK version format using apko's version parsing patterns
* Based on `Apache-2.0` code in https://github.com/chainguard-dev/apko/blob/v0.30.35/pkg/apk/apk/version.go
*/
_parse(version) {
const match = versionRegex.exec(version);
if (!match?.groups) return null;
const { major, minor, patch, extra, letter, prereleaseType, prereleaseNum, packageFixType, packageFixNum, releaseNum } = match.groups;
const packageFixFull = packageFixType ? packageFixType + packageFixNum : "";
const versionStr = major + ((minor ? `.${minor}` : "") + (patch ? `.${patch}` : "") + extra) + (letter ?? "") + packageFixFull;
let prerelease;
if (prereleaseType) prerelease = prereleaseType.substring(1) + prereleaseNum;
const release = [parseInt(major, 10)];
if (minor) release.push(parseInt(minor, 10));
if (patch) release.push(parseInt(patch, 10));
if (extra) {
const extraParts = extra.substring(1).split(".").filter(Boolean).map(Number);
release.push(...extraParts);
}
return {
version: versionStr,
releaseString: releaseNum ?? "",
release,
prerelease
};
}
/**
* Compare two APK versions according to Alpine Linux rules
*/
_compare(version, other) {
const parsed1 = this._parse(version);
const parsed2 = this._parse(other);
if (!(parsed1 && parsed2)) return 1;
const versionCompare = this._compareVersionParts(parsed1.version, parsed2.version);
if (versionCompare !== 0) return versionCompare;
const prerelease1 = parsed1.prerelease;
const prerelease2 = parsed2.prerelease;
if (prerelease1 || prerelease2) {
if (!prerelease1) return -1;
if (!prerelease2) return 1;
const prereleaseCompare = prerelease1.localeCompare(prerelease2);
if (prereleaseCompare !== 0) return prereleaseCompare;
}
const release1 = parsed1.releaseString || "";
const release2 = parsed2.releaseString || "";
if (release1 && !release2) return 1;
if (!release1 && release2) return -1;
return this._compareVersionParts(release1 || "0", release2 || "0");
}
/**
* Compare version parts using APK's version comparison rules
*/
_compareVersionParts(v1, v2) {
if (v1 === v2) return 0;
const matchesv1 = v1.match(alphaNumRegex);
const matchesv2 = v2.match(alphaNumRegex);
const matches = Math.min(matchesv1.length, matchesv2.length);
for (let i = 0; i < matches; i++) {
const matchv1 = matchesv1[i];
const matchv2 = matchesv2[i];
if (matchv1 && /^\d+$/.test(matchv1)) {
if (!matchv2 || !/^\d+$/.test(matchv2)) return 1;
const num1 = parseInt(matchv1, 10);
const num2 = parseInt(matchv2, 10);
if (num1 !== num2) return num1 - num2;
} else if (matchv2 && /^\d+$/.test(matchv2)) return -1;
else if (matchv1 !== matchv2) return matchv1.localeCompare(matchv2);
}
if (matchesv1.length !== matchesv2.length) {
const maxLength = Math.max(matchesv1.length, matchesv2.length);
for (let i = matches; i < maxLength; i++) {
const matchv1 = matchesv1[i];
const matchv2 = matchesv2[i];
if (matchv1 && /^\d+$/.test(matchv1)) return 1;
else if (matchv2 && /^\d+$/.test(matchv2)) return -1;
else if (matchv1) return -1;
else return 1;
}
}
return 0;
}
isValid(version) {
if (!version) return false;
const cleanVersion = version.replace(/^[=><~][=]?/, "");
return this._parse(cleanVersion) !== null;
}
isSingleVersion(version) {
if (!version) return false;
if (/^[><~]/.test(version)) return false;
return this.isValid(version);
}
isStable(version) {
if (!version) return false;
const cleanVersion = version.replace(/^[=><~][=]?/, "");
const parsed = this._parse(cleanVersion);
if (!parsed) return false;
return !parsed.prerelease;
}
getSatisfyingVersion(versions, range) {
const rangeMatch = /^([><=~]+)(.+)$/.exec(range);
if (!rangeMatch) return versions.find((v) => this.equals(v, range)) ?? null;
const [, operator, targetVersion] = rangeMatch;
const satisfyingVersions = versions.filter((version) => {
if (!this.isValid(version) || !this.isValid(targetVersion)) return false;
switch (operator) {
case ">": return this.isGreaterThan(version, targetVersion);
case ">=": return this.isGreaterThan(version, targetVersion) || this.equals(version, targetVersion);
case "<": return this.isLessThanRange(version, targetVersion);
case "<=": return this.isLessThanRange(version, targetVersion) || this.equals(version, targetVersion);
case "=":
case "==": return this.equals(version, targetVersion);
case "~": {
const targetParsed = this._parse(targetVersion);
const versionParsed = this._parse(version);
if (targetParsed.release[0] !== versionParsed.release[0] || targetParsed.release[1] !== versionParsed.release[1]) return false;
return this.isGreaterThan(version, targetVersion) || this.equals(version, targetVersion);
}
}
return false;
});
if (satisfyingVersions.length === 0) return null;
return satisfyingVersions.sort((a, b) => this.sortVersions(b, a))[0];
}
getMajor(version) {
if (!version) return null;
const cleanVersion = version.replace(/^[=><~][=]?/, "");
return this._parse(cleanVersion)?.release[0] ?? null;
}
getMinor(version) {
if (!version) return null;
const cleanVersion = version.replace(/^[=><~][=]?/, "");
return this._parse(cleanVersion)?.release[1] ?? null;
}
getPatch(version) {
if (!version) return null;
const cleanVersion = version.replace(/^[=><~][=]?/, "");
const parsed = this._parse(cleanVersion);
if (!parsed) return null;
return parsed.release[2] ?? null;
}
getNewValue({ currentValue, newVersion }) {
if (!/-r\d+$/.test(currentValue)) return newVersion.replace(/-r\d+$/, "");
return newVersion;
}
sortVersions(a, b) {
const cleanA = a.replace(/^=/, "");
const cleanB = b.replace(/^=/, "");
return super.sortVersions(cleanA, cleanB);
}
};
const api = new ApkVersioningApi();
//#endregion
export { api as default };
//# sourceMappingURL=index.js.map