renovate/lib/workers/repository/process/fetch.ts

204 lines
5.7 KiB
TypeScript

// TODO #22198
import is from '@sindresorhus/is';
import { getManagerConfig, mergeChildConfig } from '../../../config';
import type { RenovateConfig } from '../../../config/types';
import { logger } from '../../../logger';
import { getDefaultConfig } from '../../../modules/datasource';
import { getDefaultVersioning } from '../../../modules/datasource/common';
import type {
PackageDependency,
PackageFile,
} from '../../../modules/manager/types';
import { ExternalHostError } from '../../../types/errors/external-host-error';
import { clone } from '../../../util/clone';
import { applyPackageRules } from '../../../util/package-rules';
import * as p from '../../../util/promises';
import { Result } from '../../../util/result';
import { LookupStats } from '../../../util/stats';
import { PackageFiles } from '../package-files';
import { lookupUpdates } from './lookup';
import type { LookupUpdateConfig, UpdateResult } from './lookup/types';
type LookupResult = Result<PackageDependency, Error>;
interface LookupTaskResult {
packageFile: PackageFile;
result: LookupResult;
}
type LookupTask = Promise<LookupTaskResult>;
async function lookup(
packageFileConfig: RenovateConfig & PackageFile,
indep: PackageDependency,
): Promise<LookupResult> {
const dep = clone(indep);
dep.updates = [];
if (is.string(dep.depName)) {
dep.depName = dep.depName.trim();
}
dep.packageName ??= dep.depName;
if (dep.skipReason) {
return Result.ok(dep);
}
if (!is.nonEmptyString(dep.packageName)) {
dep.skipReason = 'invalid-name';
return Result.ok(dep);
}
if (dep.isInternal && !packageFileConfig.updateInternalDeps) {
dep.skipReason = 'internal-package';
return Result.ok(dep);
}
const { depName } = dep;
// TODO: fix types
let depConfig = mergeChildConfig(packageFileConfig, dep);
const datasourceDefaultConfig = await getDefaultConfig(depConfig.datasource!);
depConfig = mergeChildConfig(depConfig, datasourceDefaultConfig);
depConfig.versioning ??= getDefaultVersioning(depConfig.datasource);
depConfig = await applyPackageRules(depConfig, 'pre-lookup');
depConfig.packageName ??= depConfig.depName;
if (depConfig.ignoreDeps!.includes(depName!)) {
// TODO: fix types (#22198)
logger.debug(`Dependency: ${depName!}, is ignored`);
dep.skipReason = 'ignored';
return Result.ok(dep);
}
if (depConfig.enabled === false) {
logger.debug(`Dependency: ${depName!}, is disabled`);
dep.skipReason = 'disabled';
return Result.ok(dep);
}
if (!depConfig.datasource) {
return Result.ok(dep);
}
return LookupStats.wrap(depConfig.datasource, async () => {
return await Result.wrap(lookupUpdates(depConfig as LookupUpdateConfig))
.onValue((dep) => {
logger.trace({ dep }, 'Dependency lookup success');
})
.onError((err) => {
logger.trace({ err, depName }, 'Dependency lookup error');
})
.catch((err): Result<UpdateResult, Error> => {
if (
packageFileConfig.repoIsOnboarded === true ||
!(err instanceof ExternalHostError)
) {
return Result.err(err);
}
const cause = err.err;
return Result.ok({
updates: [],
warnings: [
{
topic: 'Lookup Error',
message: `${depName}: ${cause.message}`,
},
],
});
})
.transform((upd): PackageDependency => Object.assign(dep, upd));
});
}
function createLookupTasks(
config: RenovateConfig,
managerPackageFiles: Record<string, PackageFile[]>,
): LookupTask[] {
const lookupTasks: LookupTask[] = [];
for (const [manager, packageFiles] of Object.entries(managerPackageFiles)) {
const managerConfig = getManagerConfig(config, manager);
for (const packageFile of packageFiles) {
const packageFileConfig = mergeChildConfig(managerConfig, packageFile);
if (packageFile.extractedConstraints) {
packageFileConfig.constraints = {
...packageFile.extractedConstraints,
...config.constraints,
};
}
for (const dep of packageFile.deps) {
lookupTasks.push(
lookup(packageFileConfig, dep).then((result) => ({
packageFile,
result,
})),
);
}
}
}
return lookupTasks;
}
export async function fetchUpdates(
config: RenovateConfig,
managerPackageFiles: Record<string, PackageFile[]>,
): Promise<void> {
logger.debug(
{ baseBranch: config.baseBranch },
'Starting package releases lookups',
);
const allTasks = createLookupTasks(config, managerPackageFiles);
const fetchResults = await Promise.all(allTasks);
const errors: Error[] = [];
type PackageDeps = WeakMap<PackageFile, PackageDependency[]>;
const packageDeps: PackageDeps = new WeakMap();
// Separate good results from errors
for (const { packageFile, result } of fetchResults) {
const { val: dep, err } = result.unwrap();
if (dep) {
let deps = packageDeps.get(packageFile);
if (!deps) {
deps = [];
packageDeps.set(packageFile, deps);
}
if (dep.skipReason && !dep.skipStage) {
dep.skipStage = 'lookup';
}
deps.push(dep);
} else {
errors.push(err);
}
}
if (errors.length) {
p.handleMultipleErrors(errors);
}
// Assign fetched deps back to packageFiles
for (const packageFiles of Object.values(managerPackageFiles)) {
for (const packageFile of packageFiles) {
const packageFileDeps = packageDeps.get(packageFile);
if (packageFileDeps) {
packageFile.deps = packageFileDeps;
}
}
}
PackageFiles.add(config.baseBranch!, { ...managerPackageFiles });
logger.debug(
{ baseBranch: config.baseBranch },
'Package releases lookups complete',
);
}