Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Flatten update lookup tasks #27369

Merged
merged 40 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c6331e2
refactor: Flatten update lookup tasks
zharinov Feb 16, 2024
0568a30
Refactor more
zharinov Feb 18, 2024
6fbd131
Refactor more
zharinov Feb 18, 2024
59f3ed8
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 18, 2024
fdcc4ac
Refactor more
zharinov Feb 18, 2024
84001ce
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 19, 2024
e07e14c
Update import types in fetch.ts
zharinov Feb 19, 2024
db9ff3f
Refactor error handling in promises.ts and fetch.ts
zharinov Feb 19, 2024
4bee3bd
Refactor more
zharinov Feb 19, 2024
c0b233b
Refactor more
zharinov Feb 19, 2024
e5b2c49
Fix lint
zharinov Feb 19, 2024
c7806e4
Refactor createLookupTasks function to use for...of loops
zharinov Feb 19, 2024
5794b41
Tidy up and add comments
zharinov Feb 19, 2024
4858a04
Use early returns in "lookup" function
zharinov Feb 19, 2024
a487a2c
Add log messages
zharinov Feb 19, 2024
2f06032
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 19, 2024
fa55dd3
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 19, 2024
86ac068
More refactoring for `lookup` function
zharinov Feb 19, 2024
d62817f
Simplify error handling
zharinov Feb 19, 2024
faf5434
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 20, 2024
ba1790b
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 24, 2024
56e61f2
Refactor
zharinov Feb 24, 2024
3343cba
Remove redundant variable init
zharinov Feb 24, 2024
c20c0b4
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 24, 2024
a182495
Update snapshot
zharinov Feb 24, 2024
19f2e0b
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 26, 2024
91e3b02
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 27, 2024
bf868f1
Merge branch 'main' into refactor/flatten-lookups
rarkins Feb 28, 2024
f621be0
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 28, 2024
bd0a3d8
Merge branch 'main' into refactor/flatten-lookups
zharinov Feb 29, 2024
23e04b3
Update fetch concurrency to Infinity
zharinov Feb 29, 2024
a47c313
Merge branch 'main' into refactor/flatten-lookups
zharinov Mar 5, 2024
403c111
Merge branch 'main' into refactor/flatten-lookups
zharinov Mar 6, 2024
ceb6d95
Use "Promise.all"
zharinov Mar 6, 2024
958e409
Merge branch 'main' into refactor/flatten-lookups
zharinov Jul 16, 2024
269f65e
Merge branch 'main' into refactor/flatten-lookups
zharinov Jul 17, 2024
e45fcd1
Merge branch 'main' into refactor/flatten-lookups
zharinov Jul 25, 2024
716bd0f
Merge branch 'main' into refactor/flatten-lookups
zharinov Aug 4, 2024
dad056c
Merge branch 'main' into refactor/flatten-lookups
zharinov Aug 11, 2024
e67490f
Merge branch 'main' into refactor/flatten-lookups
rarkins Aug 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/util/promises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function isExternalHostError(err: any): err is ExternalHostError {
return err instanceof ExternalHostError;
}

