From 76cacd5953511b9fd77172b743461ee9df349b0b Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sun, 9 Jul 2023 08:41:16 +0300 Subject: [PATCH] refactor: Extract `ReleaseResult` filtering functions (#23253) --- lib/modules/datasource/index.ts | 201 +++++++++++++++++++++----------- 1 file changed, 131 insertions(+), 70 deletions(-) diff --git a/lib/modules/datasource/index.ts b/lib/modules/datasource/index.ts index e9f482ffef350f..b0a5b91c573572 100644 --- a/lib/modules/datasource/index.ts +++ b/lib/modules/datasource/index.ts @@ -6,9 +6,9 @@ import { ExternalHostError } from '../../types/errors/external-host-error'; import * as memCache from '../../util/cache/memory'; import * as packageCache from '../../util/cache/package'; import { clone } from '../../util/clone'; +import { filterMap } from '../../util/filter-map'; import { regEx } from '../../util/regex'; import { Result } from '../../util/result'; -import { uniq } from '../../util/uniq'; import { trimTrailingSlash } from '../../util/url'; import { defaultVersioning } from '../versioning'; import * as allVersioning from '../versioning'; @@ -343,6 +343,131 @@ function getRawReleases( return promisedRes; } +function applyExtractVersion< + Config extends Pick +>(config: Config, releaseResult: ReleaseResult): void { + if (!config.extractVersion) { + return; + } + + const extractVersionRegEx = regEx(config.extractVersion); + releaseResult.releases = filterMap(releaseResult.releases, (release) => { + const version = extractVersionRegEx.exec(release.version)?.groups?.version; + if (!version) { + return null; + } + + release.version = version; + return release; + }); +} + +function filterValidVersions< + Config extends Pick +>(config: Config, releaseResult: ReleaseResult): void { + const versioningName = + config.versioning ?? getDefaultVersioning(config.datasource); + const versioning = allVersioning.get(versioningName); + + releaseResult.releases = filterMap(releaseResult.releases, (release) => + versioning.isVersion(release.version) ? release : null + ); +} + +function sortAndRemoveDuplicates< + Config extends Pick +>(config: Config, releaseResult: ReleaseResult): void { + const versioningName = + config.versioning ?? getDefaultVersioning(config.datasource); + const versioning = allVersioning.get(versioningName); + + releaseResult.releases = releaseResult.releases.sort((a, b) => + versioning.sortVersions(a.version, b.version) + ); + + // Once releases are sorted, deduplication is straightforward and efficient + let previousVersion: string | null = null; + releaseResult.releases = filterMap(releaseResult.releases, (release) => { + if (previousVersion === release.version) { + return null; + } + previousVersion = release.version; + return release; + }); +} + +function applyConstraintsFiltering< + Config extends Pick< + GetPkgReleasesConfig, + | 'constraintsFiltering' + | 'versioning' + | 'datasource' + | 'constraints' + | 'packageName' + > +>(config: Config, releaseResult: ReleaseResult): void { + if (config?.constraintsFiltering !== 'strict') { + for (const release of releaseResult.releases) { + delete release.constraints; + } + return; + } + + const versioningName = + config.versioning ?? getDefaultVersioning(config.datasource); + const versioning = allVersioning.get(versioningName); + + const configConstraints = config.constraints; + const filteredReleases: string[] = []; + releaseResult.releases = filterMap(releaseResult.releases, (release) => { + const releaseConstraints = release.constraints; + delete release.constraints; + + // istanbul ignore if + if (!configConstraints || !releaseConstraints) { + return release; + } + + for (const [name, configConstraint] of Object.entries(configConstraints)) { + // istanbul ignore if + if (!versioning.isValid(configConstraint)) { + continue; + } + + const constraint = releaseConstraints[name]; + if (!is.nonEmptyArray(constraint)) { + // A release with no constraints is OK + continue; + } + + const satisfiesConstraints = constraint.some( + // If the constraint value is a subset of any release's constraints, then it's OK + // fallback to release's constraint match if subset is not supported by versioning + (releaseConstraint) => + !releaseConstraint || + (versioning.subset?.(configConstraint, releaseConstraint) ?? + versioning.matches(configConstraint, releaseConstraint)) + ); + + if (!satisfiesConstraints) { + filteredReleases.push(release.version); + return null; + } + } + + return release; + }); + + if (filteredReleases.length) { + const count = filteredReleases.length; + const packageName = config.packageName; + const releases = filteredReleases.join(', '); + logger.debug( + `Filtered ${count} releases for ${packageName} due to constraintsFiltering=strict: ${releases}` + ); + } +} + export async function getPkgReleases( config: GetPkgReleasesConfig ): Promise { @@ -373,75 +498,11 @@ export async function getPkgReleases( if (!res) { return res; } - if (config.extractVersion) { - const extractVersionRegEx = regEx(config.extractVersion); - res.releases = res.releases - .map((release) => { - const version = extractVersionRegEx.exec(release.version)?.groups - ?.version; - if (version) { - return { ...release, version }; // overwrite version - } - return null; // filter out any we can't extract - }) - .filter(is.truthy); - } - // Use the datasource's default versioning if none is configured - const versioning = - config.versioning ?? getDefaultVersioning(config.datasource); - const version = allVersioning.get(versioning); - - // Filter and sort valid versions - res.releases = res.releases - .filter((release) => version.isVersion(release.version)) - .sort((a, b) => version.sortVersions(a.version, b.version)); - - // Filter versions for uniqueness - res.releases = uniq(res.releases, (x, y) => x.version === y.version); - - if (config?.constraintsFiltering === 'strict') { - const filteredReleases: string[] = []; - // Filter releases for compatibility - for (const [constraintName, constraintValue] of Object.entries( - config.constraints ?? {} - )) { - if (version.isValid(constraintValue)) { - res.releases = res.releases.filter((release) => { - const constraint = release.constraints?.[constraintName]; - if (!is.nonEmptyArray(constraint)) { - // A release with no constraints is OK - return true; - } - - const satisfiesConstraints = constraint.some( - // If the constraint value is a subset of any release's constraints, then it's OK - // fallback to release's constraint match if subset is not supported by versioning - (releaseConstraint) => - !releaseConstraint || - (version.subset?.(constraintValue, releaseConstraint) ?? - version.matches(constraintValue, releaseConstraint)) - ); - if (!satisfiesConstraints) { - filteredReleases.push(release.version); - } - return satisfiesConstraints; - }); - } - } - if (filteredReleases.length) { - logger.debug( - `Filtered ${ - filteredReleases.length - } releases for ${packageName} due to constraintsFiltering=strict: ${filteredReleases.join( - ', ' - )}` - ); - } - } - // Strip constraints from releases result - res.releases.forEach((release) => { - delete release.constraints; - }); + + applyExtractVersion(config, res); + filterValidVersions(config, res); + sortAndRemoveDuplicates(config, res); + applyConstraintsFiltering(config, res); return res; }