From 6aa5c4238f589c79bb4e3a96fd01a52b6c8c4ea6 Mon Sep 17 00:00:00 2001
From: Gabriel-Ladzaretti
 <97394622+Gabriel-Ladzaretti@users.noreply.github.com>
Date: Tue, 31 Dec 2024 15:28:26 +0200
Subject: [PATCH] refactor(config): reusable env getConfig function (#33350)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
---
 lib/workers/global/config/parse/env.spec.ts |   6 +-
 lib/workers/global/config/parse/env.ts      | 175 +++++++++++---------
 2 files changed, 100 insertions(+), 81 deletions(-)

diff --git a/lib/workers/global/config/parse/env.spec.ts b/lib/workers/global/config/parse/env.spec.ts
index 3961e8d3dd..0dae85326e 100644
--- a/lib/workers/global/config/parse/env.spec.ts
+++ b/lib/workers/global/config/parse/env.spec.ts
@@ -306,7 +306,11 @@ describe('workers/global/config/parse/env', () => {
 
       it('crashes', async () => {
         const envParam: NodeJS.ProcessEnv = { RENOVATE_CONFIG: '!@#' };
-        await env.getConfig(envParam);
+        processExit.mockImplementationOnce(() => {
+          throw new Error('terminate function to simulate process.exit call');
+        });
+
+        await expect(env.getConfig(envParam)).toReject();
         expect(processExit).toHaveBeenCalledWith(1);
       });
 
diff --git a/lib/workers/global/config/parse/env.ts b/lib/workers/global/config/parse/env.ts
index 3467b1ec1a..a5543bb198 100644
--- a/lib/workers/global/config/parse/env.ts
+++ b/lib/workers/global/config/parse/env.ts
@@ -3,6 +3,7 @@ import JSON5 from 'json5';
 import { getOptions } from '../../../../config/options';
 import type { AllConfig } from '../../../../config/types';
 import { logger } from '../../../../logger';
+import { parseJson } from '../../../../util/common';
 import { coersions } from './coersions';
 import type { ParseConfigOptions } from './types';
 import { migrateAndValidateConfig } from './util';
@@ -118,9 +119,9 @@ function massageConvertedExperimentalVars(
 
 export async function getConfig(
   inputEnv: NodeJS.ProcessEnv,
+  configEnvKey = 'RENOVATE_CONFIG',
 ): Promise<AllConfig> {
-  let env = inputEnv;
-  env = normalizePrefixes(inputEnv, inputEnv.ENV_PREFIX);
+  let env = normalizePrefixes(inputEnv, inputEnv.ENV_PREFIX);
   env = massageConvertedExperimentalVars(env);
   env = renameEnvKeys(env);
   // massage the values of migrated configuration keys
@@ -128,92 +129,82 @@ export async function getConfig(
 
   const options = getOptions();
 
-  let config: AllConfig = {};
+  const config = await parseAndValidateOrExit(env, configEnvKey);
 
-  if (env.RENOVATE_CONFIG) {
-    try {
-      config = JSON5.parse(env.RENOVATE_CONFIG);
-      logger.debug({ config }, 'Detected config in env RENOVATE_CONFIG');
+  config.hostRules ??= [];
 
-      config = await migrateAndValidateConfig(config, 'RENOVATE_CONFIG');
-    } catch (err) {
-      logger.fatal({ err }, 'Could not parse RENOVATE_CONFIG');
-      process.exit(1);
+  for (const option of options) {
+    if (option.env === false) {
+      continue;
     }
-  }
 
-  config.hostRules ??= [];
+    const envName = getEnvName(option);
+    const envVal = env[envName];
+    if (!envVal) {
+      continue;
+    }
 
-  options.forEach((option) => {
-    if (option.env !== false) {
-      const envName = getEnvName(option);
-      const envVal = env[envName];
-      if (envVal) {
-        if (option.type === 'array' && option.subType === 'object') {
-          try {
-            const parsed = JSON5.parse(envVal);
-            if (is.array(parsed)) {
-              config[option.name] = parsed;
-            } else {
-              logger.debug(
-                { val: envVal, envName },
-                'Could not parse object array',
-              );
-            }
-          } catch {
-            logger.debug(
-              { val: envVal, envName },
-              'Could not parse environment variable',
-            );
-          }
+    if (option.type === 'array' && option.subType === 'object') {
+      try {
+        const parsed = JSON5.parse(envVal);
+        if (is.array(parsed)) {
+          config[option.name] = parsed;
         } else {
-          const coerce = coersions[option.type];
-          config[option.name] = coerce(envVal);
-          if (option.name === 'dryRun') {
-            if ((config[option.name] as string) === 'true') {
-              logger.warn(
-                'env config dryRun property has been changed to full',
-              );
-              config[option.name] = 'full';
-            } else if ((config[option.name] as string) === 'false') {
-              logger.warn(
-                'env config dryRun property has been changed to null',
-              );
-              delete config[option.name];
-            } else if ((config[option.name] as string) === 'null') {
-              delete config[option.name];
-            }
-          }
-          if (option.name === 'requireConfig') {
-            if ((config[option.name] as string) === 'true') {
-              logger.warn(
-                'env config requireConfig property has been changed to required',
-              );
-              config[option.name] = 'required';
-            } else if ((config[option.name] as string) === 'false') {
-              logger.warn(
-                'env config requireConfig property has been changed to optional',
-              );
-              config[option.name] = 'optional';
-            }
-          }
-          if (option.name === 'platformCommit') {
-            if ((config[option.name] as string) === 'true') {
-              logger.warn(
-                'env config platformCommit property has been changed to enabled',
-              );
-              config[option.name] = 'enabled';
-            } else if ((config[option.name] as string) === 'false') {
-              logger.warn(
-                'env config platformCommit property has been changed to disabled',
-              );
-              config[option.name] = 'disabled';
-            }
-          }
+          logger.debug(
+            { val: envVal, envName },
+            'Could not parse object array',
+          );
+        }
+      } catch {
+        logger.debug(
+          { val: envVal, envName },
+          'Could not parse environment variable',
+        );
+      }
+    } else {
+      const coerce = coersions[option.type];
+      config[option.name] = coerce(envVal);
+      if (option.name === 'dryRun') {
+        if ((config[option.name] as string) === 'true') {
+          logger.warn('env config dryRun property has been changed to full');
+          config[option.name] = 'full';
+        } else if ((config[option.name] as string) === 'false') {
+          logger.warn('env config dryRun property has been changed to null');
+          delete config[option.name];
+        } else if ((config[option.name] as string) === 'null') {
+          delete config[option.name];
+        }
+      }
+
+      if (option.name === 'requireConfig') {
+        if ((config[option.name] as string) === 'true') {
+          logger.warn(
+            'env config requireConfig property has been changed to required',
+          );
+          config[option.name] = 'required';
+        } else if ((config[option.name] as string) === 'false') {
+          logger.warn(
+            'env config requireConfig property has been changed to optional',
+          );
+          config[option.name] = 'optional';
+        }
+      }
+
+      if (option.name === 'platformCommit') {
+        if ((config[option.name] as string) === 'true') {
+          logger.warn(
+            'env config platformCommit property has been changed to enabled',
+          );
+          config[option.name] = 'enabled';
+        } else if ((config[option.name] as string) === 'false') {
+          logger.warn(
+            'env config platformCommit property has been changed to disabled',
+          );
+          config[option.name] = 'disabled';
         }
       }
     }
-  });
+  }
 
   if (env.GITHUB_COM_TOKEN) {
     logger.debug(`Converting GITHUB_COM_TOKEN into a global host rule`);
@@ -237,7 +228,31 @@ export async function getConfig(
     'VSTS_TOKEN',
   ];
 
-  unsupportedEnv.forEach((val) => delete env[val]);
+  for (const val of unsupportedEnv) {
+    delete env[val];
+  }
 
   return config;
 }
+
+async function parseAndValidateOrExit(
+  env: NodeJS.ProcessEnv,
+  configEnvKey: string,
+): Promise<AllConfig> {
+  if (!env[configEnvKey]) {
+    return {};
+  }
+
+  try {
+    const config = parseJson(
+      env[configEnvKey],
+      `${configEnvKey}.env.json5`,
+    ) as AllConfig;
+    logger.debug({ config }, `Detected config in env ${configEnvKey}`);
+
+    return await migrateAndValidateConfig(config, `${configEnvKey}`);
+  } catch (err) {
+    logger.fatal({ err }, `Could not parse ${configEnvKey}`);
+    process.exit(1);
+  }
+}
-- 
GitLab