2020-04-07 13:41:39 +00:00
|
|
|
import is from '@sindresorhus/is';
|
2021-03-10 22:04:12 +00:00
|
|
|
import { dequal } from 'dequal';
|
2022-03-03 09:35:26 +00:00
|
|
|
import { HOST_DISABLED } from '../../constants/error-messages';
|
|
|
|
import { logger } from '../../logger';
|
|
|
|
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 { regEx } from '../../util/regex';
|
|
|
|
import { trimTrailingSlash } from '../../util/url';
|
2020-02-18 07:34:10 +00:00
|
|
|
import * as allVersioning from '../versioning';
|
2021-03-02 15:57:02 +00:00
|
|
|
import datasources from './api';
|
2021-03-02 20:44:55 +00:00
|
|
|
import { addMetaData } from './metadata';
|
2022-03-14 08:13:21 +00:00
|
|
|
import { setNpmrc } from './npm';
|
|
|
|
import { resolveRegistryUrl } from './npm/npmrc';
|
2021-03-02 20:44:55 +00:00
|
|
|
import type {
|
|
|
|
DatasourceApi,
|
2019-08-15 04:30:16 +00:00
|
|
|
DigestConfig,
|
2022-01-28 11:50:03 +00:00
|
|
|
GetDigestInputConfig,
|
2020-04-04 06:53:52 +00:00
|
|
|
GetPkgReleasesConfig,
|
2020-05-01 16:03:48 +00:00
|
|
|
GetReleasesConfig,
|
|
|
|
ReleaseResult,
|
2021-03-02 20:44:55 +00:00
|
|
|
} from './types';
|
2018-12-25 05:57:11 +00:00
|
|
|
|
2021-03-02 20:44:55 +00:00
|
|
|
export * from './types';
|
|
|
|
export { isGetPkgReleasesConfig } from './common';
|
2019-08-15 04:30:16 +00:00
|
|
|
|
2021-03-02 20:44:55 +00:00
|
|
|
export const getDatasources = (): Map<string, DatasourceApi> => datasources;
|
2020-03-24 06:17:59 +00:00
|
|
|
export const getDatasourceList = (): string[] => Array.from(datasources.keys());
|
2018-06-08 08:49:08 +00:00
|
|
|
|
2018-12-25 11:31:51 +00:00
|
|
|
const cacheNamespace = 'datasource-releases';
|
|
|
|
|
2022-04-16 11:57:12 +00:00
|
|
|
function getDatasourceFor(datasource: string): DatasourceApi | null {
|
|
|
|
return datasources.get(datasource) ?? null;
|
2020-03-24 06:17:59 +00:00
|
|
|
}
|
|
|
|
|
2020-04-04 06:53:52 +00:00
|
|
|
type GetReleasesInternalConfig = GetReleasesConfig & GetPkgReleasesConfig;
|
|
|
|
|
2021-06-18 17:21:19 +00:00
|
|
|
// TODO: fix error Type
|
2022-03-03 15:08:43 +00:00
|
|
|
function logError(datasource: string, packageName: string, err: any): void {
|
2020-06-25 12:07:03 +00:00
|
|
|
const { statusCode, code: errCode, url } = err;
|
2020-06-25 07:34:41 +00:00
|
|
|
if (statusCode === 404) {
|
2022-03-03 15:08:43 +00:00
|
|
|
logger.debug({ datasource, packageName, url }, 'Datasource 404');
|
2020-06-25 07:34:41 +00:00
|
|
|
} else if (statusCode === 401 || statusCode === 403) {
|
2022-03-03 15:08:43 +00:00
|
|
|
logger.debug({ datasource, packageName, url }, 'Datasource unauthorized');
|
2020-06-25 12:07:03 +00:00
|
|
|
} else if (errCode) {
|
|
|
|
logger.debug(
|
2022-03-03 15:08:43 +00:00
|
|
|
{ datasource, packageName, url, errCode },
|
2020-06-25 12:07:03 +00:00
|
|
|
'Datasource connection error'
|
|
|
|
);
|
2020-06-25 07:34:41 +00:00
|
|
|
} else {
|
2022-03-03 15:08:43 +00:00
|
|
|
logger.debug({ datasource, packageName, err }, 'Datasource unknown error');
|
2020-06-25 07:34:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-23 10:19:25 +00:00
|
|
|
async function getRegistryReleases(
|
2021-03-02 20:44:55 +00:00
|
|
|
datasource: DatasourceApi,
|
2020-06-23 10:19:25 +00:00
|
|
|
config: GetReleasesConfig,
|
|
|
|
registryUrl: string
|
2022-04-16 11:57:12 +00:00
|
|
|
): Promise<ReleaseResult | null> {
|
2022-03-03 15:08:43 +00:00
|
|
|
const cacheKey = `${datasource.id} ${registryUrl} ${config.packageName}`;
|
2021-01-31 09:46:02 +00:00
|
|
|
if (datasource.caching) {
|
|
|
|
const cachedResult = await packageCache.get<ReleaseResult>(
|
|
|
|
cacheNamespace,
|
|
|
|
cacheKey
|
|
|
|
);
|
|
|
|
// istanbul ignore if
|
|
|
|
if (cachedResult) {
|
2021-11-19 13:22:35 +00:00
|
|
|
logger.trace({ cacheKey }, 'Returning cached datasource response');
|
2021-01-31 09:46:02 +00:00
|
|
|
return cachedResult;
|
|
|
|
}
|
|
|
|
}
|
2020-06-23 10:19:25 +00:00
|
|
|
const res = await datasource.getReleases({ ...config, registryUrl });
|
2021-03-17 12:34:47 +00:00
|
|
|
if (res?.releases.length) {
|
|
|
|
res.registryUrl ??= registryUrl;
|
|
|
|
}
|
2021-01-31 09:46:02 +00:00
|
|
|
// cache non-null responses unless marked as private
|
|
|
|
if (datasource.caching && res && !res.isPrivate) {
|
|
|
|
logger.trace({ cacheKey }, 'Caching datasource response');
|
|
|
|
const cacheMinutes = 15;
|
|
|
|
await packageCache.set(cacheNamespace, cacheKey, res, cacheMinutes);
|
|
|
|
}
|
2020-06-23 10:19:25 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-07-22 05:45:57 +00:00
|
|
|
function firstRegistry(
|
2020-06-19 19:29:34 +00:00
|
|
|
config: GetReleasesInternalConfig,
|
2021-03-02 20:44:55 +00:00
|
|
|
datasource: DatasourceApi,
|
2020-06-19 19:29:34 +00:00
|
|
|
registryUrls: string[]
|
2022-04-16 11:57:12 +00:00
|
|
|
): Promise<ReleaseResult | null> {
|
2020-06-19 19:29:34 +00:00
|
|
|
if (registryUrls.length > 1) {
|
|
|
|
logger.warn(
|
|
|
|
{ datasource: datasource.id, depName: config.depName, registryUrls },
|
|
|
|
'Excess registryUrls found for datasource lookup - using first configured only'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const registryUrl = registryUrls[0];
|
2020-06-25 08:29:45 +00:00
|
|
|
return getRegistryReleases(datasource, config, registryUrl);
|
2020-06-19 19:29:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function huntRegistries(
|
|
|
|
config: GetReleasesInternalConfig,
|
2021-03-02 20:44:55 +00:00
|
|
|
datasource: DatasourceApi,
|
2020-06-19 19:29:34 +00:00
|
|
|
registryUrls: string[]
|
2022-04-16 11:57:12 +00:00
|
|
|
): Promise<ReleaseResult | null> {
|
|
|
|
let res: ReleaseResult | null = null;
|
|
|
|
let caughtError: Error | undefined;
|
2020-06-19 19:29:34 +00:00
|
|
|
for (const registryUrl of registryUrls) {
|
|
|
|
try {
|
2020-06-23 10:19:25 +00:00
|
|
|
res = await getRegistryReleases(datasource, config, registryUrl);
|
2020-06-19 19:29:34 +00:00
|
|
|
if (res) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2020-06-22 19:28:02 +00:00
|
|
|
if (err instanceof ExternalHostError) {
|
2020-06-19 19:29:34 +00:00
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
// We'll always save the last-thrown error
|
2020-06-25 08:29:45 +00:00
|
|
|
caughtError = err;
|
2020-06-19 19:29:34 +00:00
|
|
|
logger.trace({ err }, 'datasource hunt failure');
|
|
|
|
}
|
|
|
|
}
|
2020-06-25 07:34:41 +00:00
|
|
|
if (res) {
|
|
|
|
return res;
|
2020-06-19 19:29:34 +00:00
|
|
|
}
|
2020-06-25 08:29:45 +00:00
|
|
|
if (caughtError) {
|
|
|
|
throw caughtError;
|
2020-06-25 07:34:41 +00:00
|
|
|
}
|
|
|
|
return null;
|
2020-06-19 19:29:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function mergeRegistries(
|
|
|
|
config: GetReleasesInternalConfig,
|
2021-03-02 20:44:55 +00:00
|
|
|
datasource: DatasourceApi,
|
2020-06-19 19:29:34 +00:00
|
|
|
registryUrls: string[]
|
2022-04-16 11:57:12 +00:00
|
|
|
): Promise<ReleaseResult | null> {
|
|
|
|
let combinedRes: ReleaseResult | undefined;
|
|
|
|
let caughtError: Error | undefined;
|
2020-06-19 19:29:34 +00:00
|
|
|
for (const registryUrl of registryUrls) {
|
|
|
|
try {
|
2020-06-23 10:19:25 +00:00
|
|
|
const res = await getRegistryReleases(datasource, config, registryUrl);
|
2021-03-17 12:34:47 +00:00
|
|
|
if (res) {
|
|
|
|
if (combinedRes) {
|
|
|
|
for (const existingRelease of combinedRes.releases || []) {
|
|
|
|
existingRelease.registryUrl = combinedRes.registryUrl;
|
|
|
|
}
|
|
|
|
for (const additionalRelease of res.releases || []) {
|
|
|
|
additionalRelease.registryUrl = res.registryUrl;
|
|
|
|
}
|
|
|
|
combinedRes = { ...res, ...combinedRes };
|
|
|
|
delete combinedRes.registryUrl;
|
|
|
|
combinedRes.releases = [...combinedRes.releases, ...res.releases];
|
|
|
|
} else {
|
|
|
|
combinedRes = res;
|
|
|
|
}
|
2020-06-19 19:29:34 +00:00
|
|
|
}
|
|
|
|
} catch (err) {
|
2020-06-22 19:28:02 +00:00
|
|
|
if (err instanceof ExternalHostError) {
|
2020-06-19 19:29:34 +00:00
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
// We'll always save the last-thrown error
|
2020-06-25 08:29:45 +00:00
|
|
|
caughtError = err;
|
2020-06-19 19:29:34 +00:00
|
|
|
logger.trace({ err }, 'datasource merge failure');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// De-duplicate releases
|
|
|
|
if (combinedRes?.releases?.length) {
|
|
|
|
const seenVersions = new Set<string>();
|
|
|
|
combinedRes.releases = combinedRes.releases.filter((release) => {
|
|
|
|
if (seenVersions.has(release.version)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
seenVersions.add(release.version);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
2020-06-25 07:34:41 +00:00
|
|
|
if (combinedRes) {
|
|
|
|
return combinedRes;
|
|
|
|
}
|
2020-06-25 08:29:45 +00:00
|
|
|
if (caughtError) {
|
|
|
|
throw caughtError;
|
2020-06-25 07:34:41 +00:00
|
|
|
}
|
|
|
|
return null;
|
2020-06-19 19:29:34 +00:00
|
|
|
}
|
|
|
|
|
2022-01-29 13:36:08 +00:00
|
|
|
function massageRegistryUrls(registryUrls: string[]): string[] {
|
|
|
|
return registryUrls.filter(Boolean).map(trimTrailingSlash);
|
|
|
|
}
|
|
|
|
|
2020-04-07 13:41:39 +00:00
|
|
|
function resolveRegistryUrls(
|
2021-03-02 20:44:55 +00:00
|
|
|
datasource: DatasourceApi,
|
2022-04-16 11:57:12 +00:00
|
|
|
defaultRegistryUrls: string[] | undefined,
|
2022-06-03 06:46:45 +00:00
|
|
|
registryUrls: string[] | undefined,
|
|
|
|
additionalRegistryUrls: string[] | undefined
|
2020-04-07 13:41:39 +00:00
|
|
|
): string[] {
|
2021-03-15 12:56:23 +00:00
|
|
|
if (!datasource.customRegistrySupport) {
|
2022-01-29 13:36:08 +00:00
|
|
|
if (
|
|
|
|
is.nonEmptyArray(registryUrls) ||
|
2022-06-03 06:46:45 +00:00
|
|
|
is.nonEmptyArray(defaultRegistryUrls) ||
|
|
|
|
is.nonEmptyArray(additionalRegistryUrls)
|
2022-01-29 13:36:08 +00:00
|
|
|
) {
|
2021-03-14 19:58:47 +00:00
|
|
|
logger.warn(
|
2022-06-03 06:46:45 +00:00
|
|
|
{
|
|
|
|
datasource: datasource.id,
|
|
|
|
registryUrls,
|
|
|
|
defaultRegistryUrls,
|
|
|
|
additionalRegistryUrls,
|
|
|
|
},
|
2021-06-17 15:00:45 +00:00
|
|
|
'Custom registries are not allowed for this datasource and will be ignored'
|
2021-03-14 19:58:47 +00:00
|
|
|
);
|
|
|
|
}
|
2022-06-16 11:00:11 +00:00
|
|
|
return is.function_(datasource.defaultRegistryUrls)
|
|
|
|
? datasource.defaultRegistryUrls()
|
|
|
|
: datasource.defaultRegistryUrls ?? [];
|
2021-03-14 19:58:47 +00:00
|
|
|
}
|
2022-01-29 13:36:08 +00:00
|
|
|
const customUrls = registryUrls?.filter(Boolean);
|
|
|
|
let resolvedUrls: string[] = [];
|
2020-06-22 18:18:39 +00:00
|
|
|
if (is.nonEmptyArray(customUrls)) {
|
2022-01-29 13:36:08 +00:00
|
|
|
resolvedUrls = [...customUrls];
|
|
|
|
} else if (is.nonEmptyArray(defaultRegistryUrls)) {
|
|
|
|
resolvedUrls = [...defaultRegistryUrls];
|
2022-06-30 05:06:22 +00:00
|
|
|
resolvedUrls = resolvedUrls.concat(additionalRegistryUrls ?? []);
|
2022-06-16 11:00:11 +00:00
|
|
|
} else if (is.function_(datasource.defaultRegistryUrls)) {
|
|
|
|
resolvedUrls = [...datasource.defaultRegistryUrls()];
|
2022-06-30 05:06:22 +00:00
|
|
|
resolvedUrls = resolvedUrls.concat(additionalRegistryUrls ?? []);
|
2022-01-29 13:36:08 +00:00
|
|
|
} else if (is.nonEmptyArray(datasource.defaultRegistryUrls)) {
|
|
|
|
resolvedUrls = [...datasource.defaultRegistryUrls];
|
2022-06-30 05:06:22 +00:00
|
|
|
resolvedUrls = resolvedUrls.concat(additionalRegistryUrls ?? []);
|
2020-06-22 18:18:39 +00:00
|
|
|
}
|
2022-01-29 13:36:08 +00:00
|
|
|
return massageRegistryUrls(resolvedUrls);
|
2020-04-07 13:41:39 +00:00
|
|
|
}
|
|
|
|
|
2022-07-03 16:11:20 +00:00
|
|
|
export function getDefaultVersioning(
|
|
|
|
datasourceName: string | undefined
|
|
|
|
): string {
|
|
|
|
if (!datasourceName) {
|
|
|
|
return 'semver';
|
|
|
|
}
|
2021-05-21 05:40:09 +00:00
|
|
|
const datasource = getDatasourceFor(datasourceName);
|
2021-11-01 08:28:33 +00:00
|
|
|
// istanbul ignore if: wrong regex manager config?
|
|
|
|
if (!datasource) {
|
|
|
|
logger.warn({ datasourceName }, 'Missing datasource!');
|
|
|
|
}
|
2022-06-21 11:00:21 +00:00
|
|
|
return datasource?.defaultVersioning ?? 'semver';
|
2021-01-21 11:39:18 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 08:10:52 +00:00
|
|
|
function applyReplacements(
|
|
|
|
config: GetReleasesInternalConfig
|
|
|
|
): Pick<ReleaseResult, 'replacementName' | 'replacementVersion'> | undefined {
|
|
|
|
if (config.replacementName && config.replacementVersion) {
|
|
|
|
return {
|
|
|
|
replacementName: config.replacementName,
|
|
|
|
replacementVersion: config.replacementVersion,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2019-11-24 04:09:13 +00:00
|
|
|
async function fetchReleases(
|
2020-04-04 06:53:52 +00:00
|
|
|
config: GetReleasesInternalConfig
|
2019-11-24 04:09:13 +00:00
|
|
|
): Promise<ReleaseResult | null> {
|
2020-04-07 13:41:39 +00:00
|
|
|
const { datasource: datasourceName } = config;
|
2022-03-14 08:13:21 +00:00
|
|
|
let { registryUrls } = config;
|
2022-04-16 11:57:12 +00:00
|
|
|
// istanbul ignore if: need test
|
2021-05-21 05:40:09 +00:00
|
|
|
if (!datasourceName || getDatasourceFor(datasourceName) === undefined) {
|
2020-04-07 13:41:39 +00:00
|
|
|
logger.warn('Unknown datasource: ' + datasourceName);
|
2019-11-24 04:09:13 +00:00
|
|
|
return null;
|
|
|
|
}
|
2022-03-14 08:13:21 +00:00
|
|
|
if (datasourceName === 'npm') {
|
|
|
|
if (is.string(config.npmrc)) {
|
|
|
|
setNpmrc(config.npmrc);
|
|
|
|
}
|
|
|
|
if (!is.nonEmptyArray(registryUrls)) {
|
|
|
|
registryUrls = [resolveRegistryUrl(config.packageName)];
|
|
|
|
}
|
|
|
|
}
|
2021-05-21 05:40:09 +00:00
|
|
|
const datasource = getDatasourceFor(datasourceName);
|
2022-04-16 11:57:12 +00:00
|
|
|
// istanbul ignore if: needs test
|
|
|
|
if (!datasource) {
|
|
|
|
return null;
|
|
|
|
}
|
2022-03-14 08:13:21 +00:00
|
|
|
registryUrls = resolveRegistryUrls(
|
2022-01-29 13:36:08 +00:00
|
|
|
datasource,
|
|
|
|
config.defaultRegistryUrls,
|
2022-06-03 06:46:45 +00:00
|
|
|
registryUrls,
|
|
|
|
config.additionalRegistryUrls
|
2022-01-29 13:36:08 +00:00
|
|
|
);
|
2022-04-16 11:57:12 +00:00
|
|
|
let dep: ReleaseResult | null = null;
|
2022-06-21 11:00:21 +00:00
|
|
|
const registryStrategy = datasource.registryStrategy ?? 'hunt';
|
2020-06-25 08:29:45 +00:00
|
|
|
try {
|
2021-03-15 12:56:23 +00:00
|
|
|
if (is.nonEmptyArray(registryUrls)) {
|
|
|
|
if (registryStrategy === 'first') {
|
2020-06-25 08:29:45 +00:00
|
|
|
dep = await firstRegistry(config, datasource, registryUrls);
|
2021-03-15 12:56:23 +00:00
|
|
|
} else if (registryStrategy === 'hunt') {
|
2020-06-25 08:29:45 +00:00
|
|
|
dep = await huntRegistries(config, datasource, registryUrls);
|
2021-03-15 12:56:23 +00:00
|
|
|
} else if (registryStrategy === 'merge') {
|
2020-06-25 08:29:45 +00:00
|
|
|
dep = await mergeRegistries(config, datasource, registryUrls);
|
|
|
|
}
|
|
|
|
} else {
|
2021-03-15 12:56:23 +00:00
|
|
|
dep = await datasource.getReleases(config);
|
2020-06-19 19:29:34 +00:00
|
|
|
}
|
2020-06-25 08:29:45 +00:00
|
|
|
} catch (err) {
|
2020-07-09 15:34:26 +00:00
|
|
|
if (err.message === HOST_DISABLED || err.err?.message === HOST_DISABLED) {
|
|
|
|
return null;
|
|
|
|
}
|
2020-06-25 08:29:45 +00:00
|
|
|
if (err instanceof ExternalHostError) {
|
|
|
|
throw err;
|
2020-06-19 19:29:34 +00:00
|
|
|
}
|
2022-03-03 15:08:43 +00:00
|
|
|
logError(datasource.id, config.packageName, err);
|
2020-06-19 19:29:34 +00:00
|
|
|
}
|
2021-03-10 22:04:12 +00:00
|
|
|
if (!dep || dequal(dep, { releases: [] })) {
|
2020-06-19 19:29:34 +00:00
|
|
|
return null;
|
2020-06-19 08:21:44 +00:00
|
|
|
}
|
2022-03-03 15:08:43 +00:00
|
|
|
addMetaData(dep, datasourceName, config.packageName);
|
2021-11-12 08:10:52 +00:00
|
|
|
dep = { ...dep, ...applyReplacements(config) };
|
2019-11-24 04:09:13 +00:00
|
|
|
return dep;
|
|
|
|
}
|
|
|
|
|
2019-11-26 15:13:07 +00:00
|
|
|
function getRawReleases(
|
2020-04-04 06:53:52 +00:00
|
|
|
config: GetReleasesInternalConfig
|
2019-11-26 15:13:07 +00:00
|
|
|
): Promise<ReleaseResult | null> {
|
2022-03-03 15:08:43 +00:00
|
|
|
const { datasource, packageName, registryUrls } = config;
|
|
|
|
const cacheKey = `${cacheNamespace}${datasource}${packageName}${String(
|
2020-08-27 07:07:58 +00:00
|
|
|
registryUrls
|
|
|
|
)}`;
|
2019-11-24 04:09:13 +00:00
|
|
|
// By returning a Promise and reusing it, we should only fetch each package at most once
|
2020-10-05 16:12:01 +00:00
|
|
|
const cachedResult = memCache.get<Promise<ReleaseResult | null>>(cacheKey);
|
2020-05-24 05:13:55 +00:00
|
|
|
// istanbul ignore if
|
2020-10-05 16:12:01 +00:00
|
|
|
if (cachedResult !== undefined) {
|
|
|
|
return cachedResult;
|
2019-11-24 04:09:13 +00:00
|
|
|
}
|
2020-05-24 05:13:55 +00:00
|
|
|
const promisedRes = fetchReleases(config);
|
2020-06-25 07:23:06 +00:00
|
|
|
memCache.set(cacheKey, promisedRes);
|
2020-05-24 05:13:55 +00:00
|
|
|
return promisedRes;
|
2019-11-24 04:09:13 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 15:13:07 +00:00
|
|
|
export async function getPkgReleases(
|
2020-04-04 06:53:52 +00:00
|
|
|
config: GetPkgReleasesConfig
|
2019-11-26 15:13:07 +00:00
|
|
|
): Promise<ReleaseResult | null> {
|
2020-04-04 06:53:52 +00:00
|
|
|
if (!config.datasource) {
|
|
|
|
logger.warn('No datasource found');
|
|
|
|
return null;
|
|
|
|
}
|
2022-06-21 11:00:21 +00:00
|
|
|
const packageName = config.packageName ?? config.depName;
|
2022-03-03 15:08:43 +00:00
|
|
|
if (!packageName) {
|
|
|
|
logger.error({ config }, 'Datasource getReleases without packageName');
|
2020-03-07 09:19:47 +00:00
|
|
|
return null;
|
|
|
|
}
|
2020-03-24 06:17:59 +00:00
|
|
|
let res: ReleaseResult;
|
2020-02-13 12:29:55 +00:00
|
|
|
try {
|
2020-04-17 20:42:15 +00:00
|
|
|
res = clone(
|
|
|
|
await getRawReleases({
|
|
|
|
...config,
|
2022-03-03 15:08:43 +00:00
|
|
|
packageName,
|
2020-04-17 20:42:15 +00:00
|
|
|
})
|
|
|
|
);
|
2020-03-12 11:48:57 +00:00
|
|
|
} catch (e) /* istanbul ignore next */ {
|
2020-06-22 19:28:02 +00:00
|
|
|
if (e instanceof ExternalHostError) {
|
|
|
|
e.hostType = config.datasource;
|
2022-03-03 15:08:43 +00:00
|
|
|
e.packageName = packageName;
|
2020-03-07 11:13:31 +00:00
|
|
|
}
|
2020-03-12 11:48:57 +00:00
|
|
|
throw e;
|
2020-02-13 12:29:55 +00:00
|
|
|
}
|
2018-12-26 07:36:24 +00:00
|
|
|
if (!res) {
|
|
|
|
return res;
|
|
|
|
}
|
2020-09-17 08:06:06 +00:00
|
|
|
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
|
|
|
|
})
|
2022-04-16 11:57:12 +00:00
|
|
|
.filter(is.truthy);
|
2020-09-17 08:06:06 +00:00
|
|
|
}
|
2021-01-21 11:39:18 +00:00
|
|
|
// Use the datasource's default versioning if none is configured
|
|
|
|
const versioning =
|
2022-06-21 11:00:21 +00:00
|
|
|
config.versioning ?? getDefaultVersioning(config.datasource);
|
2021-01-21 11:39:18 +00:00
|
|
|
const version = allVersioning.get(versioning);
|
2021-06-18 17:21:19 +00:00
|
|
|
|
|
|
|
// Filter and sort valid versions
|
|
|
|
res.releases = res.releases
|
|
|
|
.filter((release) => version.isVersion(release.version))
|
|
|
|
.sort((a, b) => version.sortVersions(a.version, b.version));
|
|
|
|
|
2020-09-17 08:06:06 +00:00
|
|
|
// Filter versions for uniqueness
|
|
|
|
res.releases = res.releases.filter(
|
|
|
|
(filterRelease, filterIndex) =>
|
|
|
|
res.releases.findIndex(
|
|
|
|
(findRelease) => findRelease.version === filterRelease.version
|
|
|
|
) === filterIndex
|
|
|
|
);
|
2021-01-31 08:23:24 +00:00
|
|
|
// Filter releases for compatibility
|
|
|
|
for (const [constraintName, constraintValue] of Object.entries(
|
2022-06-21 11:00:21 +00:00
|
|
|
config.constraints ?? {}
|
2021-01-31 08:23:24 +00:00
|
|
|
)) {
|
|
|
|
// Currently we only support if the constraint is a plain version
|
|
|
|
// TODO: Support range/range compatibility filtering #8476
|
|
|
|
if (version.isVersion(constraintValue)) {
|
|
|
|
res.releases = res.releases.filter((release) => {
|
2022-04-16 11:57:12 +00:00
|
|
|
const constraint = release.constraints?.[constraintName];
|
|
|
|
if (!is.nonEmptyArray(constraint)) {
|
2021-01-31 08:23:24 +00:00
|
|
|
// A release with no constraints is OK
|
|
|
|
return true;
|
|
|
|
}
|
2022-04-16 11:57:12 +00:00
|
|
|
return constraint.some(
|
2021-01-31 08:23:24 +00:00
|
|
|
// If any of the release's constraints match, then it's OK
|
|
|
|
(releaseConstraint) =>
|
|
|
|
!releaseConstraint ||
|
|
|
|
version.matches(constraintValue, releaseConstraint)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Strip constraints from releases result
|
|
|
|
res.releases.forEach((release) => {
|
2021-11-08 19:20:03 +00:00
|
|
|
delete release.constraints;
|
2021-01-31 08:23:24 +00:00
|
|
|
});
|
2018-12-26 07:36:24 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-01-28 11:50:03 +00:00
|
|
|
export function supportsDigests(datasource: string | undefined): boolean {
|
2022-04-16 11:57:12 +00:00
|
|
|
const ds = !!datasource && getDatasourceFor(datasource);
|
|
|
|
return !!ds && 'getDigest' in ds;
|
2022-01-28 11:50:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function getDigestConfig(
|
|
|
|
datasource: DatasourceApi,
|
|
|
|
config: GetDigestInputConfig
|
|
|
|
): DigestConfig {
|
|
|
|
const { currentValue, currentDigest } = config;
|
2022-09-27 13:19:18 +00:00
|
|
|
const packageName =
|
|
|
|
config.replacementName ?? config.packageName ?? config.depName;
|
2022-01-29 13:36:08 +00:00
|
|
|
const [registryUrl] = resolveRegistryUrls(
|
|
|
|
datasource,
|
|
|
|
config.defaultRegistryUrls,
|
2022-06-03 06:46:45 +00:00
|
|
|
config.registryUrls,
|
|
|
|
config.additionalRegistryUrls
|
2022-01-29 13:36:08 +00:00
|
|
|
);
|
2022-03-03 15:08:43 +00:00
|
|
|
return { packageName, registryUrl, currentValue, currentDigest };
|
2018-07-20 07:09:01 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 05:45:57 +00:00
|
|
|
export function getDigest(
|
2022-01-28 11:50:03 +00:00
|
|
|
config: GetDigestInputConfig,
|
2019-08-19 14:44:12 +00:00
|
|
|
value?: string
|
|
|
|
): Promise<string | null> {
|
2021-05-21 05:40:09 +00:00
|
|
|
const datasource = getDatasourceFor(config.datasource);
|
2022-04-16 11:57:12 +00:00
|
|
|
// istanbul ignore if: need test
|
|
|
|
if (!datasource || !('getDigest' in datasource)) {
|
|
|
|
return Promise.resolve(null);
|
|
|
|
}
|
2022-01-28 11:50:03 +00:00
|
|
|
const digestConfig = getDigestConfig(datasource, config);
|
2022-04-16 11:57:12 +00:00
|
|
|
return datasource.getDigest!(digestConfig, value);
|
2018-07-20 07:09:01 +00:00
|
|
|
}
|
2020-04-09 13:44:23 +00:00
|
|
|
|
2020-08-27 07:07:58 +00:00
|
|
|
export function getDefaultConfig(
|
|
|
|
datasource: string
|
|
|
|
): Promise<Record<string, unknown>> {
|
2021-05-21 05:40:09 +00:00
|
|
|
const loadedDatasource = getDatasourceFor(datasource);
|
2020-09-25 06:59:05 +00:00
|
|
|
return Promise.resolve<Record<string, unknown>>(
|
2022-06-21 11:00:21 +00:00
|
|
|
loadedDatasource?.defaultConfig ?? Object.create({})
|
2020-09-25 06:59:05 +00:00
|
|
|
);
|
2020-04-09 13:44:23 +00:00
|
|
|
}
|