function handleMultipleErrors(errors: Error[]): never {
export function handleMultipleErrors(errors: Error[]): never {
const hostError = errors.find(isExternalHostError);
if (hostError) {
throw hostError;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ exports[`workers/repository/process/fetch fetchUpdates() handles ignored, skippe
},
{
"depName": "skipped",
"packageName": "skipped",
"skipReason": "some-reason",
"updates": [],
},
Expand Down
5 changes: 3 additions & 2 deletions lib/workers/repository/process/fetch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getConfig } from '../../../config/defaults';
import { MavenDatasource } from '../../../modules/datasource/maven';
import type { PackageFile } from '../../../modules/manager/types';
import { ExternalHostError } from '../../../types/errors/external-host-error';
import { Result } from '../../../util/result';
import { fetchUpdates } from './fetch';
import * as lookup from './lookup';

Expand Down Expand Up @@ -156,7 +157,7 @@ describe('workers/repository/process/fetch', () => {
},
],
};
lookupUpdates.mockRejectedValueOnce(new Error('some error'));
lookupUpdates.mockResolvedValueOnce(Result.err(new Error('some error')));

await expect(
fetchUpdates({ ...config, repoIsOnboarded: true }, packageFiles),
Expand All @@ -173,7 +174,7 @@ describe('workers/repository/process/fetch', () => {
},
],
};
lookupUpdates.mockRejectedValueOnce(new Error('some error'));
lookupUpdates.mockResolvedValueOnce(Result.err(new Error('some error')));

await expect(
fetchUpdates({ ...config, repoIsOnboarded: true }, packageFiles),
Expand Down
205 changes: 128 additions & 77 deletions lib/workers/repository/process/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// TODO #22198
import is from '@sindresorhus/is';
import { getManagerConfig, mergeChildConfig } from '../../../config';
import type { RenovateConfig } from '../../../config/types';
import type { ManagerConfig, RenovateConfig } from '../../../config/types';
import { logger } from '../../../logger';
import { getDefaultConfig } from '../../../modules/datasource';
import { getDefaultVersioning } from '../../../modules/datasource/common';
Expand All @@ -17,27 +17,44 @@ import { Result } from '../../../util/result';
import { LookupStats } from '../../../util/stats';
import { PackageFiles } from '../package-files';
import { lookupUpdates } from './lookup';
import type { LookupUpdateConfig } from './lookup/types';
import type { LookupUpdateConfig, UpdateResult } from './lookup/types';

async function fetchDepUpdates(
packageFileConfig: RenovateConfig & PackageFile,
type LookupResult = Result<PackageDependency, Error>;

interface LookupTaskResult {
packageFileName: string;
manager: string;
result: LookupResult;
}

type LookupTask = Promise<LookupTaskResult>;

async function lookup(
packageFileConfig: ManagerConfig & PackageFile,
indep: PackageDependency,
): Promise<Result<PackageDependency, Error>> {
): Promise<LookupResult> {
const dep = clone(indep);
dep.updates = [];

if (dep.skipReason) {
return Result.ok(dep);
}

if (is.string(dep.depName)) {
dep.depName = dep.depName.trim();
}

dep.packageName ??= dep.depName;
if (!is.nonEmptyString(dep.packageName)) {
dep.skipReason = 'invalid-name';
return Result.ok(dep);
}

if (dep.isInternal && !packageFileConfig.updateInternalDeps) {
dep.skipReason = 'internal-package';
}
if (dep.skipReason) {
return Result.ok(dep);
}

const { depName } = dep;
// TODO: fix types
let depConfig = mergeChildConfig(packageFileConfig, dep);
Expand All @@ -46,24 +63,33 @@ async function fetchDepUpdates(
depConfig.versioning ??= getDefaultVersioning(depConfig.datasource);
depConfig = 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';
} else if (depConfig.enabled === false) {
return Result.ok(dep);
}

if (depConfig.enabled === false) {
logger.debug(`Dependency: ${depName!}, is disabled`);
dep.skipReason = 'disabled';
} else {
if (depConfig.datasource) {
const { val: updateResult, err } = await LookupStats.wrap(
depConfig.datasource,
() =>
Result.wrap(lookupUpdates(depConfig as LookupUpdateConfig)).unwrap(),
);

if (updateResult) {
Object.assign(dep, updateResult);
} else {
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)
Expand All @@ -72,76 +98,101 @@ async function fetchDepUpdates(
}

const cause = err.err;
dep.warnings ??= [];
dep.warnings.push({
topic: 'Lookup Error',
// TODO: types (#22198)
message: `${depName!}: ${cause.message}`,
return Result.ok({
updates: [],
warnings: [
{
topic: 'Lookup Error',
message: `${depName}: ${cause.message}`,
},
],
});
}
}
dep.updates ??= [];
}
return Result.ok(dep);
})
.transform((upd): PackageDependency => Object.assign(dep, upd));
});
}

async function fetchManagerPackagerFileUpdates(
function createLookupTasks(
config: RenovateConfig,
managerConfig: RenovateConfig,
pFile: PackageFile,
): Promise<void> {
const { packageFile } = pFile;
const packageFileConfig = mergeChildConfig(managerConfig, pFile);
if (pFile.extractedConstraints) {
packageFileConfig.constraints = {
...pFile.extractedConstraints,
...config.constraints,
};
}
const { manager } = packageFileConfig;
const queue = pFile.deps.map(
(dep) => async (): Promise<PackageDependency> => {
const updates = await fetchDepUpdates(packageFileConfig, dep);
return updates.unwrapOrThrow();
},
);
logger.trace(
{ manager, packageFile, queueLength: queue.length },
'fetchManagerPackagerFileUpdates starting with concurrency',
);
managerPackageFiles: Record<string, PackageFile[]>,
): LookupTask[] {
const lookupTasks: LookupTask[] = [];

pFile.deps = await p.all(queue);
logger.trace({ packageFile }, 'fetchManagerPackagerFileUpdates finished');
}
for (const [manager, packageFiles] of Object.entries(managerPackageFiles)) {
const managerConfig = getManagerConfig(config, manager);

async function fetchManagerUpdates(
config: RenovateConfig,
packageFiles: Record<string, PackageFile[]>,
manager: string,
): Promise<void> {
const managerConfig = getManagerConfig(config, manager);
const queue = packageFiles[manager].map(
(pFile) => (): Promise<void> =>
fetchManagerPackagerFileUpdates(config, managerConfig, pFile),
);
logger.trace(
{ manager, queueLength: queue.length },
'fetchManagerUpdates starting',
);
await p.all(queue);
logger.trace({ manager }, 'fetchManagerUpdates finished');
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) => ({
packageFileName: packageFile.packageFile,
manager: managerConfig.manager,
result,
})),
);
}
}
}

return lookupTasks;
}

export async function fetchUpdates(
config: RenovateConfig,
packageFiles: Record<string, PackageFile[]>,
managerPackageFiles: Record<string, PackageFile[]>,
): Promise<void> {
const managers = Object.keys(packageFiles);
const allManagerJobs = managers.map((manager) =>
fetchManagerUpdates(config, packageFiles, manager),
logger.debug(
{ baseBranch: config.baseBranch },
'Starting package releases lookups',
);
await Promise.all(allManagerJobs);
PackageFiles.add(config.baseBranch!, { ...packageFiles });

const allTasks = createLookupTasks(config, managerPackageFiles);

const fetchResults = await Promise.all(allTasks);
viceice marked this conversation as resolved.
Show resolved Hide resolved

const errors: Error[] = [];

type Manager = string;
type PackageFileName = string;
type PackageFileDeps = Record<PackageFileName, PackageDependency[]>;
type ManagerPackageFileDeps = Record<Manager, PackageFileDeps>;
const deps: ManagerPackageFileDeps = {};

// Separate good results from errors
for (const { packageFileName, manager, result } of fetchResults) {
const { val: dep, err } = result.unwrap();
if (dep) {
deps[manager] ??= {};
deps[manager][packageFileName] ??= [];
deps[manager][packageFileName].push(dep);
} else {
errors.push(err);
}
}

if (errors.length) {
p.handleMultipleErrors(errors);
}

// Assign fetched deps back to packageFiles
for (const [manager, packageFiles] of Object.entries(managerPackageFiles)) {
for (const packageFile of packageFiles) {
const packageFileDeps = deps[manager]?.[packageFile.packageFile];
if (packageFileDeps) {
packageFile.deps = packageFileDeps;
}
}
}

PackageFiles.add(config.baseBranch!, { ...managerPackageFiles });
logger.debug(
{ baseBranch: config.baseBranch },
'Package releases lookups complete',
Expand Down