Skip to content
Snippets Groups Projects
Unverified Commit 76cacd59 authored by Sergei Zharinov's avatar Sergei Zharinov Committed by GitHub
Browse files

refactor: Extract `ReleaseResult` filtering functions (#23253)

parent d5130ce6
No related branches found
No related tags found
No related merge requests found
......@@ -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,74 +343,101 @@ function getRawReleases(
return promisedRes;
}
export async function getPkgReleases(
config: GetPkgReleasesConfig
): Promise<ReleaseResult | null> {
if (!config.datasource) {
logger.warn('No datasource found');
return null;
function applyExtractVersion<
Config extends Pick<GetPkgReleasesConfig, 'extractVersion'>
>(config: Config, releaseResult: ReleaseResult): void {
if (!config.extractVersion) {
return;
}
const packageName = config.packageName;
if (!packageName) {
logger.error({ config }, 'Datasource getReleases without packageName');
const extractVersionRegEx = regEx(config.extractVersion);
releaseResult.releases = filterMap(releaseResult.releases, (release) => {
const version = extractVersionRegEx.exec(release.version)?.groups?.version;
if (!version) {
return null;
}
let res: ReleaseResult | null = null;
try {
res = clone(
await getRawReleases({
...config,
packageName,
})
release.version = version;
return release;
});
}
function filterValidVersions<
Config extends Pick<GetPkgReleasesConfig, 'versioning' | 'datasource'>
>(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
);
} catch (e) /* istanbul ignore next */ {
if (e instanceof ExternalHostError) {
e.hostType = config.datasource;
e.packageName = packageName;
}
throw e;
function sortAndRemoveDuplicates<
Config extends Pick<GetPkgReleasesConfig, 'versioning' | 'datasource'>
>(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;
}
if (!res) {
return res;
previousVersion = release.version;
return release;
});
}
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);
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;
}
// Use the datasource's default versioning if none is configured
const versioning =
return;
}
const versioningName =
config.versioning ?? getDefaultVersioning(config.datasource);
const version = allVersioning.get(versioning);
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;
// Filter and sort valid versions
res.releases = res.releases
.filter((release) => version.isVersion(release.version))
.sort((a, b) => version.sortVersions(a.version, b.version));
// istanbul ignore if
if (!configConstraints || !releaseConstraints) {
return release;
}
// Filter versions for uniqueness
res.releases = uniq(res.releases, (x, y) => x.version === y.version);
for (const [name, configConstraint] of Object.entries(configConstraints)) {
// istanbul ignore if
if (!versioning.isValid(configConstraint)) {
continue;
}
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];
const constraint = releaseConstraints[name];
if (!is.nonEmptyArray(constraint)) {
// A release with no constraints is OK
return true;
continue;
}
const satisfiesConstraints = constraint.some(
......@@ -418,30 +445,64 @@ export async function getPkgReleases(
// fallback to release's constraint match if subset is not supported by versioning
(releaseConstraint) =>
!releaseConstraint ||
(version.subset?.(constraintValue, releaseConstraint) ??
version.matches(constraintValue, releaseConstraint))
(versioning.subset?.(configConstraint, releaseConstraint) ??
versioning.matches(configConstraint, releaseConstraint))
);
if (!satisfiesConstraints) {
filteredReleases.push(release.version);
}
return satisfiesConstraints;
});
return null;
}
}
return release;
});
if (filteredReleases.length) {
const count = filteredReleases.length;
const packageName = config.packageName;
const releases = filteredReleases.join(', ');
logger.debug(
`Filtered ${
filteredReleases.length
} releases for ${packageName} due to constraintsFiltering=strict: ${filteredReleases.join(
', '
)}`
`Filtered ${count} releases for ${packageName} due to constraintsFiltering=strict: ${releases}`
);
}
}
// Strip constraints from releases result
res.releases.forEach((release) => {
delete release.constraints;
});
export async function getPkgReleases(
config: GetPkgReleasesConfig
): Promise<ReleaseResult | null> {
if (!config.datasource) {
logger.warn('No datasource found');
return null;
}
const packageName = config.packageName;
if (!packageName) {
logger.error({ config }, 'Datasource getReleases without packageName');
return null;
}
let res: ReleaseResult | null = null;
try {
res = clone(
await getRawReleases({
...config,
packageName,
})
);
} catch (e) /* istanbul ignore next */ {
if (e instanceof ExternalHostError) {
e.hostType = config.datasource;
e.packageName = packageName;
}
throw e;
}
if (!res) {
return res;
}
applyExtractVersion(config, res);
filterValidVersions(config, res);
sortAndRemoveDuplicates(config, res);
applyConstraintsFiltering(config, res);
return res;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment