From 302718f38e54c6c7590f488d749fb4da8362f36f Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Wed, 21 Aug 2024 13:36:42 +0200
Subject: [PATCH] fix: Revert "refactor: Flatten update lookup tasks" (#30933)

---
 lib/util/promises.ts                          |   2 +-
 .../process/__snapshots__/fetch.spec.ts.snap  |   1 +
 lib/workers/repository/process/fetch.spec.ts  |   5 +-
 lib/workers/repository/process/fetch.ts       | 205 +++++++-----------
 4 files changed, 81 insertions(+), 132 deletions(-)

diff --git a/lib/util/promises.ts b/lib/util/promises.ts
index 1b614aa036..736437bc1d 100644
--- a/lib/util/promises.ts
+++ b/lib/util/promises.ts
@@ -10,7 +10,7 @@ function isExternalHostError(err: any): err is ExternalHostError {
   return err instanceof ExternalHostError;
 }
 
-export function handleMultipleErrors(errors: Error[]): never {
+function handleMultipleErrors(errors: Error[]): never {
   const hostError = errors.find(isExternalHostError);
   if (hostError) {
     throw hostError;
diff --git a/lib/workers/repository/process/__snapshots__/fetch.spec.ts.snap b/lib/workers/repository/process/__snapshots__/fetch.spec.ts.snap
index 115dc22d89..8352aa00ba 100644
--- a/lib/workers/repository/process/__snapshots__/fetch.spec.ts.snap
+++ b/lib/workers/repository/process/__snapshots__/fetch.spec.ts.snap
@@ -44,6 +44,7 @@ exports[`workers/repository/process/fetch fetchUpdates() handles ignored, skippe
         },
         {
           "depName": "skipped",
+          "packageName": "skipped",
           "skipReason": "some-reason",
           "updates": [],
         },
diff --git a/lib/workers/repository/process/fetch.spec.ts b/lib/workers/repository/process/fetch.spec.ts
index c674ccd210..172a6cb456 100644
--- a/lib/workers/repository/process/fetch.spec.ts
+++ b/lib/workers/repository/process/fetch.spec.ts
@@ -4,7 +4,6 @@ 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';
 
@@ -157,7 +156,7 @@ describe('workers/repository/process/fetch', () => {
           },
         ],
       };
-      lookupUpdates.mockResolvedValueOnce(Result.err(new Error('some error')));
+      lookupUpdates.mockRejectedValueOnce(new Error('some error'));
 
       await expect(
         fetchUpdates({ ...config, repoIsOnboarded: true }, packageFiles),
@@ -174,7 +173,7 @@ describe('workers/repository/process/fetch', () => {
           },
         ],
       };
-      lookupUpdates.mockResolvedValueOnce(Result.err(new Error('some error')));
+      lookupUpdates.mockRejectedValueOnce(new Error('some error'));
 
       await expect(
         fetchUpdates({ ...config, repoIsOnboarded: true }, packageFiles),
diff --git a/lib/workers/repository/process/fetch.ts b/lib/workers/repository/process/fetch.ts
index 0d9094fcef..025aea391b 100644
--- a/lib/workers/repository/process/fetch.ts
+++ b/lib/workers/repository/process/fetch.ts
@@ -1,7 +1,7 @@
 // TODO #22198
 import is from '@sindresorhus/is';
 import { getManagerConfig, mergeChildConfig } from '../../../config';
-import type { ManagerConfig, RenovateConfig } from '../../../config/types';
+import type { RenovateConfig } from '../../../config/types';
 import { logger } from '../../../logger';
 import { getDefaultConfig } from '../../../modules/datasource';
 import { getDefaultVersioning } from '../../../modules/datasource/common';
@@ -17,44 +17,27 @@ 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';
+import type { LookupUpdateConfig } from './lookup/types';
 
-type LookupResult = Result<PackageDependency, Error>;
-
-interface LookupTaskResult {
-  packageFileName: string;
-  manager: string;
-  result: LookupResult;
-}
-
-type LookupTask = Promise<LookupTaskResult>;
-
-async function lookup(
-  packageFileConfig: ManagerConfig & PackageFile,
+async function fetchDepUpdates(
+  packageFileConfig: RenovateConfig & PackageFile,
   indep: PackageDependency,
-): Promise<LookupResult> {
+): Promise<Result<PackageDependency, Error>> {
   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);
@@ -63,33 +46,24 @@ async function lookup(
   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';
-    return Result.ok(dep);
-  }
-
-  if (depConfig.enabled === false) {
+  } else 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> => {
+  } 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 {
         if (
           packageFileConfig.repoIsOnboarded === true ||
           !(err instanceof ExternalHostError)
@@ -98,101 +72,76 @@ async function lookup(
         }
 
         const cause = err.err;
-        return Result.ok({
-          updates: [],
-          warnings: [
-            {
-              topic: 'Lookup Error',
-              message: `${depName}: ${cause.message}`,
-            },
-          ],
+        dep.warnings ??= [];
+        dep.warnings.push({
+          topic: 'Lookup Error',
+          // TODO: types (#22198)
+          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) => ({
-            packageFileName: packageFile.packageFile,
-            manager: managerConfig.manager,
-            result,
-          })),
-        );
       }
     }
+    dep.updates ??= [];
   }
-
-  return lookupTasks;
+  return Result.ok(dep);
 }
 
-export async function fetchUpdates(
+async function fetchManagerPackagerFileUpdates(
   config: RenovateConfig,
-  managerPackageFiles: Record<string, PackageFile[]>,
+  managerConfig: RenovateConfig,
+  pFile: 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 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);
-    }
+  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',
+  );
 
-  if (errors.length) {
-    p.handleMultipleErrors(errors);
-  }
+  pFile.deps = await p.all(queue);
+  logger.trace({ packageFile }, 'fetchManagerPackagerFileUpdates finished');
+}
 
-  // 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;
-      }
-    }
-  }
+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');
+}
 
-  PackageFiles.add(config.baseBranch!, { ...managerPackageFiles });
+export async function fetchUpdates(
+  config: RenovateConfig,
+  packageFiles: Record<string, PackageFile[]>,
+): Promise<void> {
+  const managers = Object.keys(packageFiles);
+  const allManagerJobs = managers.map((manager) =>
+    fetchManagerUpdates(config, packageFiles, manager),
+  );
+  await Promise.all(allManagerJobs);
+  PackageFiles.add(config.baseBranch!, { ...packageFiles });
   logger.debug(
     { baseBranch: config.baseBranch },
     'Package releases lookups complete',
-- 
GitLab