Files
Jamie Tanna 1886fd23ba chore(datasource): downgrade log level to TRACE (#43010)
This gets very noisy quickly, otherwise.

And add a `.once` so there's some level of insight.
2026-04-30 19:49:52 +00:00

431 lines
13 KiB
TypeScript

import {
isNonEmptyArray,
isNonEmptyStringAndNotWhitespace,
} from '@sindresorhus/is';
import { logger } from '../../logger/index.ts';
import {
type ConstraintName,
isAdditionalConstraintName,
} from '../../util/exec/types.ts';
import { filterMap } from '../../util/filter-map.ts';
import { regEx } from '../../util/regex.ts';
import * as allVersioning from '../versioning/index.ts';
import { defaultVersioning } from '../versioning/index.ts';
import datasources from './api.ts';
import { CustomDatasource } from './custom/index.ts';
import type {
DatasourceApi,
GetPkgReleasesConfig,
ReleaseResult,
} from './types.ts';
export function getDatasourceFor(datasource: string): DatasourceApi | null {
if (datasource?.startsWith('custom.')) {
return getDatasourceFor(CustomDatasource.id);
}
return datasources.get(datasource) ?? null;
}
export function getDefaultVersioning(
datasourceName: string | undefined,
): string {
if (!datasourceName) {
return defaultVersioning.id;
}
const datasource = getDatasourceFor(datasourceName);
if (!datasource) {
logger.warn({ datasourceName }, 'Missing datasource!');
return defaultVersioning.id;
}
if (!datasource.defaultVersioning) {
return defaultVersioning.id;
}
return datasource.defaultVersioning;
}
export function isGetPkgReleasesConfig(
input: unknown,
): input is GetPkgReleasesConfig {
return (
isNonEmptyStringAndNotWhitespace(
(input as GetPkgReleasesConfig).datasource,
) &&
isNonEmptyStringAndNotWhitespace(
(input as GetPkgReleasesConfig).packageName,
)
);
}
export function applyVersionCompatibility(
releaseResult: ReleaseResult,
versionCompatibility: string | undefined,
currentCompatibility: string | undefined,
): ReleaseResult {
if (!versionCompatibility) {
return releaseResult;
}
const versionCompatibilityRegEx = regEx(versionCompatibility);
releaseResult.releases = filterMap(releaseResult.releases, (release) => {
const regexResult = versionCompatibilityRegEx.exec(release.version);
if (!regexResult?.groups?.version) {
logger.trace(
{ releaseVersion: release.version, versionCompatibility },
'versionCompatibility: Does not match regex',
);
return null;
}
if (regexResult?.groups?.compatibility !== currentCompatibility) {
logger.trace(
{ releaseVersion: release.version, versionCompatibility },
'versionCompatibility: Does not match compatibility',
);
return null;
}
logger.trace(
{
releaseVersion: release.version,
versionCompatibility,
version: regexResult.groups.version,
compatibility: regexResult.groups.compatibility,
},
'versionCompatibility: matches',
);
release.versionOrig ??= release.version;
release.version = regexResult.groups.version;
return release;
});
return releaseResult;
}
export function applyExtractVersion(
releaseResult: ReleaseResult,
extractVersion: string | undefined,
): ReleaseResult {
if (!extractVersion) {
return releaseResult;
}
const extractVersionRegEx = regEx(extractVersion);
releaseResult.releases = filterMap(releaseResult.releases, (release) => {
const version = extractVersionRegEx.exec(release.version)?.groups?.version;
if (!version) {
return null;
}
release.versionOrig = release.version;
release.version = version;
return release;
});
return releaseResult;
}
export function filterValidVersions<
Config extends Pick<GetPkgReleasesConfig, 'versioning' | 'datasource'>,
>(releaseResult: ReleaseResult, config: Config): ReleaseResult {
const versioningName =
config.versioning ?? getDefaultVersioning(config.datasource);
const versioning = allVersioning.get(versioningName);
releaseResult.releases = filterMap(releaseResult.releases, (release) =>
versioning.isVersion(release.version) ? release : null,
);
return releaseResult;
}
export function sortAndRemoveDuplicates<
Config extends Pick<GetPkgReleasesConfig, 'versioning' | 'datasource'>,
>(releaseResult: ReleaseResult, config: Config): ReleaseResult {
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;
});
return releaseResult;
}
export function applyConstraintsFiltering<
Config extends Pick<
GetPkgReleasesConfig,
| 'constraintsFiltering'
| 'constraintsVersioning'
| 'versioning'
| 'datasource'
| 'constraints'
| 'packageName'
>,
>(releaseResult: ReleaseResult, config: Config): ReleaseResult {
if (config?.constraintsFiltering !== 'strict') {
for (const release of releaseResult.releases) {
delete release.constraints;
}
return releaseResult;
}
const versioningName =
config.versioning ?? getDefaultVersioning(config.datasource);
const versioning = allVersioning.get(versioningName);
const configConstraints = config.constraints;
const filteredReleases: string[] = [];
const startingLength = releaseResult.releases.length;
releaseResult.releases = filterMap(releaseResult.releases, (release) => {
logger.trace(
{
release,
versioning: versioningName,
},
`applyConstraintsFiltering(${release.version})`,
);
const releaseConstraints = release.constraints;
delete release.constraints;
logger.trace(
{
release,
versioning: versioningName,
configConstraints: configConstraints ?? 'undefined',
releaseConstraints: releaseConstraints ?? 'undefined',
},
`applyConstraintsFiltering(${release.version}): checking constraints`,
);
if (!configConstraints || !releaseConstraints) {
return release;
}
for (const [name, configConstraint] of Object.entries(
configConstraints,
) as [ConstraintName, string][]) {
let constraintVersioningName = versioningName;
if (
isAdditionalConstraintName(name) &&
config.constraintsVersioning?.[name]
) {
const val = config.constraintsVersioning[name];
logger.once.debug(
{ packageName: config.packageName, release },
`applyConstraintsFiltering(${config.packageName}, ${name}): overriding constraintsVersioning from ${constraintVersioningName} to ${val}`,
);
logger.trace(
{ packageName: config.packageName, release },
`applyConstraintsFiltering(${release.version}): overriding constraintsVersioning from ${constraintVersioningName} to ${val}`,
);
constraintVersioningName = val;
}
const constraintVersioning = allVersioning.get(constraintVersioningName);
logger.trace(
{
release,
versioning: constraintVersioningName,
constraintName: name,
constraint: configConstraint,
},
`applyConstraintsFiltering(${release.version}) for constraint ${name}`,
);
const isValid = constraintVersioning.isValid(configConstraint);
logger.trace(
{
release,
versioning: constraintVersioningName,
},
`applyConstraintsFiltering(${release.version}): versioning.isValid(${configConstraint})=${isValid}`,
);
if (!isValid) {
logger.once.warn(
{
packageName: config.packageName,
constraint: configConstraint,
versioning: constraintVersioningName,
},
'Invalid constraint used with strict constraintsFiltering',
);
continue;
}
const constraint = releaseConstraints[name];
logger.trace(
{
release,
versioning: constraintVersioningName,
},
`applyConstraintsFiltering(${release.version}): releaseConstraints[${name}]=${JSON.stringify(constraint)}`,
);
if (!isNonEmptyArray(constraint)) {
// A release with no constraints is OK
continue;
}
let satisfiesConstraints = false;
for (const releaseConstraint of constraint) {
logger.trace(
{
release,
versioning: constraintVersioningName,
releaseConstraint,
},
`applyConstraintsFiltering(${release.version}): releaseConstraint=${releaseConstraint}`,
);
if (!releaseConstraint) {
satisfiesConstraints = true;
logger.once.debug(
{
packageName: config.packageName,
versioning: constraintVersioningName,
constraint: releaseConstraint,
},
'Undefined release constraint',
);
break;
}
const isValidConstraintVersion =
constraintVersioning.isValid(releaseConstraint);
logger.trace(
{
release,
versioning: constraintVersioningName,
releaseConstraint,
},
`applyConstraintsFiltering(${release.version}): constraintVersioning.isValid(${releaseConstraint})=${isValidConstraintVersion}`,
);
const isValidVersion = versioning.isValid(releaseConstraint);
logger.trace(
{
release,
versioning: constraintVersioningName,
releaseConstraint,
},
`applyConstraintsFiltering(${release.version}): versioning.isValid(${releaseConstraint})=${isValidVersion}`,
);
if (!isValidVersion || !isValidConstraintVersion) {
logger.once.debug(
{
packageName: config.packageName,
versioning: constraintVersioningName,
constraint: releaseConstraint,
},
'Invalid release constraint',
);
break;
}
const isEqual = configConstraint === releaseConstraint;
logger.trace(
{
release,
versioning: constraintVersioningName,
configConstraint,
releaseConstraint,
},
`applyConstraintsFiltering(${release.version}): ${configConstraint} === ${releaseConstraint}=${isEqual}`,
);
if (isEqual) {
satisfiesConstraints = true;
break;
}
const isSubset = constraintVersioning.subset?.(
configConstraint,
releaseConstraint,
);
logger.trace(
{
release,
versioning: constraintVersioningName,
configConstraint,
releaseConstraint,
},
`applyConstraintsFiltering(${release.version}): versioning.subset?.(${configConstraint}, ${releaseConstraint})=${isSubset}`,
);
if (isSubset) {
satisfiesConstraints = true;
break;
}
const doesMatchConfig = constraintVersioning.matches(
configConstraint,
releaseConstraint,
);
logger.trace(
{
release,
versioning: constraintVersioningName,
configConstraint,
releaseConstraint,
},
`applyConstraintsFiltering(${release.version}): versioning.matches(${configConstraint}, ${releaseConstraint})=${doesMatchConfig}`,
);
const doesMatchRelease = constraintVersioning.matches(
releaseConstraint,
configConstraint,
);
logger.trace(
{
release,
versioning: constraintVersioningName,
configConstraint,
releaseConstraint,
},
`applyConstraintsFiltering(${release.version}): versioning.matches(${releaseConstraint}, ${configConstraint})=${doesMatchRelease}`,
);
if (doesMatchConfig || doesMatchRelease) {
satisfiesConstraints = true;
break;
}
}
logger.trace(
{
release,
versioning: constraintVersioningName,
},
`applyConstraintsFiltering(${release.version}): satisfiesConstraints=${satisfiesConstraints}`,
);
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(
{
datasource: config.datasource,
packageName: config.packageName,
},
`Filtered out ${count} non-matching releases out of ${startingLength} total for ${packageName} due to constraintsFiltering=strict: ${releases}`,
);
}
return releaseResult;
}